1
0
Fork 0
mirror of https://git.sr.ht/~roxwize/mipilin synced 2025-01-31 02:53:36 +00:00

Some iprovements idfk

Signed-off-by: roxwize <rae@roxwize.xyz>
This commit is contained in:
Rae 5e 2025-01-27 23:34:46 -05:00
parent a3e6df6ce8
commit 961d963677
Signed by: rae
GPG key ID: 5B1A0FAB9BAB81EE
13 changed files with 160 additions and 102 deletions

View file

@ -1,13 +1,14 @@
- [ ] An audit log
- [ ] Invite code pruning
- [ ] Make the journal work (i dont really remember what was left out though)
- [x] Make the journal work (i dont really remember what was left out though)
- [x] Write a 404 page
- [ ] Make better login pages
- [ ] You do realize using toLocaleString on the server only makes it use your locale right
- [ ] Make recent updates also account for new journal entries
- [ ] View all previous moods and journal entries
- [ ] Visibility indicator on journal entries
- [ ] Hide journal entries from feed that are hidden unless current user is a moderator
- [x] Visibility indicator on journal entries
- [x] Hide journal entries from feed that are hidden unless current user is a moderator
- [ ] Edit/delete journal entries?
- [ ] Journal entry comments
- [ ] A Forum
- [ ] Make it easier to view journal entries

View file

@ -126,7 +126,7 @@ 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.");
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

