import { createCache } from '$lib/cache.server'; import { cacheUpdater, cachedMethod, refExtendCachedMethod } from './root'; import { sql } from '$lib/db.server'; import { argon2id, hash, verify } from 'argon2'; import { PostgresError } from 'postgres'; const cache = createCache(); /** * @template T * @typedef {import('$types/base').Result} Result */ /** * @typedef {import('$types/base').User} User */ /** * @param {import('postgres').Row} row * @returns {User} */ function parseUserFromRow(row) { return { id: row['id'], name: row['username'], joinDate: row['join_time'] }; } /** * @param {Result} users * @returns {Result} */ const updateUserCache = cacheUpdater(cache); /** * @param {number[]} user_ids * @returns {Promise>} */ export const getUsersCached = cachedMethod(cache, getUsers); export const getUsersCachedByRef = refExtendCachedMethod(getUsersCached); /** * @param {number[]} user_ids * @returns {Promise>} */ export async function getUsers(user_ids) { if (user_ids.length == 0) return new Map(); const query = sql` SELECT id, username, join_time FROM doki8902.user WHERE id IN ${ sql(user_ids) };`; let users = await query; /** * @type {Result} */ let result = new Map(); users.forEach(row => { result.set(row['id'], parseUserFromRow(row)); }) return updateUserCache(result); } /** * @param {number} user_id * @returns {Promise} */ export async function getUser(user_id) { const users = await getUsers([user_id]); return users.get(user_id) || { error: true, msg: `Could not find user of ID ${user_id}` }; } /** * @param {string} username * @param {string} password * @returns {Promise} */ export async function registerUser(username, password) { const hashedPassword = await hash(password, { type: argon2id, memoryCost: 2 ** 16, timeCost: 4, parallelism: 1, hashLength: 64, }); const insert = sql` INSERT INTO doki8902.user (username, password) VALUES (${ username }, ${ hashedPassword });`; try { await insert; } catch (e) { if (e && typeof(e) === 'object' && 'name' in e && e.name === 'PostgresError') { const pgerr = /** @type {PostgresError} */ (e); switch (pgerr.constraint_name) { case 'idx_user_username': return { error: true, msg: "Username taken", }; case 'username_length_min': return { error: true, msg: "Username has invalid length", }; case 'username_valid_symbols': return { error: true, msg: "Username contains invalid symbols", }; default: break; } } } return { success: true, }; }