mirror of
https://git.sr.ht/~roxwize/mipilin
synced 2025-01-31 02:53:36 +00:00
Rae from website
Signed-off-by: roxwize <rae@roxwize.xyz>
This commit is contained in:
parent
453a143bfa
commit
a3e6df6ce8
13 changed files with 691 additions and 35 deletions
2
TODO.md
2
TODO.md
|
@ -9,3 +9,5 @@
|
||||||
- [ ] Visibility indicator on journal entries
|
- [ ] Visibility indicator on journal entries
|
||||||
- [ ] Hide journal entries from feed that are hidden unless current user is a moderator
|
- [ ] Hide journal entries from feed that are hidden unless current user is a moderator
|
||||||
- [ ] Edit/delete journal entries?
|
- [ ] Edit/delete journal entries?
|
||||||
|
- [ ] Journal entry comments
|
||||||
|
- [ ] A Forum
|
||||||
|
|
12
db/schema.ts
12
db/schema.ts
|
@ -38,6 +38,18 @@ export const journalEntries = pgTable("journal_entries", {
|
||||||
date: timestamp().notNull()
|
date: timestamp().notNull()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const journalComments = pgTable("journal_comments", {
|
||||||
|
id: integer().primaryKey().generatedAlwaysAsIdentity(),
|
||||||
|
user: integer()
|
||||||
|
.references(() => users.id, { onDelete: "cascade" })
|
||||||
|
.notNull(),
|
||||||
|
entry: integer()
|
||||||
|
.references(() => journalEntries.id, { onDelete: "cascade" })
|
||||||
|
.notNull(),
|
||||||
|
content: varchar({ length: 512 }).notNull(),
|
||||||
|
date: timestamp().notNull()
|
||||||
|
});
|
||||||
|
|
||||||
export const profiles = pgTable("profiles", {
|
export const profiles = pgTable("profiles", {
|
||||||
user: integer()
|
user: integer()
|
||||||
.references(() => users.id, { onDelete: "cascade" })
|
.references(() => users.id, { onDelete: "cascade" })
|
||||||
|
|
19
drizzle/0003_closed_alex_power.sql
Normal file
19
drizzle/0003_closed_alex_power.sql
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS "journal_comments" (
|
||||||
|
"id" integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (sequence name "journal_comments_id_seq" INCREMENT BY 1 MINVALUE 1 MAXVALUE 2147483647 START WITH 1 CACHE 1),
|
||||||
|
"user" integer NOT NULL,
|
||||||
|
"entry" integer NOT NULL,
|
||||||
|
"content" varchar(512) NOT NULL,
|
||||||
|
"date" timestamp NOT NULL
|
||||||
|
);
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "journal_comments" ADD CONSTRAINT "journal_comments_user_users_id_fk" FOREIGN KEY ("user") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
||||||
|
--> statement-breakpoint
|
||||||
|
DO $$ BEGIN
|
||||||
|
ALTER TABLE "journal_comments" ADD CONSTRAINT "journal_comments_entry_journal_entries_id_fk" FOREIGN KEY ("entry") REFERENCES "public"."journal_entries"("id") ON DELETE cascade ON UPDATE no action;
|
||||||
|
EXCEPTION
|
||||||
|
WHEN duplicate_object THEN null;
|
||||||
|
END $$;
|
496
drizzle/meta/0003_snapshot.json
Normal file
496
drizzle/meta/0003_snapshot.json
Normal file
|
@ -0,0 +1,496 @@
|
||||||
|
{
|
||||||
|
"id": "0c1bcfed-899c-4ec9-9a7d-c987aa50dfb8",
|
||||||
|
"prevId": "f8e1fcdc-dfa2-41cd-886a-5f29a1c091df",
|
||||||
|
"version": "7",
|
||||||
|
"dialect": "postgresql",
|
||||||
|
"tables": {
|
||||||
|
"public.follows": {
|
||||||
|
"name": "follows",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"follower_id": {
|
||||||
|
"name": "follower_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"follows_user_id_users_id_fk": {
|
||||||
|
"name": "follows_user_id_users_id_fk",
|
||||||
|
"tableFrom": "follows",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"follows_follower_id_users_id_fk": {
|
||||||
|
"name": "follows_follower_id_users_id_fk",
|
||||||
|
"tableFrom": "follows",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"follower_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {
|
||||||
|
"follows_user_id_follower_id_pk": {
|
||||||
|
"name": "follows_user_id_follower_id_pk",
|
||||||
|
"columns": [
|
||||||
|
"user_id",
|
||||||
|
"follower_id"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.invite_codes": {
|
||||||
|
"name": "invite_codes",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"token": {
|
||||||
|
"name": "token",
|
||||||
|
"type": "varchar(22)",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"user_id": {
|
||||||
|
"name": "user_id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false
|
||||||
|
},
|
||||||
|
"granted": {
|
||||||
|
"name": "granted",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"expires": {
|
||||||
|
"name": "expires",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": "'1970-01-01 00:00:00.000'"
|
||||||
|
},
|
||||||
|
"confers": {
|
||||||
|
"name": "confers",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": false,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"invite_codes_user_id_users_id_fk": {
|
||||||
|
"name": "invite_codes_user_id_users_id_fk",
|
||||||
|
"tableFrom": "invite_codes",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user_id"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.journal_comments": {
|
||||||
|
"name": "journal_comments",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "journal_comments_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"name": "user",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"entry": {
|
||||||
|
"name": "entry",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"content": {
|
||||||
|
"name": "content",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"journal_comments_user_users_id_fk": {
|
||||||
|
"name": "journal_comments_user_users_id_fk",
|
||||||
|
"tableFrom": "journal_comments",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
},
|
||||||
|
"journal_comments_entry_journal_entries_id_fk": {
|
||||||
|
"name": "journal_comments_entry_journal_entries_id_fk",
|
||||||
|
"tableFrom": "journal_comments",
|
||||||
|
"tableTo": "journal_entries",
|
||||||
|
"columnsFrom": [
|
||||||
|
"entry"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.journal_entries": {
|
||||||
|
"name": "journal_entries",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "journal_entries_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"name": "user",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"mood-change": {
|
||||||
|
"name": "mood-change",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"name": "title",
|
||||||
|
"type": "varchar(64)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
|
"entry": {
|
||||||
|
"name": "entry",
|
||||||
|
"type": "varchar(4096)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
|
"visibility": {
|
||||||
|
"name": "visibility",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 1
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"journal_entries_user_users_id_fk": {
|
||||||
|
"name": "journal_entries_user_users_id_fk",
|
||||||
|
"tableFrom": "journal_entries",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.profiles": {
|
||||||
|
"name": "profiles",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"user": {
|
||||||
|
"name": "user",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"bio": {
|
||||||
|
"name": "bio",
|
||||||
|
"type": "varchar(1024)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
|
"website": {
|
||||||
|
"name": "website",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "''"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"profiles_user_users_id_fk": {
|
||||||
|
"name": "profiles_user_users_id_fk",
|
||||||
|
"tableFrom": "profiles",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.updates": {
|
||||||
|
"name": "updates",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "updates_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"name": "user",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"mood": {
|
||||||
|
"name": "mood",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 0
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"name": "description",
|
||||||
|
"type": "varchar(512)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": "''"
|
||||||
|
},
|
||||||
|
"date": {
|
||||||
|
"name": "date",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {
|
||||||
|
"updates_user_users_id_fk": {
|
||||||
|
"name": "updates_user_users_id_fk",
|
||||||
|
"tableFrom": "updates",
|
||||||
|
"tableTo": "users",
|
||||||
|
"columnsFrom": [
|
||||||
|
"user"
|
||||||
|
],
|
||||||
|
"columnsTo": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"onDelete": "cascade",
|
||||||
|
"onUpdate": "no action"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
},
|
||||||
|
"public.users": {
|
||||||
|
"name": "users",
|
||||||
|
"schema": "",
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": true,
|
||||||
|
"notNull": true,
|
||||||
|
"identity": {
|
||||||
|
"type": "always",
|
||||||
|
"name": "users_id_seq",
|
||||||
|
"schema": "public",
|
||||||
|
"increment": "1",
|
||||||
|
"startWith": "1",
|
||||||
|
"minValue": "1",
|
||||||
|
"maxValue": "2147483647",
|
||||||
|
"cache": "1",
|
||||||
|
"cycle": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"email": {
|
||||||
|
"name": "email",
|
||||||
|
"type": "varchar",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"name": "name",
|
||||||
|
"type": "varchar(26)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"pass": {
|
||||||
|
"name": "pass",
|
||||||
|
"type": "varchar(255)",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"registered": {
|
||||||
|
"name": "registered",
|
||||||
|
"type": "timestamp",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"name": "status",
|
||||||
|
"type": "integer",
|
||||||
|
"primaryKey": false,
|
||||||
|
"notNull": true,
|
||||||
|
"default": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"indexes": {},
|
||||||
|
"foreignKeys": {},
|
||||||
|
"compositePrimaryKeys": {},
|
||||||
|
"uniqueConstraints": {
|
||||||
|
"users_email_unique": {
|
||||||
|
"name": "users_email_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"email"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"users_name_unique": {
|
||||||
|
"name": "users_name_unique",
|
||||||
|
"nullsNotDistinct": false,
|
||||||
|
"columns": [
|
||||||
|
"name"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policies": {},
|
||||||
|
"checkConstraints": {},
|
||||||
|
"isRLSEnabled": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enums": {},
|
||||||
|
"schemas": {},
|
||||||
|
"sequences": {},
|
||||||
|
"roles": {},
|
||||||
|
"policies": {},
|
||||||
|
"views": {},
|
||||||
|
"_meta": {
|
||||||
|
"columns": {},
|
||||||
|
"schemas": {},
|
||||||
|
"tables": {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,13 @@
|
||||||
"when": 1735185254730,
|
"when": 1735185254730,
|
||||||
"tag": "0002_special_ben_parker",
|
"tag": "0002_special_ben_parker",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 3,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1737088748932,
|
||||||
|
"tag": "0003_closed_alex_power",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
4
main.ts
4
main.ts
|
@ -58,6 +58,10 @@ object-src 'none'; base-uri 'none';"
|
||||||
);
|
);
|
||||||
return next();
|
return next();
|
||||||
});
|
});
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
console.log(`${req.ip.padEnd(24)} ${req.method.padStart(8)} ${req.path}`);
|
||||||
|
return next();
|
||||||
|
})
|
||||||
|
|
||||||
await init(app, db);
|
await init(app, db);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,14 @@ import {
|
||||||
} from "../db/schema.js";
|
} from "../db/schema.js";
|
||||||
import { and, count, desc, eq, sql } from "drizzle-orm";
|
import { and, count, desc, eq, sql } from "drizzle-orm";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { getMoods, render, render404, UserStatus } from "./util.js";
|
import {
|
||||||
|
confirm,
|
||||||
|
getMoods,
|
||||||
|
journalMoodString,
|
||||||
|
render,
|
||||||
|
render404,
|
||||||
|
UserStatus
|
||||||
|
} from "./util.js";
|
||||||
|
|
||||||
export default async function (app: Express, db: NodePgDatabase) {
|
export default async function (app: Express, db: NodePgDatabase) {
|
||||||
const { moods, moodsSorted } = await getMoods();
|
const { moods, moodsSorted } = await getMoods();
|
||||||
|
@ -155,17 +162,28 @@ export default async function (app: Express, db: NodePgDatabase) {
|
||||||
render(db, "journal", "your journal", res, req);
|
render(db, "journal", "your journal", res, req);
|
||||||
});
|
});
|
||||||
app.get("/journal/:id", async (req, res) => {
|
app.get("/journal/:id", async (req, res) => {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
if (isNaN(id)) {
|
||||||
|
req.flash("error", "Invalid ID");
|
||||||
|
render404(db, res, req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const entry: {
|
const entry: {
|
||||||
uname: string,
|
id: number;
|
||||||
title: string,
|
uid: number;
|
||||||
content: string,
|
uname: string;
|
||||||
moodChange: number,
|
title: string;
|
||||||
moodString?: string,
|
content: string;
|
||||||
date: Date,
|
moodChange: number;
|
||||||
visibility: number
|
moodString?: string;
|
||||||
|
date: Date;
|
||||||
|
visibility: number;
|
||||||
} = (
|
} = (
|
||||||
await db
|
await db
|
||||||
.select({
|
.select({
|
||||||
|
id: journalEntries.id,
|
||||||
|
uid: users.id,
|
||||||
uname: users.name,
|
uname: users.name,
|
||||||
title: journalEntries.title,
|
title: journalEntries.title,
|
||||||
content: journalEntries.entry,
|
content: journalEntries.entry,
|
||||||
|
@ -174,45 +192,88 @@ export default async function (app: Express, db: NodePgDatabase) {
|
||||||
visibility: journalEntries.visibility
|
visibility: journalEntries.visibility
|
||||||
})
|
})
|
||||||
.from(journalEntries)
|
.from(journalEntries)
|
||||||
.where(eq(journalEntries.id, parseInt(req.params.id)))
|
.where(eq(journalEntries.id, id))
|
||||||
.leftJoin(users, eq(journalEntries.user, users.id))
|
.leftJoin(users, eq(journalEntries.user, users.id))
|
||||||
)[0];
|
)[0];
|
||||||
|
|
||||||
//? put into util function?
|
//? put into util function?
|
||||||
//? also GOD
|
//? also GOD
|
||||||
switch (entry.moodChange) {
|
entry.moodString = journalMoodString(entry.moodChange);
|
||||||
case -2:
|
|
||||||
entry.moodString = "much worse"
|
|
||||||
break;
|
|
||||||
case -1:
|
|
||||||
entry.moodString = "worse";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
case 0:
|
|
||||||
entry.moodString = "about the same";
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
entry.moodString = "better";
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
entry.moodString = "much better";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const isMod = req.session["status"] & UserStatus.MODERATOR;
|
||||||
if (
|
if (
|
||||||
!entry ||
|
!entry ||
|
||||||
(entry.visibility === 0 && entry.uname !== req.session["user"] && !(req.session["status"] & UserStatus.MODERATOR))
|
(entry.visibility === 0 &&
|
||||||
|
entry.uname !== req.session["user"] && !isMod)
|
||||||
) {
|
) {
|
||||||
render404(db, res, req);
|
render404(db, res, req);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// maybe turn ([x] !== req.session["user"] && !(req.session["status"] & UserStatus.MODERATOR)) into util function
|
||||||
|
entry.content =
|
||||||
|
entry.visibility === 2 &&
|
||||||
|
entry.uid !== req.session["uid"] &&
|
||||||
|
!(req.session["status"] & UserStatus.MODERATOR)
|
||||||
|
? "This journal entry's contents have been made private."
|
||||||
|
: entry.content;
|
||||||
|
|
||||||
const entryTimestamp = dayjs(entry.date).fromNow();
|
const entryTimestamp = dayjs(entry.date).fromNow();
|
||||||
|
const isSelf = entry.uid === req.session["uid"];
|
||||||
render(db, "journal_view", entry.title, res, req, {
|
render(db, "journal_view", entry.title, res, req, {
|
||||||
entry,
|
entry,
|
||||||
entryTimestamp
|
entryTimestamp,
|
||||||
|
isSelf,
|
||||||
|
isMod
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
app.post("/journal/:id/edit", async (req, res) => {
|
||||||
|
const id = parseInt(req.params.id);
|
||||||
|
if (isNaN(id)) {
|
||||||
|
req.flash("error", "Invalid ID");
|
||||||
|
render404(db, res, req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entry = (
|
||||||
|
await db
|
||||||
|
.select({
|
||||||
|
uid: journalEntries.user
|
||||||
|
})
|
||||||
|
.from(journalEntries)
|
||||||
|
.where(eq(journalEntries.id, id))
|
||||||
|
.limit(1)
|
||||||
|
)[0];
|
||||||
|
|
||||||
|
const isMod = req.session["status"] & UserStatus.MODERATOR;
|
||||||
|
if (
|
||||||
|
!entry ||
|
||||||
|
(entry?.uid !== req.session["uid"] &&
|
||||||
|
!isMod)
|
||||||
|
) {
|
||||||
|
render404(db, res, req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (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}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.body.action === "edit") {
|
||||||
|
// TODO!!
|
||||||
|
} else if (req.body.action === "delete") {
|
||||||
|
if (!req.body.confirm) {
|
||||||
|
confirm(db, res, req);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await db.delete(journalEntries).where(eq(journalEntries.id, id));
|
||||||
|
req.flash("success", "Journal entry deleted ;w;");
|
||||||
|
res.redirect("/");
|
||||||
|
} else {
|
||||||
|
render404(db, res, req);
|
||||||
|
}
|
||||||
|
});
|
||||||
app.post("/update/journal", async (req, res) => {
|
app.post("/update/journal", async (req, res) => {
|
||||||
if (!req.session["loggedIn"]) {
|
if (!req.session["loggedIn"]) {
|
||||||
res.redirect("/login");
|
res.redirect("/login");
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {
|
||||||
updates,
|
updates,
|
||||||
users
|
users
|
||||||
} from "../db/schema.js";
|
} from "../db/schema.js";
|
||||||
import { and, desc, eq } from "drizzle-orm";
|
import { and, desc, eq, ne } from "drizzle-orm";
|
||||||
import { getMoods, render, render404, UserStatus } from "./util.js";
|
import { getMoods, render, render404, UserStatus } from "./util.js";
|
||||||
import { PgColumn } from "drizzle-orm/pg-core";
|
import { PgColumn } from "drizzle-orm/pg-core";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
@ -79,10 +79,15 @@ export default async function (app: Express, db: NodePgDatabase) {
|
||||||
.select({
|
.select({
|
||||||
id: journalEntries.id,
|
id: journalEntries.id,
|
||||||
title: journalEntries.title,
|
title: journalEntries.title,
|
||||||
date: journalEntries.date
|
date: journalEntries.date,
|
||||||
|
visibility: journalEntries.visibility
|
||||||
})
|
})
|
||||||
.from(journalEntries)
|
.from(journalEntries)
|
||||||
.where(eq(journalEntries.user, user.id))
|
.where(
|
||||||
|
user.id === req.session["uid"] || req.session["status"] & UserStatus.MODERATOR
|
||||||
|
? eq(journalEntries.user, user.id)
|
||||||
|
: and(eq(journalEntries.user, user.id), ne(journalEntries.visibility, 0))
|
||||||
|
)
|
||||||
.orderBy(desc(journalEntries.date))
|
.orderBy(desc(journalEntries.date))
|
||||||
.limit(5)
|
.limit(5)
|
||||||
).map((e) => {
|
).map((e) => {
|
||||||
|
@ -90,6 +95,7 @@ export default async function (app: Express, db: NodePgDatabase) {
|
||||||
id: e.id,
|
id: e.id,
|
||||||
title: e.title,
|
title: e.title,
|
||||||
date: e.date,
|
date: e.date,
|
||||||
|
visibility: e.visibility,
|
||||||
relativeDate: now.to(e.date)
|
relativeDate: now.to(e.date)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -115,7 +121,7 @@ export default async function (app: Express, db: NodePgDatabase) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!isSelf) {
|
if (!isSelf && userMood) {
|
||||||
userMood.mood = moods[userMood.mood as number];
|
userMood.mood = moods[userMood.mood as number];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -115,3 +115,27 @@ export async function createInviteCode(
|
||||||
});
|
});
|
||||||
return token;
|
return token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function journalMoodString(mood: number) {
|
||||||
|
switch (mood) {
|
||||||
|
case -2:
|
||||||
|
return "much worse";
|
||||||
|
case -1:
|
||||||
|
return "worse";
|
||||||
|
default:
|
||||||
|
case 0:
|
||||||
|
return "about the same";
|
||||||
|
case 1:
|
||||||
|
return "better";
|
||||||
|
case 2:
|
||||||
|
return "much better";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function confirm(
|
||||||
|
db: NodePgDatabase,
|
||||||
|
res: Response,
|
||||||
|
req: Request
|
||||||
|
) {
|
||||||
|
render(db, "confirm", "Confirm action", res, req, { body: req.body, url: req.url });
|
||||||
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ input {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
button:focus,
|
button:focus,
|
||||||
textarea:focus,
|
textarea:focus,
|
||||||
|
|
9
views/confirm.pug
Normal file
9
views/confirm.pug
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
extends site.pug
|
||||||
|
|
||||||
|
block content
|
||||||
|
form(action=url, method="post")
|
||||||
|
for [k, v] of Object.entries(body)
|
||||||
|
input(type="text", name=k, value=v, hidden)
|
||||||
|
p Sure you want to do this??
|
||||||
|
button(name="confirm", value="y") Yes
|
||||||
|
button(onclick="history.back();") NO!!!!
|
|
@ -1,7 +1,14 @@
|
||||||
extends site.pug
|
extends site.pug
|
||||||
|
|
||||||
block content
|
block content
|
||||||
h1= entry.title
|
form(action=`/journal/${entry.id}/edit`, method="post")
|
||||||
|
h1(style="display:flex;align-items:center;gap:1em;")
|
||||||
|
| #{entry.title}
|
||||||
|
if isSelf || isMod
|
||||||
|
div(style="display:flex;")
|
||||||
|
if isSelf
|
||||||
|
button(name="action", value="edit") edit
|
||||||
|
button(name="action", value="delete") delete
|
||||||
span
|
span
|
||||||
| by
|
| by
|
||||||
a(href=`/users/${entry.uname}`)= entry.uname
|
a(href=`/users/${entry.uname}`)= entry.uname
|
||||||
|
|
|
@ -34,7 +34,15 @@ block content
|
||||||
for entry of userJournalEntries
|
for entry of userJournalEntries
|
||||||
li
|
li
|
||||||
a(href=`/journal/${entry.id}`)= entry.title
|
a(href=`/journal/${entry.id}`)= entry.title
|
||||||
span.subtle(title=entry.date.toLocaleString()) (#{entry.relativeDate})
|
span.subtle(title=entry.date.toLocaleString())
|
||||||
|
| (#{entry.relativeDate}
|
||||||
|
if entry.visibility !== 1
|
||||||
|
| ,
|
||||||
|
if entry.visibility === 0
|
||||||
|
| private
|
||||||
|
if entry.visibility === 2
|
||||||
|
| mood-only
|
||||||
|
| )
|
||||||
else
|
else
|
||||||
div [no entries]
|
div [no entries]
|
||||||
br
|
br
|
||||||
|
|
Loading…
Reference in a new issue