159 lines
4 KiB
TypeScript
159 lines
4 KiB
TypeScript
import { SERVER, SERVER_OPTIONS } from 'jsr:@andyburke/serverus/server';
|
|
import { convert_to_words } from 'jsr:@andyburke/lurid/word_bytes';
|
|
import { API_CLIENT } from '../utils/api.ts';
|
|
import { Cookie, getSetCookies } from '@std/http/cookie';
|
|
import { generateTotp } from '@stdext/crypto/totp';
|
|
|
|
const TLDs: string[] = [
|
|
'com',
|
|
'org',
|
|
'net',
|
|
'edu',
|
|
'gov',
|
|
'nexus',
|
|
'shop',
|
|
'unreasonablylongtldname'
|
|
];
|
|
|
|
const random_byte_buffer: Uint8Array = new Uint8Array(3);
|
|
export function random_email_address(): string {
|
|
crypto.getRandomValues(random_byte_buffer);
|
|
const name = convert_to_words(random_byte_buffer).join('-');
|
|
|
|
crypto.getRandomValues(random_byte_buffer);
|
|
const domain = convert_to_words(random_byte_buffer).join('-');
|
|
|
|
const tld = TLDs[Math.floor(Math.random() * TLDs.length)];
|
|
return `${name}@${domain}.${tld}`;
|
|
}
|
|
|
|
export function random_username(): string {
|
|
crypto.getRandomValues(random_byte_buffer);
|
|
return convert_to_words(random_byte_buffer).join('-');
|
|
}
|
|
|
|
const BASE_PORT: number = 50_000;
|
|
const MAX_PORT_OFFSET: number = 10_000;
|
|
let current_port_offset = 0;
|
|
function get_next_free_port(): number {
|
|
let free_port: number | undefined = undefined;
|
|
let attempts = 0;
|
|
do {
|
|
const port_to_try: number = BASE_PORT + (current_port_offset++ % MAX_PORT_OFFSET);
|
|
|
|
try {
|
|
Deno.listen({ port: port_to_try }).close();
|
|
free_port = port_to_try;
|
|
} catch (error) {
|
|
if (!(error instanceof Deno.errors.AddrInUse)) {
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
++attempts;
|
|
|
|
if (attempts % MAX_PORT_OFFSET === 0) {
|
|
console.warn(`Tried all ports at least once while trying to locate a free one, something wrong?`);
|
|
}
|
|
} while (!free_port);
|
|
|
|
return free_port;
|
|
}
|
|
|
|
/**
|
|
* Interface defining the configuration for an ephemeral server
|
|
* @property {string} hostname - hostname bound to, default: 'localhost'
|
|
* @property {number} port - port bound to, default: next free port
|
|
* @property {SERVER} server - server instance
|
|
*/
|
|
export interface EPHEMERAL_SERVER {
|
|
hostname: string;
|
|
port: number;
|
|
server: SERVER;
|
|
}
|
|
|
|
/**
|
|
* Gets an ephemeral Serverus SERVER on an unused port.
|
|
*
|
|
* @param options Optional SERVER_OPTIONS
|
|
* @returns A LISTEN_SERVER_SETUP object with information and a reference to the server
|
|
*/
|
|
export async function get_ephemeral_listen_server(options?: SERVER_OPTIONS): Promise<EPHEMERAL_SERVER> {
|
|
const server_options = {
|
|
...{
|
|
hostname: 'localhost',
|
|
port: get_next_free_port()
|
|
},
|
|
...(options ?? {})
|
|
};
|
|
|
|
const server = new SERVER(server_options);
|
|
|
|
const ephemeral_server: EPHEMERAL_SERVER = {
|
|
hostname: server_options.hostname,
|
|
port: server_options.port,
|
|
server: await server.start()
|
|
};
|
|
|
|
return ephemeral_server;
|
|
}
|
|
|
|
type NEW_USER_INFO = {
|
|
username: string;
|
|
password: string;
|
|
};
|
|
|
|
export async function get_new_user(client: API_CLIENT, user_info?: Record<string, any>): Promise<Record<string, any>> {
|
|
const info: Record<string, any> = {
|
|
username: random_username(),
|
|
password: `${random_username()} ! ${random_username()}`,
|
|
...user_info
|
|
};
|
|
|
|
await client.fetch('/users', {
|
|
method: 'POST',
|
|
json: info
|
|
});
|
|
|
|
const cookies: Cookie[] = [];
|
|
const auth_response: any = await client.fetch('/auth', {
|
|
method: 'POST',
|
|
json: info,
|
|
done: (response) => {
|
|
cookies.push(...getSetCookies(response.headers));
|
|
}
|
|
});
|
|
|
|
info.user = auth_response.user;
|
|
info.session = auth_response.session;
|
|
|
|
cookies.push({
|
|
name: 'totp',
|
|
value: await generateTotp(info.session?.secret),
|
|
maxAge: 30,
|
|
expires: Date.now() + 30_000,
|
|
path: '/'
|
|
});
|
|
|
|
info.headers_for_get = new Headers();
|
|
for (const cookie of cookies) {
|
|
info.headers_for_get.append(`x-${cookie.name}`, cookie.value);
|
|
}
|
|
info.headers_for_get.append('cookie', cookies.map((cookie) => `${cookie.name}=${cookie.value}`).join('; '));
|
|
|
|
return info;
|
|
}
|
|
|
|
export async function set_user_permissions(client: API_CLIENT, user: any, session: any, permissions: string[]): Promise<any> {
|
|
return await client.fetch(`/users/${user.id}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'x-session_id': session.id,
|
|
'x-totp': await generateTotp(session.secret),
|
|
'x-test-override': 'true'
|
|
},
|
|
json: {
|
|
permissions
|
|
}
|
|
});
|
|
}
|