feat : comment feature
CI / Verify (push) Successful in 2m30s
CI / Build immutable images and deploy (push) Successful in 3m9s

This commit is contained in:
2026-10-08 01:40:08 +07:00 Unverified
parent 8c8815cda5
commit 5812d99715
65 changed files with 27013 additions and 10 deletions
+46
View File
@@ -158,12 +158,58 @@ with the latest version reported by Lunaris.
- Enable the Discord Message Content intent and grant the bot View Channel and
Send Messages in the configured channels.
### Guide comments
Comment tables live in the dedicated PostgreSQL `comments` schema: `comment`,
`thread`, `revision`, `attachment`, `revision_attachment`, `reaction`,
`push_subscription`, and `read_state`.
Character guides have a Comment section at `/{slug}/comment`. Stygian comments
appear at the bottom of `/stygian?schedule={id}` and remain accessible at
`/stygian/comment?schedule={id}`; both share the selected schedule's discussion. Readers can
browse anonymously; posting, replies, likes, and dislikes require an account.
Replies expand inline with connector lines; replies to replies identify their recipient.
The Thai comment UI supports image paste (Ctrl+V) with previews and loading skeletons.
Comment boards and the admin inbox receive Redis-backed SSE change events;
reads after each event recheck authorization. Reconnecting refreshes missed updates.
Signed-in authors can enable browser notifications after posting. New replies notify
only the direct parent author, excluding self-replies. Notification clicks open the
root thread, load the relevant reply page, and highlight/focus the exact reply.
This uses the existing `WEB_PUSH_PUBLIC_KEY`, `WEB_PUSH_PRIVATE_KEY`, and
`WEB_PUSH_SUBJECT` configuration, with subscriptions in `comments.push_subscription`.
Likes, dislikes, and admin hearts update optimistically and save in the background;
a failed save rolls the reaction back and shows an error.
Each comment or reply supports 4,000 characters and up to five JPEG, PNG, or
WebP images, each no larger than 10 MiB. Image bytes are uploaded to the
S3 bucket through Bun’s `S3Client`; PostgreSQL stores only object keys, metadata,
and revision references. Browsers load images directly from `S3_PUBLIC_URL`. Videos are unsupported. Authors can edit
or delete their comments, and the “แก้ไขแล้ว” link exposes previous text and images.
Deletion permanently removes the comment and every descendant reply, including their
revision history, attachment metadata, and reactions. Their S3 images are also deleted.
`/admin/comments` provides the cross-guide inbox, guide/schedule filtering,
visible/hidden and unanswered filters, replies, reactions, admin hearts, and
hide/restore moderation. Conversation buttons appear only on comments with replies;
the dialog shows a thread skeleton while loading. Admin-only unread badges update
via SSE, cap at `99+`, and link to the guide inbox. Opening the inbox marks its
selected guide (or all guides) read for that admin. Hiding a root also hides its replies. Moderation actions
are recorded in the activity log. Comment reads and legacy attachment redirects
are not cached; guide content caches are independent of discussions.
The comment migration must run before deploying these pages. For PostgreSQL
integration verification, point `DATABASE_INTEGRATION_URL` at a migrated test or
development database and run `bun run test tests/comments.integration.test.ts`.
The suite creates isolated fixtures and removes them afterward; image storage is
mocked while decoding and database operations are exercised.
### Media visibility
| Upload path | Visibility |
| --- | --- |
| Presigned staged upload | Private in S3. `/media/[id]` allows public access only while a published guide references the object; otherwise administrator authorization is required. |
| Direct guide image upload | Public CDN object, including uploads made for drafts. |
| Comment attachment | Stored in Bun S3 and loaded directly from `S3_PUBLIC_URL`. Hiding removes image links from public comment responses; existing URLs remain accessible until the S3 object is deleted. `/api/comments/images/[id]` only redirects old links after a visibility check. |
| Commission attachment | Served from `S3_PUBLIC_URL`; anyone with the URL can view it. |
Keep sensitive material out of the direct upload path and commission attachments.