//! TODO: There is like no error checking in queries at all //! TODO: Also no API? seriously? also when a form errors it just redirects with a 301 like everything is okay when it ISNT and it NEVER HAS BEEN import "dotenv/config"; import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime.js"; import express, { Express } from "express"; import bodyParser from "body-parser"; import session from "express-session"; import connectPgSimple from "connect-pg-simple"; import flash from "connect-flash"; import { drizzle, NodePgDatabase } from "drizzle-orm/node-postgres"; import { updates, users } from "./db/schema.js"; import { count, desc, eq } from "drizzle-orm"; // routes import adminRoutes from "./routes/admin.js"; import loginRoutes from "./routes/login.js"; import userRoutes from "./routes/users.js"; import updateRoutes from "./routes/updates.js"; import { createInviteCode, getMoods, render, render404, setNonce, UserStatus } from "./routes/util.js"; const db = drizzle(process.env.DATABASE_URL!); //! TODO: Make sure SQL queries arent being repeated too much (async () => { const { moods } = await getMoods(); // setup dayjs dayjs.extend(relativeTime); // setup express app const app = express(); app.set("view engine", "pug"); app.use(express.static("static")); app.use(bodyParser.urlencoded({ extended: false })); app.use( session({ store: new (connectPgSimple(session))(), secret: process.env.COOKIE_SECRET, resave: false, cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 } }) ); app.use(flash()); //== Content Security Policy app.use((_, res, next) => { res.setHeader("X-Powered-By", "Lots and lots of milk"); res.setHeader( "Content-Security-Policy", "\ script-src 'nonce-" + setNonce() + "';\ object-src 'none'; base-uri 'none';" ); return next(); }); await init(app, db); app.get("/", async (req, res) => { const upd = db .selectDistinctOn([updates.user], { date: updates.date, user: users.name }) .from(updates) .innerJoin(users, eq(updates.user, users.id)) .orderBy(updates.user, desc(updates.date)) .limit(25) .as("upd"); const recentUpdates = await db.select().from(upd).orderBy(desc(upd.date)); const date = dayjs(); const feedUpdates = ( await db .select({ user: users.name, mood: updates.mood, date: updates.date, desc: updates.description }) .from(updates) .innerJoin(users, eq(updates.user, users.id)) .limit(50) .orderBy(desc(updates.date)) ).map((e) => { return { user: e.user, mood: moods[e.mood], date: e.date, relativeDate: date.to(dayjs(e.date)), desc: e.desc }; }); render(db, "index", "home", res, req, { users: (await db.select().from(users)).length, recentUpdates, feedUpdates }); }); adminRoutes(app, db); await userRoutes(app, db); await updateRoutes(app, db); loginRoutes(app, db); app.get("*", (req, res) => { render404(db, res, req); }); app.listen(1337, () => { console.log("Listening on http://127.0.0.1:1337/"); }); })(); async function init(app: Express, db: NodePgDatabase) { const totalUsers = await db.select({ value: count() }).from(users); if (totalUsers[0].value === 0) { console.log("There are no users registered. Creating a temporary invite code right now."); console.log(" " + await createInviteCode(db, 0, new Date(Date.now() + (1 * 1000 * 60)), UserStatus.MODERATOR)); console.log("This code expires in 1 minute and will confer admin powers to whoever signs up with it."); } }