From dfd27154cd71f25a867edf0009fea66bc1752690 Mon Sep 17 00:00:00 2001 From: roxwize Date: Wed, 29 Jan 2025 20:01:06 -0500 Subject: [PATCH] weh Signed-off-by: roxwize --- .env.example | 1 + TODO.md | 1 + main.ts | 220 +++++++++++++++++++++++++------------------- views/dashboard.pug | 6 +- views/index.pug | 13 ++- 5 files changed, 140 insertions(+), 101 deletions(-) diff --git a/.env.example b/.env.example index edfd346..9f77619 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ +LISTEN_ON_SOCKET=false DATABASE_URL= COOKIE_SECRET= diff --git a/TODO.md b/TODO.md index da1fad8..fb94db9 100644 --- a/TODO.md +++ b/TODO.md @@ -16,3 +16,4 @@ - [ ] Private journals are stored in plaintext and can be found if a database breach happens, maybe (optionally?) encrypt them (with PGP keys?) (or database encryption) - [ ] What If mipilin Existed On Geminispace - [ ] Failed requests that dont go through just redirect to other pages with a 200 or 302 when u SHOULD be using an external api that actually properly communicates these errors and just tells it to the frontend +- [ ] Users posting about suicide or experiencing sexual abuse should have a popup for reaching out even though its overdone. Let them still make the post but tell them that they can also call 988 or whatever CA or those other places have OR they could just dm a mod diff --git a/main.ts b/main.ts index 94f28cc..788020a 100644 --- a/main.ts +++ b/main.ts @@ -20,118 +20,144 @@ 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"; +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(); + const { moods } = await getMoods(); - // setup dayjs - dayjs.extend(relativeTime); + // 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(); - }); - app.use((req, res, next) => { - let s = ""; - for (const [k, v] of Object.entries(req.query)) - s += `${k}=${v}&`; - s = s.slice(0, -1); - - console.log(`${req.ip.padEnd(24)} ${req.method.padStart(8)} ${req.path}${s ? "?" + s : ""}`); - 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 + // 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 } }) - .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 - }; + ); + 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(); + }); + app.use((req, res, next) => { + let s = ""; + for (const [k, v] of Object.entries(req.query)) s += `${k}=${v}&`; + s = s.slice(0, -1); + + console.log( + `${req.ip.padEnd(24)} ${req.method.padStart(8)} ${req.path}${ + s ? "?" + s : "" + }` + ); + return next(); }); - render(db, "index", "home", res, req, { - users: (await db.select().from(users)).length, - recentUpdates, - feedUpdates + 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); + adminRoutes(app, db); + await userRoutes(app, db); + await updateRoutes(app, db); + loginRoutes(app, db); - app.get("*", (req, res) => { - render404(db, res, req); - }); + app.get("*", (req, res) => { + render404(db, res, req); + }); - app.listen(1337, () => { - console.log("Listening on http://127.0.0.1:1337/"); - }); + const path = process.env.LISTEN_ON_SOCKET === "true" ? `/run/user/${process.getuid()}/mipilin/mipilin.sock` : 1337; + app.listen(path, () => { + console.log(`mipilin is now listening on ${path}!! Requests will be printed below. Good Luck`); + }); })(); 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() + (10 * 1000 * 60)), UserStatus.MODERATOR)); - console.log("This code expires in 10 minutes and will confer admin powers to whoever signs up with it."); - } + 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() + 10 * 1000 * 60), + UserStatus.MODERATOR + )) + ); + console.log( + "This code expires in 10 minutes and will confer admin powers to whoever signs up with it." + ); + } } diff --git a/views/dashboard.pug b/views/dashboard.pug index 667b6b4..453e0ea 100644 --- a/views/dashboard.pug +++ b/views/dashboard.pug @@ -40,7 +40,7 @@ block content a(href="#invite-codes") Invite codes p This is where you "MIPILIN"! That is all you need to know!!! p If onlookers notice your actions and inquire about what you are doing, you MUST tell them that you are mipilining all over the place, and then PROMPTLY SCROLL DOWN AND GENERATE AN INVITE CODE SO THAT THEY CAN MIPILIN TOO. - form#dashboard-update-form(action="/update/mood", method="post", onsubmit="disable(this);") + form#dashboard-update-form(action="/update/mood", method="post") select(name="mood", required) //- Maybe put the index of the mood in the value of the option element //- so that indexOf (slow) wont have to be used everytime mood is updated @@ -78,8 +78,8 @@ block content if !isTrusted p You need be manually marked as "trusted" to generate codes. script(nonce=nonce). - function disable(form) { - const btn = form.querySelector("button"); + document.getElementById("dashboard-update-form").onsubmit = (ev) => { + const btn = ev.target.querySelector("button"); btn.setAttribute("disabled", ""); setTimeout(() => { diff --git a/views/index.pug b/views/index.pug index 1e016d9..3d2e904 100644 --- a/views/index.pug +++ b/views/index.pug @@ -45,6 +45,17 @@ block content | mipilin is free and open source forevur. If you want to CHECK OUT TEH CODE or FILE AN ISSUE then you may do so at the respective links provided on the a(href="https://sr.ht/~roxwize/mipilin") project page | . - h1 Global Feed + h1 Important beta notices + p + | If you just want to see the global feed, click + a(href="#home-feed") here + | . + p + | mipilin is still in a closed beta. Trusted users verified manually by moderators can generate invite codes and give them out to whoever they wish. Invite codes will also be given upon request to members of Hack Club and people who contact me on Discord, my handle on the Slack workspace is roxwize. + p + | During the duration of the beta, updates will be posted in its channel in the workspace (#mipilin) and eventually on the website as well. Expect lots of new stuff to come and go, and for the site to go offline occasionally as I push updates and restart the server for various reasons. + p + | Right now there is no formal set of rules. All you should know to do is use your common sense, and don't go out of your way to harass other people. Peace and love. + h1#home-feed Global Feed p Look at how all these people are doing!!! +feed(feedUpdates)