import { NodePgDatabase } from "drizzle-orm/node-postgres"; import type { Request, Response } from "express"; import { inviteCodes, updates } from "../db/schema.js"; import { count, desc, eq } from "drizzle-orm"; import fs from "node:fs/promises"; export enum UserStatus { MODERATOR = 0b0001, BANNED = 0b0010, TRUSTED = 0b0100, GUEST = 0b1000 } const nonceChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-_"; let nonce: string; export function setNonce() { nonce = ""; for (let i = 0; i < 32; i++) nonce += nonceChars[Math.floor(Math.random() * nonceChars.length)]; return nonce; } export function getNonce() { if (!nonce) throw new Error("Nonce doesn't exist"); return nonce; } let moods: string[], moodsSorted: string[]; export async function getMoods() { if (!moods) moods = (await fs.readFile("./static/moods.txt")) .toString("utf-8") .split(";"); if (!moodsSorted) moodsSorted = Array.from(moods).sort(); return { moods, moodsSorted }; } export async function render( db: NodePgDatabase, page: string, title: string, res: Response, req: Request, stuff?: Object ) { //? maybe you should cache this and save the current mood to the session until it's changed const { moods } = await getMoods(); let currentMood: string; if (req.session["loggedIn"]) { const update = ( await db .select({ mood: updates.mood }) .from(updates) .where(eq(updates.user, req.session["uid"])) .orderBy(desc(updates.date)) .limit(1) )[0]; currentMood = moods[update?.mood]; } const o = { title, session: req.session, flashes: req.flash(), moods, currentMood, nonce }; res.render(page, { ...o, ...stuff }); } export async function render404( db: NodePgDatabase, res: Response, req: Request ) { res.statusCode = 404; render(db, "404", "not found", res, req); } const inviteCodeChars = "abcdefghijklmnopqrstuvwxyz0123456789"; export async function createInviteCode( db: NodePgDatabase, user: number, expires: Date, confers = 0 ) { let existingToken = 1, token: string; while (existingToken) { token = user.toString().padStart(4, "0") + "-"; for (let i = 0; i < 17; i++) { if ((i + 1) % 6 === 0) { token += "-"; continue; } token += inviteCodeChars[ Math.floor(Math.random() * inviteCodeChars.length) ]; } existingToken = ( await db .select({ value: count() }) .from(inviteCodes) .where(eq(inviteCodes.token, token)) )[0].value; } //@ts-expect-error await db.insert(inviteCodes).values({ token, user: user || undefined, granted: new Date(Date.now()), expires, confers }); return token; } export function journalMoodString(mood: number) { switch (mood) { case -2: return "much worse"; case -1: return "worse"; default: case 0: return "about the same"; case 1: return "better"; case 2: return "much better"; } } export function confirm( db: NodePgDatabase, res: Response, req: Request ) { render(db, "confirm", "Confirm action", res, req, { body: req.body, url: req.url }); } const emailRegex = /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/i; const urlRegex = /https?:\/\/(?:www\.)?(?:[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b)*(?:\/[\/\d\w\.-]*)*(?:[\?])*(?:.+)*/i; export function validateEmail(email: string) { return emailRegex.test(email); } export function validateUrl(url: string) { return urlRegex.test(url); }