chore : update readme [no ci]

This commit is contained in:
2026-10-04 15:34:01 +07:00 Unverified
parent 643e77a88f
commit 250ef24962
+140 -81
View File
@@ -1,30 +1,109 @@
# Buzz Guide
Buzz Guide is a Thai-first guide CMS for turning structured character, build,
team, rotation, and comparison data into responsive game-guide pages.
A Thai-first Genshin Impact guide CMS for publishing character builds, teams,
rotations, and comparisons from structured data.
Production target: `https://guide.sudloh.com`
**Live site:** [guide.sudloh.com](https://guide.sudloh.com)
This project is an independent community tool for Genshin Impact players. It is
not affiliated with or endorsed by HoYoverse.
Buzz Guide is an independent community project and is not affiliated with or
endorsed by HoYoverse. The repository and deployment resources retain the
`buzz-sheet` name.
## Included capabilities
## Features
- Searchable character guide directory.
- Structured editors for overview, weapons, artifacts, constellations, teams,
and custom sections with autosave and conflict recovery.
- Better Auth email/password login, registration email OTP, profile email changes,
and administrator-managed accounts.
- S3-compatible media uploads with reference-aware same-origin delivery for
staged private objects.
- PostgreSQL transactions and a retryable outbox.
- Shared Redis cache invalidation and privacy-safe Server-Sent Events across
replicas.
- Discord-triggered Lunaris catalog updates with exact-version synchronization.
- **Public guides:** a searchable character directory and responsive guide pages.
- **Visual editing:** structured editors for overviews, weapons, artifacts,
constellations, teams, and custom sections, with autosave and conflict recovery.
- **Accounts:** email/password authentication through Better Auth, registration
email OTP, profile email changes, and administrator-managed accounts.
- **Media:** S3-compatible uploads and publication-aware delivery for staged files.
- **Catalog updates:** Discord-triggered synchronization with Lunaris.
- **Commissions:** PromptPay checkout, Slip2Go verification, ticket attachments,
and opt-in browser push notifications.
- **Shared state:** PostgreSQL transactions, a retryable outbox, Redis caching,
and Server-Sent Events across application replicas.
The five reference workbooks are represented only by sanitized structural and
The five reference workbooks are included only as sanitized structural and
formula fixtures. Their guide text and media are not imported or published.
## Contents
- [Local development](#local-development)
- [Commands](#commands)
- [Routes](#routes)
- [How updates and uploads work](#how-updates-and-uploads-work)
- [Verification](#verification)
- [Containers](#containers)
- [Kubernetes](#kubernetes)
- [Acknowledgements](#acknowledgements)
## Local development
### Requirements
- Bun **1.3.14**
- PostgreSQL
- Redis
- S3-compatible object storage
### Setup
```bash
git clone https://git.astrxl.dev/astral/buzz-sheet.git
cd buzz-sheet
bun install --frozen-lockfile
cp .env.example .env
```
Fill in `.env` using the placeholders in `.env.example`. Keep credentials out of
Git; `.env` is ignored by the repository.
| Setting | Purpose |
| --- | --- |
| `BETTER_AUTH_URL` | Must exactly match the application's origin. |
| `BASE_URL` | Public origin used for SEO metadata, the sitemap, and robots.txt. |
| Database, Redis, and S3 settings | Connection details for the required services. |
Apply the committed database migrations, then start the development server:
```bash
bun run db:migrate
bun run dev
```
Visitors can register at `/auth/register`. For background processing, run the
outbox worker in a separate terminal. Run the Discord worker when using catalog
updates; its configuration is described below.
```bash
bun run worker:outbox
```
```bash
bun run worker:discord
```
Email, commission payments, and push notifications need their corresponding
service credentials. See [External prerequisites](#external-prerequisites) for
the production configuration details.
## Commands
| Command | Purpose |
| --- | --- |
| `bun run dev` | Start the Next.js development server |
| `bun run test` | Run the Vitest unit and integration suite |
| `bun run test:coverage` | Produce V8 coverage output |
| `bun run typecheck` | Run TypeScript without emitting files |
| `bun run lint` | Run ESLint |
| `bun run security:audit` | Check dependency advisories and time-limited exceptions |
| `bun run build` | Compile the production application |
| `bun run db:generate` | Generate a Drizzle migration from schema changes |
| `bun run db:migrate` | Apply committed Drizzle migrations |
| `bun run import:guides` | Preview the one-time Google Sheets guide migration (`-- --apply` to write) |
| `bun run worker:discord` | Listen for Lunaris update announcements |
| `bun run worker:outbox` | Process cache and event outbox records |
## Routes
| Route | Purpose |
@@ -47,79 +126,45 @@ The Discord check requires a heartbeat from the connected catalog worker within
A page is public only when both its page and character are visible. A visible
character without a visible page returns 404.
## Local development
## How updates and uploads work
Requirements:
### Editing and live updates
- Bun 1.3.14
- PostgreSQL
- Redis
- S3-compatible object storage
Edits carry an `expectedVersion`. Accepted changes save guide content, media
reference counts, and outbox records in a single PostgreSQL transaction.
Install dependencies and create your local environment file:
The outbox worker invalidates the affected Redis cache tags before publishing
an event containing only `{ type, id, version }`. Clients then fetch the current
state. SSE carries change notifications, never guide content; PostgreSQL remains
the source of truth.
```bash
bun install --frozen-lockfile
cp .env.example .env
```
Public guides use a shared Redis-backed Next.js cache. Admin routes stay dynamic.
SSE streams send a heartbeat every 90 seconds and close after 30 minutes so
clients reconnect. Response headers disable Traefik buffering.
`.env.example` contains placeholders only. Configure `.env` yourself; it is
ignored by Git and must never be committed. `BETTER_AUTH_URL` must exactly
match the application origin. Set `BASE_URL` to the public site origin for SEO
metadata, sitemap, and robots.txt. Visitors can create public accounts at `/auth/register`.
### Discord catalog synchronization
Prepare the database and start the application:
The catalog worker watches the private channel set by `DISCORD_CHANNEL_ID`.
Every new message, including one sent by you, triggers a check and synchronization
with the latest version reported by Lunaris.
```bash
bun run db:migrate
bun run dev
```
- Startup connects the listener without replaying history or starting a sync.
- Results go to `DISCORD_LOG_CHANNEL_ID`; start notifications link to the
triggering message.
- Send `!lunaris stop` in the watched channel to cancel the current fetch and
clear queued fetches. A later message starts another check.
- Enable the Discord Message Content intent and grant the bot View Channel and
Send Messages in the configured channels.
## Commands
### Media visibility
| Command | Purpose |
| Upload path | Visibility |
| --- | --- |
| `bun run dev` | Start the Next.js development server |
| `bun run test` | Run the Vitest unit and integration suite |
| `bun run test:coverage` | Produce V8 coverage output |
| `bun run typecheck` | Run TypeScript without emitting files |
| `bun run lint` | Run ESLint |
| `bun run security:audit` | Check dependency advisories and time-limited exceptions |
| `bun run build` | Compile the production application |
| `bun run db:generate` | Generate a Drizzle migration from schema changes |
| `bun run db:migrate` | Apply committed Drizzle migrations |
| `bun run import:guides` | Preview the one-time Google Sheets guide migration (`-- --apply` to write) |
| `bun run worker:discord` | Listen for Lunaris update announcements |
| `bun run worker:outbox` | Process cache and event outbox records |
| 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. |
| Commission attachment | Served from `S3_PUBLIC_URL`; anyone with the URL can view it. |
## Runtime design
Accepted edits use an `expectedVersion` and commit guide content, media reference
counts, and outbox records in one PostgreSQL transaction. The outbox worker then invalidates affected Redis tags
before publishing an opaque `{ type, id, version }` event. Connected clients
refetch authoritative state; SSE never contains page content and is not the
source of truth.
The Discord catalog worker watches the private channel configured by
`DISCORD_CHANNEL_ID`. Every new message in that channel, including messages you send yourself,
triggers a check and synchronization of the latest version reported by Lunaris. Startup
only connects the listener; it does not replay channel history or start a sync.
Worker and sync results are sent to `DISCORD_LOG_CHANNEL_ID`; start notifications
include a clickable link to the message that triggered the check. Send
`!lunaris stop` in the configured channel to cancel the active fetch and clear
queued fetches; a later message starts a new check. Enable the Discord
Message Content intent and grant the bot View Channel and Send Messages in the
configured channels.
Public guides use a shared Redis-backed Next.js cache. Admin routes remain
dynamic. SSE streams send a 90-second heartbeat and close after 30 minutes so
clients reconnect. Traefik buffering is disabled by the response headers.
Presigned staged uploads remain private in S3. `/media/[id]` serves a staged
object publicly only while a published guide references it; otherwise
administrator authorization is required. The direct upload path writes public
CDN objects for guide images, so draft uploads on that path must not contain
sensitive material.
Keep sensitive material out of the direct upload path and commission attachments.
## Verification
@@ -195,6 +240,8 @@ not create in-cluster data stores or credentials.
### External prerequisites
#### Application secrets
Before the first rollout, a cluster administrator must provision a
`buzz-sheet-env` Secret in the `buzz-sheet` namespace containing:
@@ -227,6 +274,8 @@ DISCORD_CHANNEL_ID
across replicas and rolling deployments. Do not place secret values in the
ConfigMap or commit them to this repository.
#### Email verification
Before deploying required email verification, add `sudloh.com` to Resend and
publish the DNS records shown in its domain setup. Verify the domain, create a
sending-only API key restricted to it, and set `RESEND_API_KEY` in the external
@@ -235,6 +284,8 @@ Secret. Account verification and password reset emails are sent from
sessions continue until they expire or the user signs out, after which an
unverified address must be verified before signing in again.
#### Commission payments and attachments
The commission page accepts PromptPay mobile, national ID, or e-wallet identifiers.
For `COMMISSION_PROMPTPAY_TYPE=mobile`, use a real 10-digit Thai mobile number
registered with PromptPay for receiving transfers in `COMMISSION_PROMPTPAY_VALUE`.
@@ -250,6 +301,8 @@ view it. Each checkout accepts payment for one hour after creation. The outbox
worker removes expired unpaid checkouts; paid tickets remain available. Apply
the commission database migration before enabling checkout.
#### Browser push notifications
Commission message notifications require a stable VAPID key pair and subject
(`bunx web-push generate-vapid-keys --json`). Set the three `WEB_PUSH_*` values
in the web deployment secret and apply the push-subscription migration before
@@ -257,16 +310,22 @@ enabling notifications. Users opt in from a prompt when entering the commission
arrive after tabs close; on iPhone and iPad, the site must be added to the Home
Screen before the browser offers push permission.
#### Ingress and client IP handling
Traefik must overwrite `X-Real-Ip` for every request, and the web pods must be
reachable only through Traefik before enabling the configured client IP based
rate limits. The NetworkPolicy assumes Traefik runs in `kube-system` with the
`app.kubernetes.io/name=traefik` pod label; verify those labels in the target
cluster before applying it.
#### Deployment notifications
Every page also subscribes to `/api/active`. When a serving pod reports a newer
deployment ID than the browser's current build, a persistent Thai notification
offers a full reload into the new release.
#### Initial bootstrap
The base manifests contain deliberate `replace-me` image and deployment values.
For initial bootstrap, the platform operator applies `k8s/base` through an
environment overlay that supplies an existing immutable image revision. The
@@ -292,7 +351,7 @@ registry.neko-piranha.ts.net/astral/buzz-sheet:<git-sha>
registry.neko-piranha.ts.net/astral/buzz-sheet:migrate-<git-sha>
```
Configure these Gitea secrets later:
Configure the following Gitea secret before enabling deployment:
| Secret | Purpose |
| --- | --- |