docs: update deployment env and runtime docs for convex qwen minio
This commit is contained in:
39
.env.example
39
.env.example
@@ -1,10 +1,20 @@
|
||||
# Runtime
|
||||
NODE_ENV=production
|
||||
PORT=3000
|
||||
STATE_FILE_PATH=/data/state.json
|
||||
LOG_LEVEL=info
|
||||
APP_BASE_URL=https://xartaudio.example.com
|
||||
|
||||
# Better Auth
|
||||
BETTER_AUTH_SECRET=replace-me
|
||||
BETTER_AUTH_BASE_PATH=/api/auth
|
||||
BETTER_AUTH_DEV_PASSWORD=replace-me
|
||||
|
||||
# Convex
|
||||
CONVEX_DEPLOYMENT_URL=https://your-deployment.convex.cloud
|
||||
CONVEX_AUTH_TOKEN=
|
||||
CONVEX_STATE_QUERY=state:getLatestSnapshot
|
||||
CONVEX_STATE_MUTATION=state:saveSnapshot
|
||||
|
||||
# Webhook secrets
|
||||
X_WEBHOOK_SECRET=replace-me
|
||||
POLAR_WEBHOOK_SECRET=replace-me
|
||||
@@ -18,19 +28,22 @@ POLAR_ACCESS_TOKEN=replace-me
|
||||
POLAR_SERVER=production
|
||||
POLAR_PRODUCT_IDS=prod_123
|
||||
|
||||
# TTS (OpenAI-compatible)
|
||||
TTS_API_KEY=replace-me
|
||||
TTS_BASE_URL=
|
||||
TTS_MODEL=gpt-4o-mini-tts
|
||||
TTS_VOICE=alloy
|
||||
# Qwen3 TTS
|
||||
QWEN_TTS_API_KEY=replace-me
|
||||
QWEN_TTS_BASE_URL=https://dashscope-intl.aliyuncs.com/compatible-mode/v1
|
||||
QWEN_TTS_MODEL=qwen-tts-latest
|
||||
QWEN_TTS_VOICE=Cherry
|
||||
QWEN_TTS_FORMAT=mp3
|
||||
|
||||
# S3-compatible object storage
|
||||
S3_BUCKET=replace-me
|
||||
S3_REGION=us-east-1
|
||||
S3_ENDPOINT=
|
||||
S3_ACCESS_KEY_ID=replace-me
|
||||
S3_SECRET_ACCESS_KEY=replace-me
|
||||
S3_SIGNED_URL_TTL_SEC=3600
|
||||
# MinIO object storage
|
||||
MINIO_ENDPOINT=minio.example.com
|
||||
MINIO_PORT=443
|
||||
MINIO_USE_SSL=true
|
||||
MINIO_BUCKET=replace-me
|
||||
MINIO_REGION=us-east-1
|
||||
MINIO_ACCESS_KEY=replace-me
|
||||
MINIO_SECRET_KEY=replace-me
|
||||
MINIO_SIGNED_URL_TTL_SEC=3600
|
||||
|
||||
# Credit policy
|
||||
BASE_CREDITS=1
|
||||
|
||||
@@ -4,7 +4,7 @@ WORKDIR /app
|
||||
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
ENV STATE_FILE_PATH=/data/state.json
|
||||
ENV CONVEX_DEPLOYMENT_URL=
|
||||
|
||||
COPY package.json bun.lock ./
|
||||
RUN bun install --frozen-lockfile
|
||||
@@ -15,7 +15,6 @@ COPY spec.md ./spec.md
|
||||
RUN bun run build:css
|
||||
|
||||
EXPOSE 3000
|
||||
VOLUME ["/data"]
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --retries=3 CMD bun -e "fetch('http://127.0.0.1:'+process.env.PORT+'/health').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"
|
||||
|
||||
|
||||
65
README.md
65
README.md
@@ -330,10 +330,10 @@ This repository now contains a deployable production-style app (single container
|
||||
5. Real integration adapters implemented:
|
||||
- X API (`twitter-api-v2`)
|
||||
- Polar SDK checkout/webhook handling (`@polar-sh/sdk`)
|
||||
- TTS (`openai`)
|
||||
- Object storage + signed URLs (`@aws-sdk/client-s3`, `@aws-sdk/s3-request-presigner`)
|
||||
- TTS (`Qwen3 TTS`, OpenAI-compatible endpoint via `fetch`)
|
||||
- Object storage + signed URLs (`minio`)
|
||||
6. Persistent state across restarts:
|
||||
- all wallet/job/asset/access state is snapshotted and stored to `STATE_FILE_PATH`
|
||||
- all wallet/job/asset/access state is snapshotted through Convex query/mutation functions
|
||||
7. Abuse protection:
|
||||
- fixed-window rate limiting for webhook, auth, and action routes
|
||||
8. PWA support:
|
||||
@@ -344,9 +344,9 @@ This repository now contains a deployable production-style app (single container
|
||||
- `bun run lint`
|
||||
|
||||
### Authentication model
|
||||
1. Browser flow uses secure-ish HTTP-only cookie session (`xartaudio_user`) via `/auth/dev-login`.
|
||||
2. API calls also support `x-user-id` header for scripted usage/testing.
|
||||
3. This auth layer is intentionally replaceable with X OAuth in production rollout.
|
||||
1. Browser flow is powered by Better Auth under `/api/auth/*`.
|
||||
2. `/auth/dev-login` bootstraps a Better Auth session for local/dev testing.
|
||||
3. API calls also support `x-user-id` header for scripted usage/testing.
|
||||
|
||||
### Runtime endpoints
|
||||
1. Public:
|
||||
@@ -380,10 +380,17 @@ Use `.env.example` as the source of truth.
|
||||
|
||||
1. Runtime:
|
||||
- `PORT`
|
||||
- `STATE_FILE_PATH`
|
||||
- `LOG_LEVEL`
|
||||
- `APP_BASE_URL`
|
||||
2. Secrets:
|
||||
2. Auth + state:
|
||||
- `BETTER_AUTH_SECRET`
|
||||
- `BETTER_AUTH_BASE_PATH`
|
||||
- `BETTER_AUTH_DEV_PASSWORD`
|
||||
- `CONVEX_DEPLOYMENT_URL`
|
||||
- `CONVEX_AUTH_TOKEN`
|
||||
- `CONVEX_STATE_QUERY`
|
||||
- `CONVEX_STATE_MUTATION`
|
||||
3. Secrets:
|
||||
- `X_WEBHOOK_SECRET`
|
||||
- `POLAR_WEBHOOK_SECRET`
|
||||
- `X_BEARER_TOKEN`
|
||||
@@ -391,23 +398,26 @@ Use `.env.example` as the source of truth.
|
||||
- `POLAR_ACCESS_TOKEN`
|
||||
- `POLAR_SERVER`
|
||||
- `POLAR_PRODUCT_IDS`
|
||||
- `TTS_API_KEY`
|
||||
- `TTS_BASE_URL`
|
||||
- `TTS_MODEL`
|
||||
- `TTS_VOICE`
|
||||
- `S3_BUCKET`
|
||||
- `S3_REGION`
|
||||
- `S3_ENDPOINT`
|
||||
- `S3_ACCESS_KEY_ID`
|
||||
- `S3_SECRET_ACCESS_KEY`
|
||||
- `S3_SIGNED_URL_TTL_SEC`
|
||||
3. Credit model:
|
||||
- `QWEN_TTS_API_KEY`
|
||||
- `QWEN_TTS_BASE_URL`
|
||||
- `QWEN_TTS_MODEL`
|
||||
- `QWEN_TTS_VOICE`
|
||||
- `QWEN_TTS_FORMAT`
|
||||
- `MINIO_ENDPOINT`
|
||||
- `MINIO_PORT`
|
||||
- `MINIO_USE_SSL`
|
||||
- `MINIO_BUCKET`
|
||||
- `MINIO_REGION`
|
||||
- `MINIO_ACCESS_KEY`
|
||||
- `MINIO_SECRET_KEY`
|
||||
- `MINIO_SIGNED_URL_TTL_SEC`
|
||||
4. Credit model:
|
||||
- `BASE_CREDITS`
|
||||
- `INCLUDED_CHARS`
|
||||
- `STEP_CHARS`
|
||||
- `STEP_CREDITS`
|
||||
- `MAX_CHARS_PER_ARTICLE`
|
||||
4. Rate limits:
|
||||
5. Rate limits:
|
||||
- `WEBHOOK_RPM`
|
||||
- `AUTH_RPM`
|
||||
- `ACTION_RPM`
|
||||
@@ -416,17 +426,16 @@ Use `.env.example` as the source of truth.
|
||||
|
||||
1. Create a new service from this repository and select `Dockerfile` build mode.
|
||||
2. Set container port to `3000`.
|
||||
3. Add a persistent volume mounted at `/data`.
|
||||
4. Set `STATE_FILE_PATH=/data/state.json`.
|
||||
5. Configure all secrets and policy env vars from `.env.example`.
|
||||
6. Expose HTTPS URL and point providers to:
|
||||
3. Configure all secrets and policy env vars from `.env.example`.
|
||||
4. Ensure `CONVEX_DEPLOYMENT_URL` is reachable from the container network.
|
||||
5. Expose HTTPS URL and point providers to:
|
||||
- `https://<your-domain>/api/webhooks/x`
|
||||
- `https://<your-domain>/api/webhooks/polar`
|
||||
7. Verify deployment health with `GET /health`.
|
||||
6. Verify deployment health with `GET /health`.
|
||||
|
||||
## Production Checklist
|
||||
|
||||
1. Replace dev-login cookie auth with X OAuth before public launch.
|
||||
2. Populate integration keys in Coolify environment for X, Polar, TTS, and S3.
|
||||
3. Replace local state file with managed database for multi-replica scaling.
|
||||
1. Replace `/auth/dev-login` with direct Better Auth UI/OAuth sign-in for public launch.
|
||||
2. Populate integration keys in Coolify environment for X, Polar, Qwen3 TTS, MinIO, and Convex.
|
||||
3. Implement Convex functions named by `CONVEX_STATE_QUERY` and `CONVEX_STATE_MUTATION`.
|
||||
4. Add tracing and external alerting.
|
||||
|
||||
@@ -8,7 +8,7 @@ test("Dockerfile contains production container essentials", () => {
|
||||
const dockerfile = fs.readFileSync("Dockerfile", "utf8");
|
||||
assert.match(dockerfile, /FROM oven\/bun:1\.3\.5-alpine/);
|
||||
assert.match(dockerfile, /EXPOSE 3000/);
|
||||
assert.match(dockerfile, /STATE_FILE_PATH=\/data\/state\.json/);
|
||||
assert.match(dockerfile, /CONVEX_DEPLOYMENT_URL=/);
|
||||
assert.match(dockerfile, /bun install --frozen-lockfile/);
|
||||
assert.match(dockerfile, /bun run build:css/);
|
||||
assert.match(dockerfile, /HEALTHCHECK/);
|
||||
@@ -25,8 +25,10 @@ test("env example includes required webhook and credit settings", () => {
|
||||
assert.match(envFile, /POLAR_WEBHOOK_SECRET=/);
|
||||
assert.match(envFile, /POLAR_ACCESS_TOKEN=/);
|
||||
assert.match(envFile, /POLAR_PRODUCT_IDS=/);
|
||||
assert.match(envFile, /TTS_API_KEY=/);
|
||||
assert.match(envFile, /S3_BUCKET=/);
|
||||
assert.match(envFile, /QWEN_TTS_API_KEY=/);
|
||||
assert.match(envFile, /MINIO_ENDPOINT=/);
|
||||
assert.match(envFile, /CONVEX_DEPLOYMENT_URL=/);
|
||||
assert.match(envFile, /BETTER_AUTH_SECRET=/);
|
||||
assert.match(envFile, /INCLUDED_CHARS=/);
|
||||
assert.match(envFile, /WEBHOOK_RPM=/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user