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

mipilin r9... Fimally

Signed-off-by: roxwize <rae@roxwize.xyz>
This commit is contained in:
Rae 5e 2025-01-28 17:05:54 -05:00
parent 961d963677
commit fa8fae4638
Signed by: rae
GPG key ID: 5B1A0FAB9BAB81EE
9 changed files with 217 additions and 150 deletions

View file

@ -12,3 +12,5 @@
- [ ] Journal entry comments - [ ] Journal entry comments
- [ ] A Forum - [ ] A Forum
- [ ] Make it easier to view journal entries - [ ] Make it easier to view journal entries
- [x] Users can fuck with invite codes by using an invite code and then making an account with it and then having more invite codes SO MAKE IT The case that only users with the trusted status may Do that
- [ ] Private journals are stored in plaintext and can be found if a database breach happens, maybe (optionally?) encrypt them (with PGP keys?)

View file

@ -68,6 +68,11 @@ export default function (app: Express, db: NodePgDatabase) {
return; return;
} }
if (!(req.session["status"] & UserStatus.MODERATOR)) { if (!(req.session["status"] & UserStatus.MODERATOR)) {
if (!(req.session["status"] & UserStatus.TRUSTED)) {
req.flash("error", "Only trusted users can perform this action.");
res.redirect(req.get("Referrer") || "/");
return;
}
const { codesUsed } = ( const { codesUsed } = (
await db await db
.select({ codesUsed: count() }) .select({ codesUsed: count() })
@ -87,7 +92,7 @@ export default function (app: Express, db: NodePgDatabase) {
"error", "error",
"You've generated the maximum of five codes this week. Your counter will reset next month." "You've generated the maximum of five codes this week. Your counter will reset next month."
); );
res.redirect("/dashboard"); res.redirect(req.get("Referrer") || "/");
return; return;
} }
@ -100,19 +105,19 @@ export default function (app: Express, db: NodePgDatabase) {
"success", "success",
`Your code has been created as <b>${code}</b>. It expires in a week so use it ASAP!!!` `Your code has been created as <b>${code}</b>. It expires in a week so use it ASAP!!!`
); );
res.redirect("/dashboard"); res.redirect(req.get("Referrer") || "/");
return; return;
} }
const expiration = new Date(req.body.expiration || 0); const expiration = new Date(req.body.expiration || 0);
if (req.body.expiration && expiration.getTime() <= Date.now()) { if (req.body.expiration && expiration.getTime() <= Date.now()) {
req.flash("error", "Chosen expiration date is in the past."); req.flash("error", "Chosen expiration date is in the past.");
res.redirect("/mod"); res.redirect(req.get("Referrer") || "/");
return; return;
} }
const code = await createInviteCode(db, req.session["uid"], expiration); const code = await createInviteCode(db, req.session["uid"], expiration);
req.flash("success", `Your code has been created as <b>${code}</b>.`); req.flash("success", `Your code has been created as <b>${code}</b>.`);
res.redirect("/mod"); res.redirect(req.get("Referrer") || "/");
}); });
} }

View file

