chore : update readme [no ci]
This commit is contained in:
@@ -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 |
|
||||
| --- | --- |
|
||||
|
||||
Reference in New Issue
Block a user