import { doModifyAvatar as dbModifyAvatar, doModifyAvatarIntro as dbModifyAvatarIntro, getAvatar, getLatestEnrolledAvatars as dbGetLatestEnrolledAvatars, getLatestLoggedInAvatars as dbGetLatestLoggedInAvatars, postAvatar as dbPostAvatar, setLastDate, } from "@/app/www/system/DB"; import { post, wipe } from "@/app/www/system/Totem"; import { AVATAR_ENTRY_PATH } from "@/app/www/utilities/Path"; import { equalCipher } from "@/app/www/utilities/Utility"; import { pbkdf2, randomBytes } from "crypto"; import { readFile } from "fs/promises"; import { join } from "path"; import { getDatetime } from "taehui-lib/date"; import { promisify } from "util"; const pw = promisify(pbkdf2); export const getTotem = async ( pendingAvatarID: string, pendingAvatarCipher: string, ) => { const data = await getAvatar(pendingAvatarID); if (data) { const { avatarID, avatarCipher, avatarName, level, fax, avatarIntro } = data; if (await equalCipher(avatarCipher, pendingAvatarCipher)) { await setLastDate(avatarID, getDatetime()); return { totem: await post(avatarID, getDatetime()), avatarID, avatarName, level, fax, avatarIntro, }; } } }; const getDBCipher = async (avatarCipher: string) => { const salt = randomBytes(24).toString("base64"); return `sha256:12000:${salt}:${Buffer.from( await pw(avatarCipher, salt, 12000, 24, "sha256"), ).toString("base64")}`; }; export const doModifyAvatar = async ( totem: string, avatarID: string, avatarCipher: string, avatarCipherModified: string, avatarName: string, fax: string, avatarIntro: string, ) => { if ( !!avatarCipher && !!avatarName && !avatarName.startsWith("@") && avatarName.length <= 16 && (!fax || /^.+@.+$/.test(fax)) && (await dbModifyAvatar( avatarID, avatarCipher, avatarCipherModified && (await getDBCipher(avatarCipherModified)), avatarName, fax, avatarIntro, )) ) { if (avatarCipherModified) { await wipe(totem); try { await fetch("http://localhost:4003/totem", { method: "PATCH", body: avatarID, }); } catch {} } return true; } else { return false; } }; export const doModifyAvatarIntro = async ( avatarID: string, avatarIntro: string, ) => { return dbModifyAvatarIntro(avatarID, avatarIntro); }; export const postAvatar = async ( avatarID: string, avatarCipher: string, avatarName: string, fax: string, ) => { return ( /[a-zA-Z\d]/.test(avatarID) && !!avatarCipher && !!avatarName && !avatarName.startsWith("@") && avatarName.length <= 16 && (!fax || /^.+@.+$/.test(fax)) && !(await getAvatar(avatarID)) && (await dbPostAvatar( avatarID, await getDBCipher(avatarCipher), avatarName, fax, getDatetime(), )) ); }; export const wipeTotem = async (avatarID: string, totem: string) => { return Promise.all([setLastDate(avatarID, getDatetime()), wipe(totem)]); }; export const getAvatarDrawing = async (avatarID: string) => { try { return await readFile(join(AVATAR_ENTRY_PATH, `${avatarID}.png`)); } catch (e: any) { if (e.code === "ENOENT") { return null; } throw e; } }; export const getLatestLoggedInAvatars = async () => { return dbGetLatestLoggedInAvatars(); }; export const getLatestEnrolledAvatars = async () => { return dbGetLatestEnrolledAvatars(); }; export const doInvalidateDrawing = async (avatarID: string) => { try { await fetch("http://localhost:4003/drawing", { method: "PATCH", body: avatarID, }); } catch {} };