@ -2,7 +2,7 @@ import { Express } from "express";
import bcrypt from "bcrypt"; import bcrypt from "bcrypt";
import { render } from "./util.js"; import { render } from "./util.js";
import { NodePgDatabase } from "drizzle-orm/node-postgres"; import { NodePgDatabase } from "drizzle-orm/node-postgres";
import { inviteCodes, profiles, users } from "../db/schema.js"; import { follows, inviteCodes, profiles, users } from "../db/schema.js";
import { eq } from "drizzle-orm"; import { eq } from "drizzle-orm";
//! TEMP Also not sanitized like at all //! TEMP Also not sanitized like at all
@ -21,6 +21,11 @@ export default function(app: Express, db: NodePgDatabase) {
return; return;
} }
// validation // validation
if (!req.body.name || !req.body.referral || !req.body.email || !req.body.pass) {
req.flash("error", "A required field wasn't filled in.");
res.redirect("/register");
return;
}
if (req.body.referral.length < 22) { if (req.body.referral.length < 22) {
req.flash("error", "Invalid invite code! Make sure you pasted it in correctly WITH the hyphens."); req.flash("error", "Invalid invite code! Make sure you pasted it in correctly WITH the hyphens.");
res.redirect("/register"); res.redirect("/register");
@ -36,7 +41,11 @@ export default function(app: Express, db: NodePgDatabase) {
res.redirect("/register"); res.redirect("/register");
return; return;
} }
if (!req.body.name.match(/[A-Z0-9_-]/i)) {
//! dumb
req.body.name = req.body.name.trim();
const match = req.body.name.match(/[A-Z0-9_-]+/i);
if (match?.[0] !== req.body.name) {
req.flash( req.flash(
"error", "error",
"Username can only contain letters, numbers, underscores, hyphens, and periods!!" "Username can only contain letters, numbers, underscores, hyphens, and periods!!"
@ -101,6 +110,13 @@ export default function(app: Express, db: NodePgDatabase) {
)[0]; )[0];
await db.insert(profiles).values({ user: uid }); await db.insert(profiles).values({ user: uid });
// Follow me by default ;w;;;
//! Also this assumes that im at id 1 which might not be true ever
await db.insert(follows).values({
userId: 1,
followerId: uid
});
req.session["loggedIn"] = true; req.session["loggedIn"] = true;
req.session["status"] = code.confers; req.session["status"] = code.confers;
req.session["user"] = req.body.name; req.session["user"] = req.body.name;

View file

@ -112,6 +112,12 @@ export default async function (app: Express, db: NodePgDatabase) {
) )
)[0]; )[0];
const followed = await db
.select({ uname: users.name })
.from(follows)
.where(eq(follows.followerId, req.session["uid"]))
.innerJoin(users, eq(follows.userId, users.id));
render(db, "dashboard", "dashboard", res, req, { render(db, "dashboard", "dashboard", res, req, {
user, user,
moods, moods,
@ -120,6 +126,8 @@ export default async function (app: Express, db: NodePgDatabase) {
recentUpdates, recentUpdates,
codes, codes,
codesUsed, codesUsed,
followed,
isTrusted: req.session["status"] & (UserStatus.MODERATOR | UserStatus.TRUSTED),
feed: [] feed: []
}); });
}); });
@ -128,6 +136,15 @@ export default async function (app: Express, db: NodePgDatabase) {
res.redirect("/login"); res.redirect("/login");
return; return;
} }
// make sure the user isnt updating too fast
//! TODO: also do this for journal entries
const lastUpdate = (await db.select({ date: updates.date }).from(updates).where(eq(updates.user, req.session["uid"])).orderBy(desc(updates.date)).limit(1))?.[0];
if (Date.now() < lastUpdate?.date?.getTime() + 10 * 1000) {
req.flash("error", "You're updating your mood too fast! Wait ten seconds between updates.");
res.redirect(req.get("Referrer") || "/");
return;
}
const moodIndex = moods.indexOf(req.body.mood.trim()); const moodIndex = moods.indexOf(req.body.mood.trim());
if (moodIndex === -1) { if (moodIndex === -1) {
req.flash( req.flash(
@ -205,7 +222,8 @@ export default async function (app: Express, db: NodePgDatabase) {
if ( if (
!entry || !entry ||
(entry.visibility === 0 && (entry.visibility === 0 &&
entry.uname !== req.session["user"] && !isMod) entry.uname !== req.session["user"] &&
!isMod)
) { ) {
render404(db, res, req); render404(db, res, req);
return; return;
@ -247,16 +265,19 @@ export default async function (app: Express, db: NodePgDatabase) {
)[0]; )[0];
const isMod = req.session["status"] & UserStatus.MODERATOR; const isMod = req.session["status"] & UserStatus.MODERATOR;
if ( if (!entry || (entry?.uid !== req.session["uid"] && !isMod)) {
!entry ||
(entry?.uid !== req.session["uid"] &&
!isMod)
) {
render404(db, res, req); render404(db, res, req);
return; return;
} }
if (isMod && entry.uid !== req.session["uid"] && req.body.action !== "delete") { if (
req.flash("error", "Moderators can only delete other users' posts."); isMod &&
entry.uid !== req.session["uid"] &&
req.body.action !== "delete"
) {
req.flash(
"error",
"Moderators can only delete other users' posts."
);
res.redirect(`/journal/${req.params.id}`); res.redirect(`/journal/${req.params.id}`);
return; return;
} }

View file

@ -149,7 +149,7 @@ export default async function (app: Express, db: NodePgDatabase) {
(uname || "") !== req.session["user"] && (uname || "") !== req.session["user"] &&
!(req.session["status"] & UserStatus.MODERATOR) !(req.session["status"] & UserStatus.MODERATOR)
) { ) {
res.redirect("back"); res.redirect(req.get("Referrer") || "/");
return; return;
} }
@ -182,11 +182,7 @@ export default async function (app: Express, db: NodePgDatabase) {
.where(eq(users.name, req.params.user)) .where(eq(users.name, req.params.user))
)[0]; )[0];
if (!uid) { if (!uid) {
req.flash( render404(db, res, req);
"error",
"It looks like you're trying to follow a user that doesn't exist anymore."
);
res.redirect("/");
return; return;
} }
const isFollowing = !!( const isFollowing = !!(
@ -217,6 +213,6 @@ export default async function (app: Express, db: NodePgDatabase) {
followerId: req.session["uid"] followerId: req.session["uid"]
}); });
} }
res.redirect(`/users/${req.params.user}`); res.redirect(req.get("Referrer") || "/");
}); });
} }

