import * as crypto from "crypto"; import * as bcrypt from "bcrypt"; import { rand } from "mobiletto-orm"; import { DEFAULT_BCRYPT_TIME_TARGET, PrivateConfigType, PublicConfigType } from "yuebing-model"; import { Cached } from "~/server/utils/cached"; import { privateConfigRepository, publicConfigRepository } from "~/server/utils/repo/configRepo"; import { DEFAULT_PUBLIC_CONFIG, DEFAULT_PRIVATE_CONFIG } from "~/server/utils/default"; export const HAS_ADMIN: Cached = new Cached( async (): Promise => { const admins = await accountRepository().safeFindBy("admin", true); const found = (admins && admins.length > 0) || false; if (found) { HAS_ADMIN.timeout = undefined; } else { HAS_ADMIN.timeout = 1000 * 20; } return found; }, { name: "hasAdmin", default: false, timeout: 60000 }, ); export const hasAdmin = (): Promise => { return HAS_ADMIN.get(); }; export const PUBLIC_CONFIG: Cached = new Cached( async (): Promise => { return Object.assign({}, await publicConfigRepository().findSingleton(), { needsAdmin: !(await hasAdmin()), crypto: { ciphers: crypto.getCiphers() }, }); }, { name: "publicConfig", timeout: 60000, default: async () => Object.assign({}, DEFAULT_PUBLIC_CONFIG, { needsAdmin: !(await hasAdmin()), crypto: { ciphers: crypto.getCiphers() }, }) as PublicConfigType, }, ); export const PRIVATE_CONFIG: Cached = new Cached( async (): Promise => await privateConfigRepository().findSingleton(), { name: "privateConfig", default: DEFAULT_PRIVATE_CONFIG, timeout: 60000 }, ); export const registrationEnabled = async (): Promise => (await PUBLIC_CONFIG.get()).registrationEnabled || false; export const needsAdmin = async (): Promise => (await PUBLIC_CONFIG.get()).needsAdmin || false; export const bcryptTimeTarget = async (): Promise => (await PRIVATE_CONFIG.get()).auth?.bcryptTimeTarget || DEFAULT_BCRYPT_TIME_TARGET; const bcryptStartRounds = 8; const bcryptDurations: Record = {}; export const bcryptRounds = async (): Promise => { const target = await bcryptTimeTarget(); const data = rand(128); for (let rounds = bcryptStartRounds; true; rounds++) { if (!bcryptDurations[rounds]) { const start = Date.now(); await bcrypt.hash(data, rounds); bcryptDurations[rounds] = Date.now() - start; } if (bcryptDurations[rounds] >= target) return rounds + 1; } };