import { NodePgDatabase } from "drizzle-orm/node-postgres"; import { Express } from "express"; import { follows, journalEntries, profiles, updates, users } from "../db/schema.js"; import { and, desc, eq, ne } from "drizzle-orm"; import { getMoods, render, render404, UserStatus, validateUrl } from "./util.js"; import { PgColumn } from "drizzle-orm/pg-core"; import dayjs from "dayjs"; export default async function (app: Express, db: NodePgDatabase) { const { moods } = await getMoods(); app.get("/users/:user", async (req, res) => { const isSelf = req.params.user === req.session["user"]; const user: { id: number; name: string; registered: Date; relativeRegistered?: string; bio: string; website: string; } = ( await db .select({ id: users.id, name: users.name, registered: users.registered, bio: profiles.bio, website: profiles.website }) .from(users) .where(eq(users.name, req.params.user)) .leftJoin(profiles, eq(profiles.user, users.id)) )[0]; if (!user) { render404(db, res, req); return; } user.relativeRegistered = dayjs(user.registered).fromNow(); // follows const isFollowing = !!( await db .select() .from(follows) .where( and( eq(follows.followerId, req.session["uid"]), eq(follows.userId, user.id) ) ) .limit(1) )[0]; // mood let moodSelection: { [k: string]: PgColumn } = { desc: updates.description, date: updates.date }; if (!isSelf) moodSelection.mood = updates.mood; const userMood: { [k: string]: string | number | Date } = ( await db .select(moodSelection) .from(updates) .where(eq(updates.user, user.id)) .orderBy(desc(updates.date)) .limit(1) )[0]; // journal entries const now = dayjs(); const userJournalEntries = ( await db .select({ id: journalEntries.id, title: journalEntries.title, date: journalEntries.date, visibility: journalEntries.visibility }) .from(journalEntries) .where( user.id === req.session["uid"] || req.session["status"] & UserStatus.MODERATOR ? eq(journalEntries.user, user.id) : and( eq(journalEntries.user, user.id), ne(journalEntries.visibility, 0) ) ) .orderBy(desc(journalEntries.date)) .limit(5) ).map((e) => { return { id: e.id, title: e.title, date: e.date, visibility: e.visibility, relativeDate: now.to(e.date) }; }); // feed const userMoodFeed = ( await db .select({ mood: updates.mood, date: updates.date, desc: updates.description }) .from(updates) .where(eq(updates.user, user.id)) .orderBy(desc(updates.date)) ).map((e) => { return { user: user.name, mood: moods[e.mood], date: e.date, relativeDate: now.to(dayjs(e.date)), desc: e.desc }; }); if (!isSelf && userMood) { userMood.mood = moods[userMood.mood as number]; } render(db, "user", `${req.params.user}'s profile`, res, req, { user, isSelf, userMood, userMoodFeed, userJournalEntries, isFollowing }); }); app.post("/users/:user/edit", async (req, res) => { if (!req.session["loggedIn"]) { res.redirect("/login"); return; } const { uname } = ( await db .select({ uname: users.name }) .from(users) .where(eq(users.name, req.params.user)) )[0]; if ( (uname || "") !== req.session["user"] && !(req.session["status"] & UserStatus.MODERATOR) ) { render404(db, res, req); return; } if (req.query.param && (req.session["status"] & UserStatus.MODERATOR)) { switch (req.query.param) { case "status": if (!req.query.status) { req.flash("error", "No status parameter specified."); res.redirect(req.get("Referrer") || "/"); return; } await db.update(users).set({ // @ts-expect-error status: parseInt(req.query.status) }).where(eq(users.name, req.params.user)); break; default: render404(db, res, req); return; } } else { if (!validateUrl(req.body.website)) { req.flash( "error", "The website URL provided is invalid or malformed." ); res.redirect(req.get("Referrer") || "/"); return; } await db .update(profiles) .set({ // @ts-expect-error bio: req.body.bio, website: req.body.website }) .where(eq(profiles.user, req.session["uid"])); req.flash("success", "Profile updated!"); res.redirect("/dashboard"); return; } req.flash("success", "Updated"); res.redirect(req.get("Referrer") || "/"); }); app.post("/users/:user/follow", async (req, res) => { if (!req.session["loggedIn"]) { res.redirect("/login"); return; } if (req.session["user"] === req.params.user) { req.flash("error", "Can't Follow Yourself Dummy"); res.redirect(`/users/${req.params.user}`); return; } const { uid } = ( await db .select({ uid: users.id }) .from(users) .where(eq(users.name, req.params.user)) )[0]; if (!uid) { render404(db, res, req); return; } const isFollowing = !!( await db .select() .from(follows) .where( and( eq(follows.followerId, req.session["uid"]), eq(follows.userId, uid) ) ) .limit(1) )[0]; if (isFollowing) { // unfollow await db .delete(follows) .where( and( eq(follows.followerId, req.session["uid"]), eq(follows.userId, uid) ) ); } else { await db.insert(follows).values({ userId: uid, followerId: req.session["uid"] }); } res.redirect(req.get("Referrer") || "/"); }); }