View file

@ -185,10 +185,18 @@ select {
table button { table button {
padding: 0 4px; padding: 0 4px;
} }
th, td { th,
td {
text-align: left; text-align: left;
padding-right: 1em; padding-right: 1em;
} }
tr:nth-child(odd) td {
background-color: rgba(0, 0, 0, 0.1);
}
tr:hover td {
background-color: rgba(0, 0, 0, 0.2);
cursor: default;
}
.feed-update { .feed-update {
background-color: #d5d4bb; background-color: #d5d4bb;

View file

@ -18,6 +18,7 @@ block content
a(href=`/users/${user.uname}`)= user.uname a(href=`/users/${user.uname}`)= user.uname
td= user.status.toString(2).padStart(4, "0") td= user.status.toString(2).padStart(4, "0")
h2 Invite codes h2 Invite codes
p Well Rae. You've made quite a The Closed Beta.
form(action="/codes/delete", method="post") form(action="/codes/delete", method="post")
table table
tbody tbody

View file

@ -15,11 +15,23 @@ block page
button(type="submit") Edit button(type="submit") Edit
h1(style="margin-top:1em;") Mood history h1(style="margin-top:1em;") Mood history
ul#dashboard-mood-history ul#dashboard-mood-history
if moodHistory.length < 1
span [no updates]
for mood of moodHistory for mood of moodHistory
li li
strong= mood.mood strong= mood.mood
| |
| #{mood.date} | #{mood.date}
h1(style="margin-top:1em;") Follows
table
tbody
for follow of followed
tr
td
a(href=`/users/${follow.uname}`)= follow.uname
td
form(action=`/users/${follow.uname}/follow`, method="post")
button Unfollow
block content block content
p p
@ -42,7 +54,7 @@ block content
+feed(recentUpdates) +feed(recentUpdates)
h1#invite-codes(style="margin-top:1em;") Invite codes h1#invite-codes(style="margin-top:1em;") Invite codes
p Invite your friends to the mipilin beta! You can create up to five invite codes every month, and they all expire within a week. p If you're a trusted user, you can invite your friends to the mipilin beta! You can create up to five invite codes every month, and they all expire within a week.
p p
| Your current invite codes ( | Your current invite codes (
strong= codesUsed strong= codesUsed
@ -61,13 +73,15 @@ block content
.subtle You have no currently active invite codes. .subtle You have no currently active invite codes.
br br
form(action="/codes/create", method="post") form(action="/codes/create", method="post")
button(type="submit", disabled=codesUsed>=5) Generate button(type="submit", disabled=codesUsed>=5 || !isTrusted) Generate
if codesUsed >= 5
p You've generated the maximum amount of codes this month.
if !isTrusted
p You need be manually marked as "trusted" to generate codes.
script(nonce=nonce). script(nonce=nonce).
function disable(form) { function disable(form) {
form.preventDefault();
const btn = form.querySelector("button"); const btn = form.querySelector("button");
btn.preventDefault(); btn.setAttribute("disabled", "");
btn.setAttribute("disabled", true);
setTimeout(() => { setTimeout(() => {
btn.removeAttribute("disabled"); btn.removeAttribute("disabled");

View file

@ -36,6 +36,10 @@ block content
| 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 | 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 a(href="https://www.imood.com/") imood
|, from which I borrowed many ideas and basically all of the moods. |, from which I borrowed many ideas and basically all of the moods.
p
| If you're wondering how to get started, look down. There is a lot of people! Also on the sidebar to your left is important information, including another list of users. Click on them! Follow them! Interact with them! Following someone will make their updates appear on your
a(href="/dashboard/#feed") dashboard's local feed
| . No one likes an empty local feed!!
p 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 | 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 a(href="https://sr.ht/~roxwize/mipilin") project page