1
0
Fork 0
mirror of https://git.sr.ht/~roxwize/mipilin synced 2025-01-30 18:53:36 +00:00
Signed-off-by: roxwize <rae@roxwize.xyz>
This commit is contained in:
Rae 5e 2025-01-29 20:01:06 -05:00
parent ccc894dc33
commit dfd27154cd
Signed by: rae
GPG key ID: 5B1A0FAB9BAB81EE
5 changed files with 140 additions and 101 deletions

View file

@ -1,2 +1,3 @@
LISTEN_ON_SOCKET=false
DATABASE_URL=
COOKIE_SECRET=

View file

@ -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

220
main.ts
View file

@ -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."
);
}
}

View file

@ -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(() => {

View file

@ -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)