diff --git a/TODO.md b/TODO.md
index bb19174..2615b4b 100644
--- a/TODO.md
+++ b/TODO.md
@@ -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
diff --git a/main.ts b/main.ts
index d178395..a30f735 100644
--- a/main.ts
+++ b/main.ts
@@ -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.");
}
}
diff --git a/routes/admin.ts b/routes/admin.ts
index b144656..11688c0 100644
--- a/routes/admin.ts
+++ b/routes/admin.ts
@@ -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 ${code}. 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 ${code}. 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 ${code}.`);
- res.redirect("/mod");
- });
+ req.flash("success", `Your code has been created as ${code}.`);
+ res.redirect("/mod");
+ });
}
diff --git a/routes/updates.ts b/routes/updates.ts
index a907139..c27a289 100644
--- a/routes/updates.ts
+++ b/routes/updates.ts
@@ -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 } = (
diff --git a/routes/util.ts b/routes/util.ts
index addf4ef..cfe09a7 100644
--- a/routes/util.ts
+++ b/routes/util.ts
@@ -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 =
diff --git a/static/css/main.css b/static/css/main.css
index 6862e02..4dcf33a 100644
--- a/static/css/main.css
+++ b/static/css/main.css
@@ -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 {
diff --git a/static/favicon.png b/static/favicon.png
new file mode 100644
index 0000000..b6cb54e
Binary files /dev/null and b/static/favicon.png differ
diff --git a/views/_util.pug b/views/_util.pug
new file mode 100644
index 0000000..f74e0d4
--- /dev/null
+++ b/views/_util.pug
@@ -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
diff --git a/views/admin.pug b/views/admin.pug
index 36482e3..485e869 100644
--- a/views/admin.pug
+++ b/views/admin.pug
@@ -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
diff --git a/views/dashboard.pug b/views/dashboard.pug
index afd4c01..b25a838 100644
--- a/views/dashboard.pug
+++ b/views/dashboard.pug
@@ -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(() => {
diff --git a/views/index.pug b/views/index.pug
index 57b1133..39a2405 100644
--- a/views/index.pug
+++ b/views/index.pug
@@ -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
diff --git a/views/journal.pug b/views/journal.pug
index 62d2f5c..e24a515 100644
--- a/views/journal.pug
+++ b/views/journal.pug
@@ -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?)
diff --git a/views/journal_edit.pug b/views/journal_edit.pug
new file mode 100644
index 0000000..6f9b228
--- /dev/null
+++ b/views/journal_edit.pug
@@ -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")