@ -5,96 +5,114 @@ import { inviteCodes, users } from "../db/schema.js";
import { and, count, desc, eq, sql } from "drizzle-orm";
import dayjs from "dayjs";
const USER_REFERRAL_EXPIRATION = 7 * 24 * 60 * 60 * 1000
const USER_REFERRAL_EXPIRATION = 7 * 24 * 60 * 60 * 1000;
export default function (app: Express, db: NodePgDatabase) {
app.get("/mod", async (req, res) => {
if (
!req.session["loggedIn"] ||
!(req.session["status"] & UserStatus.MODERATOR)
) {
render404(db, res, req);
return;
}
app.get("/mod", async (req, res) => {
if (
!req.session["loggedIn"] ||
!(req.session["status"] & UserStatus.MODERATOR)
) {
render404(db, res, req);
return;
}
const now = dayjs();
const codes = (
await db
.select({
expires: inviteCodes.expires,
token: inviteCodes.token,
uname: users.name
})
.from(inviteCodes)
.leftJoin(users, eq(inviteCodes.user, users.id))
.orderBy(desc(inviteCodes.granted))
).map((e) => {
return {
expires: e.expires,
token: e.token,
uname: e.uname,
expiresString: now.to(dayjs(e.expires))
};
const now = dayjs();
const codes = (
await db
.select({
expires: inviteCodes.expires,
token: inviteCodes.token,
uname: users.name
})
.from(inviteCodes)
.leftJoin(users, eq(inviteCodes.user, users.id))
.orderBy(desc(inviteCodes.granted))
).map((e) => {
return {
expires: e.expires,
token: e.token,
uname: e.uname,
expiresString: now.to(dayjs(e.expires || 0))
};
});
// TODO: also add a last login field to the user schema (and paginate this area)
const userTable = await db
.select({ uname: users.name, id: users.id, status: users.status })
.from(users)
.orderBy(desc(users.registered))
.limit(10);
render(db, "admin", "admin panel", res, req, { codes, userTable });
});
render(db, "admin", "admin panel", res, req, { codes });
});
app.post("/codes/delete", async (req, res) => {
if (
!req.session["loggedIn"] ||
!(req.session["status"] & UserStatus.MODERATOR)
) {
res.redirect("/");
return;
}
app.post("/codes/delete", async (req, res) => {
if (
!req.session["loggedIn"] ||
!(req.session["status"] & UserStatus.MODERATOR)
) {
res.redirect("/");
return;
}
await db.delete(inviteCodes).where(eq(inviteCodes.token, req.body.token));
req.flash("success", "Deleted.");
res.redirect("/mod");
});
app.post("/codes/create", async (req, res) => {
if (
!req.session["loggedIn"]
) {
res.redirect("/login");
return;
}
if (!(req.session["status"] & UserStatus.MODERATOR)) {
const { codesUsed } = (
await db
.select({ codesUsed: count() })
.from(inviteCodes)
.where(
and(
eq(inviteCodes.user, req.session["uid"]),
eq(
sql`extract(month from granted)`,
sql`extract(month from current_date)`
)
)
)
)[0];
if (codesUsed >= 5) {
req.flash("error", "You've generated the maximum of five codes this week. Your counter will reset next month.");
res.redirect("/dashboard");
return;
}
.delete(inviteCodes)
.where(eq(inviteCodes.token, req.body.token));
req.flash("success", "Deleted.");
res.redirect("/mod");
});
app.post("/codes/create", async (req, res) => {
if (!req.session["loggedIn"]) {
res.redirect("/login");
return;
}
if (!(req.session["status"] & UserStatus.MODERATOR)) {
const { codesUsed } = (
await db
.select({ codesUsed: count() })
.from(inviteCodes)
.where(
and(
eq(inviteCodes.user, req.session["uid"]),
eq(
sql`extract(month from granted)`,
sql`extract(month from current_date)`
)
)
)
)[0];
if (codesUsed >= 5) {
req.flash(
"error",
"You've generated the maximum of five codes this week. Your counter will reset next month."
);
res.redirect("/dashboard");
return;
}
const code = await createInviteCode(db, req.session["uid"], new Date(Date.now() + USER_REFERRAL_EXPIRATION));
req.flash("success", `Your code has been created as <b>${code}</b>. It expires in a week so use it ASAP!!!`);
res.redirect("/dashboard");
return;
}
const code = await createInviteCode(
db,
req.session["uid"],
new Date(Date.now() + USER_REFERRAL_EXPIRATION)
);
req.flash(
"success",
`Your code has been created as <b>${code}</b>. It expires in a week so use it ASAP!!!`
);
res.redirect("/dashboard");
return;
}
const expiration = new Date(req.body.expiration || 0);
if (req.body.expiration && expiration.getTime() <= Date.now()) {
req.flash("error", "Chosen expiration date is in the past.");
res.redirect("/mod");
return;
}
const code = await createInviteCode(db, req.session["uid"], expiration);
const expiration = new Date(req.body.expiration || 0);
if (req.body.expiration && expiration.getTime() <= Date.now()) {
req.flash("error", "Chosen expiration date is in the past.");
res.redirect("/mod");
return;
}
const code = await createInviteCode(db, req.session["uid"], expiration);
req.flash("success", `Your code has been created as <b>${code}</b>.`);
res.redirect("/mod");
});
req.flash("success", `Your code has been created as <b>${code}</b>.`);
res.redirect("/mod");
});
}

View file

@ -93,7 +93,8 @@ export default async function (app: Express, db: NodePgDatabase) {
).map((e) => {
return {
token: e.token,
expires: now.to(dayjs(e.expires || 0))
expires: e.expires,
expiresString: now.to(dayjs(e.expires || 0))
};
});
const { codesUsed } = (

View file

@ -5,9 +5,10 @@ import { count, desc, eq } from "drizzle-orm";
import fs from "node:fs/promises";
export enum UserStatus {
MODERATOR = 0b001,
BANNED = 0b010,
TRUSTED = 0b100
MODERATOR = 0b0001,
BANNED = 0b0010,
TRUSTED = 0b0100,
GUEST = 0b1000
}
const nonceChars =

View file

@ -103,17 +103,19 @@ summary {
}
#page {
position: relative;
height: calc(100% - 1.5em - 76px);
overflow-y: auto;
}
#sidebar {
padding: 1em;
position: sticky;
top: 0;
float: left;
border-right: 1px solid #afa870;
padding: 1em;
width: 256px;
height: 100%;
margin-right: 24px;
border-right: 1px solid #afa870;
}
#content {

BIN
static/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

8
views/_util.pug Normal file
View file

@ -0,0 +1,8 @@
mixin invite_code_expiration(code)
- const timestamp = code.expires.getTime()
if timestamp === 0
td.subtle never
else if Date.now() >= timestamp
td.error EXPIRED
else
td= code.expiresString

View file

@ -1,7 +1,22 @@
extends site.pug
include _util.pug
block content
h1 Admin Panel
p Don't Be Evil
h2 Users
table
tbody
tr
th UID
th Name
th Status
for user of userTable
tr
td!= user.id.toString()
td
a(href=`/users/${user.uname}`)= user.uname
td= user.status.toString(2).padStart(4, "0")
h2 Invite codes
form(action="/codes/delete", method="post")
table
@ -14,12 +29,7 @@ block content
- const timestamp = code.expires.getTime()
tr
td= code.token
if timestamp === 0
td.subtle never
else if Date.now() >= timestamp
td.error EXPIRED
else
td= code.expiresString
+invite_code_expiration(code)
td
a(href=`/users/${code.uname}`)= code.uname
td

View file

@ -1,4 +1,5 @@
extends site.pug
include _util.pug
block head
link(rel="stylesheet", href="/css/dashboard.css")
@ -25,6 +26,8 @@ block content
a(href="#feed") Feed
| /
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. If you do not do this I will cry.
form#dashboard-update-form(action="/update/mood", method="post", onsubmit="disable(this);")
select(name="mood", required)
//- Maybe put the index of the mood in the value of the option element
@ -53,7 +56,7 @@ block content
for code of codes
tr
td= code.token
td= code.expires
+invite_code_expiration(code)
else
.subtle You have no currently active invite codes.
br
@ -61,7 +64,9 @@ block content
button(type="submit", disabled=codesUsed>=5) Generate
script(nonce=nonce).
function disable(form) {
form.preventDefault();
const btn = form.querySelector("button");
btn.preventDefault();
btn.setAttribute("disabled", true);
setTimeout(() => {

View file

@ -33,7 +33,13 @@ block page
block content
p
| Hi, this is mipilin! It lets you tell your friends how you're feeling, as well as keep a journal of your current mood and their trends over time! Due respect goes to imood, from which I borrowed many ideas and basically all of the moods.
| Hi, this is mipilin (pronounced /mipilin/)! It lets you tell your friends how you're feeling, as well as keep a journal of your current mood and their trends over time! Due respect goes to
a(href="https://www.imood.com/") imood
|, from which I borrowed many ideas and basically all of the moods.
p
| 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
p Look at how all these people are doing!!!
include _feed.pug

View file

@ -7,6 +7,7 @@ block head
block content
h1 Your Journal
p This is where you can log your overall mood every day, and get a glimpse at how your life is going so far!
p In the near future there will be a magnificient graph that will let you visualize your past entries and your mood trends. You must tay stuned.
form#journal-update(action="/update/journal", method="post")
.input
span Overall mood change (how do you feel compared to yesterday?)

5
views/journal_edit.pug Normal file
View file

@ -0,0 +1,5 @@
extends site.pug
block content
p God i fucking hate myself
textarea(name="description", id="description", placeholder="max 4096 chars", maxlength="4096", cols="60", rows="12")