chore: add cloud deployment templates

This commit is contained in:
Codex
2026-07-01 20:09:02 +08:00
parent fa2c39119b
commit cc9b552684
13 changed files with 632 additions and 0 deletions

View File

@@ -104,6 +104,7 @@ supabase/seed.sql 最小租户 seed
scripts/import-pocketbase/ PocketBase schema/数据导入器和校验器
scripts/smoke-seed.js 本地集成测试 seed 数据
scripts/api-integration-test.js
scripts/deploy/ 云服务器部署模板Nginx、systemd、环境变量示例、发布脚本
docs/refactor/ 重构架构、进度、治理文档
docker-compose.api.yml API 容器化运行配置
```
@@ -180,6 +181,7 @@ apps/taro/deploy/h5-platform-admin.runtime-config.example.json
```text
docs/refactor/taro-h5-deployment.md
scripts/deploy/README.md
```
学生端当前页面:
@@ -431,6 +433,8 @@ npm run test:rls
接下来在新云服务器上建议按下面顺序推进。不要跳过证据门禁;它是后续给合作商交付 SaaS 时的安全底线。
当前 `tjszsb.com` 已建议按 6 个生产入口使用:`api.tjszsb.com` 反代业务 API`app.tjszsb.com` 承载学生 H5`admin.tjszsb.com` 承载租户后台,`console.tjszsb.com` 承载平台后台,`supabase.tjszsb.com` 反代 Supabase gateway/Auth/Storage/PostgREST`studio.tjszsb.com` 仅限固定 IP/VPN 访问 Supabase Studio。可提交到仓库的部署模板在 `scripts/deploy/`;真实服务器文件建议放在 `/opt/tiku-saas/repo``/srv/tiku-saas/www/*``/etc/tiku-saas/*.env`。Gitea token、数据库密码、支付私钥、短信密钥、对象存储密钥都只允许放服务器本地不允许写入 Git、前端运行时配置或部署脚本。
1. 准备服务器基础环境:安装 Docker、Node.js 20+、Supabase CLI、Nginx/Caddy、进程管理或容器编排工具拉取本仓库 `main`,以 Gitea 最新提交为准。
2. 启动 Supabase/PostgreSQL执行全部 migrations 和最小 seed确认 `DATABASE_URL` 指向云端数据库。
3.`docs/refactor/postgresql-4c16g-tuning.md` 应用 4 核 16G `shared-host` 起步参数,启用 `pg_stat_statements`,重启 PostgreSQL 后跑 `PG_TUNING_PROFILE=shared-host npm run perf:postgres:evidence -- --strict --json`

View File

@@ -17,6 +17,8 @@
- `packages/config``packages/db``packages/domain`:新系统共享基础包。
- `scripts/import-pocketbase`PocketBase schema/数据导入工具。
- `docker-compose.api.yml``docker-compose.api.benchmark.yml``apps/api/Dockerfile`:本地 Docker API 和受限资源压测入口。
- `scripts/deploy/README.md`:云服务器部署 runbook覆盖 `tjszsb.com` 六域名规划、服务器目录、Gitea 安全部署、Nginx、systemd 和更新脚本。
- `scripts/deploy/bin/deploy.sh`:服务器端发布脚本模板,负责拉取 Gitea、构建 API/worker/Taro H5、发布静态文件和重启服务真实密钥只从 `/etc/tiku-saas/*.env` 读取。
- `docs/refactor/architecture.md`:新重构目录边界和工程规范。
- `docs/refactor/ai-development-guardrails.md`:后续 AI/开发者必须遵守的 Supabase-first 架构和安全守则。
- `docs/refactor/content-import-contract.md`:题目、单词、知识手册导入契约,明确后端校验、旧格式转换和前端职责。

153
scripts/deploy/README.md Normal file
View File

@@ -0,0 +1,153 @@
# tiku-supabase 云服务器部署说明
本文档用于把当前仓库部署到云服务器,并和已经解析好的域名打通。仓库内只保存安全模板,真实密钥、数据库密码、支付密钥、短信密钥、对象存储密钥和 Gitea 部署凭证必须放在服务器 `/etc/tiku-saas/` 下,不能提交到 Git。
## 域名规划
建议先按下面 6 个域名落地:
| 域名 | 用途 | 服务器转发 |
| --- | --- | --- |
| `api.tjszsb.com` | 自研业务 APITaro/H5/小程序统一调用 | `127.0.0.1:8787` |
| `app.tjszsb.com` | 学生 H5 题库端 | `/srv/tiku-saas/www/student` |
| `admin.tjszsb.com` | 租户后台 H5 | `/srv/tiku-saas/www/tenant-admin` |
| `console.tjszsb.com` | SaaS 平台后台 H5 | `/srv/tiku-saas/www/platform-admin` |
| `supabase.tjszsb.com` | Supabase API gateway/Auth/Storage/PostgREST | Supabase gateway通常是 `127.0.0.1:8000` |
| `studio.tjszsb.com` | Supabase Studio 运维后台 | 仅允许固定 IP/VPN/内网访问 |
`studio.tjszsb.com` 不建议裸露给公网。若必须临时开放,至少要加 Nginx IP 白名单、强密码、服务器防火墙和访问日志审计。
## 服务器目录
推荐使用固定目录,方便后续脚本和 AI 协作不漂移:
```text
/opt/tiku-saas/repo Git 工作副本
/opt/tiku-saas/bin 服务器本地执行脚本
/srv/tiku-saas/www/student 学生端 H5 静态文件
/srv/tiku-saas/www/tenant-admin 租户后台 H5 静态文件
/srv/tiku-saas/www/platform-admin 平台后台 H5 静态文件
/srv/tiku-saas/data 运行期数据
/srv/tiku-saas/backups 数据库和对象存储备份
/etc/tiku-saas/deploy.env 部署脚本配置,含 Gitea 只读部署凭证
/etc/tiku-saas/api.env API 生产环境变量
/etc/tiku-saas/worker.env worker 生产环境变量
/etc/tiku-saas/runtime-config/ 三套 H5 公开运行时配置
```
建议创建独立低权限用户:
```bash
sudo useradd --system --create-home --shell /bin/bash deploy
sudo mkdir -p /opt/tiku-saas/bin /srv/tiku-saas/www/student /srv/tiku-saas/www/tenant-admin /srv/tiku-saas/www/platform-admin /srv/tiku-saas/data /srv/tiku-saas/backups /etc/tiku-saas/runtime-config
sudo chown -R deploy:deploy /opt/tiku-saas /srv/tiku-saas
sudo chmod 750 /etc/tiku-saas
```
## 首次安装
1. 安装基础组件Docker、Docker Compose、Node.js 20+、Nginx、Certbot、Git、rsync、flock。
2. 按 Supabase 官方 self-hosting Docker 文档部署 Supabase。生产必须启用 HTTPS 反向代理Supabase 官方也要求生产自托管部署使用 HTTPS。
3. 把本目录模板复制到服务器:
```bash
sudo mkdir -p /opt/tiku-saas/bin /etc/tiku-saas/runtime-config
sudo cp scripts/deploy/bin/deploy.sh /opt/tiku-saas/bin/deploy.sh
sudo cp scripts/deploy/env/deploy.env.example /etc/tiku-saas/deploy.env
sudo cp scripts/deploy/env/api.env.example /etc/tiku-saas/api.env
sudo cp scripts/deploy/env/worker.env.example /etc/tiku-saas/worker.env
sudo cp scripts/deploy/runtime-config/h5-student.runtime-config.example.json /etc/tiku-saas/runtime-config/h5-student.runtime-config.json
sudo cp scripts/deploy/runtime-config/h5-tenant-admin.runtime-config.example.json /etc/tiku-saas/runtime-config/h5-tenant-admin.runtime-config.json
sudo cp scripts/deploy/runtime-config/h5-platform-admin.runtime-config.example.json /etc/tiku-saas/runtime-config/h5-platform-admin.runtime-config.json
sudo chmod 700 /opt/tiku-saas/bin/deploy.sh
sudo chmod 600 /etc/tiku-saas/*.env /etc/tiku-saas/runtime-config/*.json
```
4. 编辑 `/etc/tiku-saas/*.env``/etc/tiku-saas/runtime-config/*.json`,填入真实生产配置。
5. 安装 systemd 服务:
```bash
sudo cp scripts/deploy/systemd/tiku-api.service /etc/systemd/system/tiku-api.service
sudo cp scripts/deploy/systemd/tiku-worker.service /etc/systemd/system/tiku-worker.service
sudo systemctl daemon-reload
sudo systemctl enable tiku-api tiku-worker
```
6. 安装 Nginx 配置:
```bash
sudo cp scripts/deploy/nginx/tjszsb.com.conf.example /etc/nginx/sites-available/tiku-saas.conf
sudo ln -s /etc/nginx/sites-available/tiku-saas.conf /etc/nginx/sites-enabled/tiku-saas.conf
sudo nginx -t
sudo systemctl reload nginx
```
7. 申请 HTTPS 证书:
```bash
sudo certbot --nginx -d api.tjszsb.com -d app.tjszsb.com -d admin.tjszsb.com -d console.tjszsb.com -d supabase.tjszsb.com -d studio.tjszsb.com
```
## Gitea 凭证
优先推荐 SSH deploy key。若暂时使用 Gitea token必须新建一个只读部署 token并写入 `/etc/tiku-saas/deploy.env`,不要把 token 写进脚本、Git remote、命令历史或 README。
已经在聊天、工单、截图里出现过的 token 都应当视为暴露,正式上云前请立即吊销并重新生成。
`deploy.sh` 会通过临时 `GIT_ASKPASS``git clone/fetch` 提供账号和 token避免 token 出现在 `git remote -v` 里。
## 更新发布
服务器上执行:
```bash
sudo -u deploy /opt/tiku-saas/bin/deploy.sh
```
脚本会执行:
1. 获取 `main` 最新代码。
2. `npm ci` 安装锁定依赖。
3. 运行仓库安全扫描和生产上线门禁测试。
4. 构建 API、worker、学生 H5、租户后台 H5、平台后台 H5。
5.`rsync --delete` 发布静态产物。
6. 复制服务器本地 `runtime-config.json` 到对应 Web 根目录。
7. 重启 `tiku-api``tiku-worker`
8. 输出当前发布的 Git commit。
## 上线前检查
每次正式放量前至少执行:
```bash
npm run security:repo
node scripts/production-launch-gate-test.js
node scripts/launch-persona-smoke-test.js
npm run readiness:production
```
接入真实生产配置后,还要在服务器上补跑:
```bash
npm run readiness:production:db
npm run smoke:auth:remote
npm run perf:api:local
```
压测必须在目标云服务器、目标数据库参数、目标对象存储和目标 Nginx 配置下重新计算,本地 Windows 压测数据只能作为开发参考。
## 关键安全要求
- 前端只保存 `supabasePublishableKey`,严禁出现 service role、数据库密码、短信密钥、支付私钥。
- 自研业务 API 默认只接受 Supabase JWT 或迁移期受控 app session不允许前端携带平台管理密钥。
- API、worker、Supabase、Nginx 日志要开启轮转,避免磁盘被日志打满。
- 数据库至少每日备份,正式放量前要完成一次恢复演练。
- 支付回调、短信回调、对象存储回调必须使用 HTTPS 域名,并在 API 层校验签名和租户归属。
- Supabase Studio 必须限制访问来源。
## 参考
- Supabase self-hosting Docker: https://supabase.com/docs/guides/self-hosting/docker
- Supabase reverse proxy and HTTPS: https://supabase.com/docs/guides/self-hosting/self-hosted-proxy-https
- Supabase Auth self-hosting config: https://supabase.com/docs/guides/self-hosting/auth/config
- Supabase self-hosted S3 storage: https://supabase.com/docs/guides/self-hosting/self-hosted-s3

View File

@@ -0,0 +1,140 @@
#!/usr/bin/env bash
set -Eeuo pipefail
CONFIG_FILE="${CONFIG_FILE:-/etc/tiku-saas/deploy.env}"
if [[ ! -r "$CONFIG_FILE" ]]; then
echo "Missing deploy config: $CONFIG_FILE" >&2
exit 1
fi
# shellcheck disable=SC1090
source "$CONFIG_FILE"
: "${GIT_REPO:?GIT_REPO is required}"
: "${GIT_BRANCH:=main}"
: "${APP_ROOT:=/opt/tiku-saas}"
: "${REPO_DIR:=$APP_ROOT/repo}"
: "${WWW_ROOT:=/srv/tiku-saas/www}"
: "${RUNTIME_CONFIG_DIR:=/etc/tiku-saas/runtime-config}"
: "${RUN_SECURITY_CHECKS:=true}"
: "${RUN_LAUNCH_GATE:=true}"
: "${RESTART_SERVICES:=true}"
LOCK_FILE="${LOCK_FILE:-/tmp/tiku-saas-deploy.lock}"
mkdir -p "$APP_ROOT" "$WWW_ROOT/student" "$WWW_ROOT/tenant-admin" "$WWW_ROOT/platform-admin"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
echo "Another deployment is already running." >&2
exit 1
fi
log() {
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
cleanup() {
if [[ -n "${ASKPASS_FILE:-}" && -f "$ASKPASS_FILE" ]]; then
rm -f "$ASKPASS_FILE"
fi
}
trap cleanup EXIT
prepare_git_auth() {
if [[ -n "${GIT_SSH_COMMAND:-}" ]]; then
export GIT_SSH_COMMAND
return
fi
if [[ -z "${GIT_USERNAME:-}" || -z "${GITEA_TOKEN:-}" ]]; then
return
fi
ASKPASS_FILE="$(mktemp)"
chmod 700 "$ASKPASS_FILE"
cat > "$ASKPASS_FILE" <<'ASKPASS'
#!/usr/bin/env bash
case "$1" in
*Username*) printf '%s\n' "$GIT_USERNAME" ;;
*Password*) printf '%s\n' "$GITEA_TOKEN" ;;
*) printf '\n' ;;
esac
ASKPASS
export GIT_ASKPASS="$ASKPASS_FILE"
export GIT_TERMINAL_PROMPT=0
}
prepare_git_auth
if [[ ! -d "$REPO_DIR/.git" ]]; then
log "Cloning repository..."
git clone --branch "$GIT_BRANCH" "$GIT_REPO" "$REPO_DIR"
fi
cd "$REPO_DIR"
log "Fetching $GIT_BRANCH..."
git fetch origin "$GIT_BRANCH" --prune
git checkout "$GIT_BRANCH"
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "Repository has local changes. Refusing to deploy until the server checkout is clean." >&2
exit 1
fi
git merge --ff-only "origin/$GIT_BRANCH"
CURRENT_SHA="$(git rev-parse --short=12 HEAD)"
log "Deploying commit $CURRENT_SHA"
log "Installing dependencies with npm ci..."
npm ci
if [[ "$RUN_SECURITY_CHECKS" == "true" ]]; then
log "Running repository security scan..."
npm run security:repo
fi
if [[ "$RUN_LAUNCH_GATE" == "true" ]]; then
log "Running production launch gate test..."
node scripts/production-launch-gate-test.js
fi
log "Building API and worker..."
npm run build:api
npm run build:worker
log "Building H5 portals..."
npm run build:taro:h5:student
npm run build:taro:h5:tenant
npm run build:taro:h5:platform
log "Publishing H5 static assets..."
rsync -a --delete apps/taro/dist/h5-student/ "$WWW_ROOT/student/"
rsync -a --delete apps/taro/dist/h5-tenant-admin/ "$WWW_ROOT/tenant-admin/"
rsync -a --delete apps/taro/dist/h5-platform-admin/ "$WWW_ROOT/platform-admin/"
install_runtime_config() {
local source_file="$1"
local target_dir="$2"
if [[ ! -r "$source_file" ]]; then
echo "Missing runtime config: $source_file" >&2
exit 1
fi
install -m 0644 "$source_file" "$target_dir/runtime-config.json"
}
log "Installing H5 runtime config files..."
install_runtime_config "$RUNTIME_CONFIG_DIR/h5-student.runtime-config.json" "$WWW_ROOT/student"
install_runtime_config "$RUNTIME_CONFIG_DIR/h5-tenant-admin.runtime-config.json" "$WWW_ROOT/tenant-admin"
install_runtime_config "$RUNTIME_CONFIG_DIR/h5-platform-admin.runtime-config.json" "$WWW_ROOT/platform-admin"
if [[ "$RESTART_SERVICES" == "true" ]]; then
log "Restarting systemd services..."
systemctl restart tiku-api.service
systemctl restart tiku-worker.service
systemctl --no-pager --full status tiku-api.service tiku-worker.service >/dev/null
fi
log "Deployment finished: $CURRENT_SHA"

57
scripts/deploy/env/api.env.example vendored Normal file
View File

@@ -0,0 +1,57 @@
# Copy to /etc/tiku-saas/api.env and chmod 600.
# This file is read by systemd. Do not commit the real file.
NODE_ENV=production
PORT=8787
DATABASE_URL=postgresql://tiku_app:replace-with-password@127.0.0.1:5432/postgres
DEFAULT_TENANT_SLUG=master
CORS_ORIGIN=https://app.tjszsb.com,https://admin.tjszsb.com,https://console.tjszsb.com
ALLOW_LEGACY_AUTH_HEADERS=false
ALLOW_PLATFORM_ADMIN_KEY=false
SUPABASE_URL=https://supabase.tjszsb.com
SUPABASE_JWT_ISSUER=https://supabase.tjszsb.com/auth/v1
AUTH_JWT_JWKS_URL=https://supabase.tjszsb.com/auth/v1/.well-known/jwks.json
AUTH_SESSION_SECRET=replace-with-strong-random-session-secret
AUTH_CODE_PEPPER=replace-with-strong-random-code-pepper
AUTH_SMS_PROVIDER=aliyun
ALIYUN_SMS_ACCESS_KEY_ID=replace-with-access-key-id
ALIYUN_SMS_ACCESS_KEY_SECRET=replace-with-access-key-secret
ALIYUN_SMS_SIGN_NAME=replace-with-sms-sign
ALIYUN_SMS_TEMPLATE_LOGIN=replace-with-template-code
WECHAT_MINIAPP_APP_ID=replace-with-miniapp-app-id
WECHAT_MINIAPP_APP_SECRET=replace-with-miniapp-app-secret
WECHAT_WEB_APP_ID=replace-with-web-app-id
WECHAT_WEB_APP_SECRET=replace-with-web-app-secret
QQ_CONNECT_APP_ID=replace-with-qq-app-id
QQ_CONNECT_APP_KEY=replace-with-qq-app-key
PAYMENT_WECHAT_ENABLED=true
WECHAT_PAY_MCH_ID=replace-with-merchant-id
WECHAT_PAY_APP_ID=replace-with-pay-app-id
WECHAT_PAY_API_V3_KEY=replace-with-api-v3-key
WECHAT_PAY_PRIVATE_KEY=replace-with-private-key-path-or-kms-id
WECHAT_PAY_NOTIFY_URL=https://api.tjszsb.com/api/commerce/webhooks/wechat-pay
PAYMENT_ALIPAY_ENABLED=true
ALIPAY_APP_ID=replace-with-alipay-app-id
ALIPAY_APP_PRIVATE_KEY=replace-with-private-key-path-or-kms-id
ALIPAY_PUBLIC_KEY=replace-with-alipay-public-key
ALIPAY_NOTIFY_URL=https://api.tjszsb.com/api/commerce/webhooks/alipay
STORAGE_DEFAULT_PROVIDER=aliyun_oss
ALIYUN_OSS_REGION=oss-cn-beijing
ALIYUN_OSS_ENDPOINT=https://oss-cn-beijing.aliyuncs.com
ALIYUN_OSS_BUCKET=replace-with-bucket
ALIYUN_OSS_ACCESS_KEY_ID=replace-with-access-key-id
ALIYUN_OSS_ACCESS_KEY_SECRET=replace-with-access-key-secret
ASSET_SIGNING_SECRET=replace-with-strong-random-asset-secret
WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http
WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false
ASSET_SECURITY_SCAN_ENDPOINT=https://replace-with-security-scanner.example.com/scan
ASSET_SECURITY_SCAN_TOKEN=replace-with-scanner-token

18
scripts/deploy/env/deploy.env.example vendored Normal file
View File

@@ -0,0 +1,18 @@
# Copy to /etc/tiku-saas/deploy.env and chmod 600.
# Keep real credentials on the server only.
GIT_REPO=https://git.gongxue100.com/chenhaogxjy/tiku-supabase.git
GIT_BRANCH=main
# Prefer SSH deploy key. If using HTTPS token, create a rotated read-only deploy token.
GIT_USERNAME=replace-with-readonly-deploy-user
GITEA_TOKEN=replace-with-rotated-readonly-token
APP_ROOT=/opt/tiku-saas
REPO_DIR=/opt/tiku-saas/repo
WWW_ROOT=/srv/tiku-saas/www
RUNTIME_CONFIG_DIR=/etc/tiku-saas/runtime-config
RUN_SECURITY_CHECKS=true
RUN_LAUNCH_GATE=true
RESTART_SERVICES=true

32
scripts/deploy/env/worker.env.example vendored Normal file
View File

@@ -0,0 +1,32 @@
# Copy to /etc/tiku-saas/worker.env and chmod 600.
# This file is read by systemd. Do not commit the real file.
NODE_ENV=production
DATABASE_URL=postgresql://tiku_app:replace-with-password@127.0.0.1:5432/postgres
SUPABASE_URL=https://supabase.tjszsb.com
STORAGE_DEFAULT_PROVIDER=aliyun_oss
ALIYUN_OSS_REGION=oss-cn-beijing
ALIYUN_OSS_ENDPOINT=https://oss-cn-beijing.aliyuncs.com
ALIYUN_OSS_BUCKET=replace-with-bucket
ALIYUN_OSS_ACCESS_KEY_ID=replace-with-access-key-id
ALIYUN_OSS_ACCESS_KEY_SECRET=replace-with-access-key-secret
ASSET_SIGNING_SECRET=replace-with-strong-random-asset-secret
WORKER_ASSET_SECURITY_SCANNER=metadata_rules,http
WORKER_ASSET_SECURITY_SCAN_FAIL_OPEN=false
ASSET_SECURITY_SCAN_ENDPOINT=https://replace-with-security-scanner.example.com/scan
ASSET_SECURITY_SCAN_TOKEN=replace-with-scanner-token
WECHAT_PAY_MCH_ID=replace-with-merchant-id
WECHAT_PAY_APP_ID=replace-with-pay-app-id
WECHAT_PAY_API_V3_KEY=replace-with-api-v3-key
WECHAT_PAY_PRIVATE_KEY=replace-with-private-key-path-or-kms-id
ALIPAY_APP_ID=replace-with-alipay-app-id
ALIPAY_APP_PRIVATE_KEY=replace-with-private-key-path-or-kms-id
ALIPAY_PUBLIC_KEY=replace-with-alipay-public-key
CRM_WEBHOOK_TIMEOUT_MS=5000
WORKER_POLL_INTERVAL_MS=5000

View File

@@ -0,0 +1,149 @@
# Copy to /etc/nginx/sites-available/tiku-saas.conf, review, then symlink into sites-enabled.
# Certbot can inject ssl_certificate directives after the HTTP blocks are reachable.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name app.tjszsb.com;
root /srv/tiku-saas/www/student;
index index.html;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
location = /runtime-config.json {
add_header Cache-Control "no-store" always;
try_files $uri =404;
}
location ~* \.(?:js|css|png|jpg|jpeg|gif|webp|svg|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
}
}
server {
listen 80;
server_name admin.tjszsb.com;
root /srv/tiku-saas/www/tenant-admin;
index index.html;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
location = /runtime-config.json {
add_header Cache-Control "no-store" always;
try_files $uri =404;
}
location ~* \.(?:js|css|png|jpg|jpeg|gif|webp|svg|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
}
}
server {
listen 80;
server_name console.tjszsb.com;
root /srv/tiku-saas/www/platform-admin;
index index.html;
add_header X-Frame-Options SAMEORIGIN always;
add_header X-Content-Type-Options nosniff always;
add_header Referrer-Policy strict-origin-when-cross-origin always;
location = /runtime-config.json {
add_header Cache-Control "no-store" always;
try_files $uri =404;
}
location ~* \.(?:js|css|png|jpg|jpeg|gif|webp|svg|ico|woff2?)$ {
expires 30d;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
location / {
try_files $uri $uri/ /index.html;
}
}
server {
listen 80;
server_name api.tjszsb.com;
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:8787;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
}
server {
listen 80;
server_name supabase.tjszsb.com;
client_max_body_size 100m;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
server {
listen 80;
server_name studio.tjszsb.com;
# Replace these placeholders with office/VPN/server bastion IPs before enabling.
allow 127.0.0.1;
# allow 203.0.113.10;
deny all;
location / {
# Supabase self-hosted Studio port depends on your compose file. Common values are 3000 or 54323.
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}

View File

@@ -0,0 +1,7 @@
{
"portal": "platform-admin",
"apiBaseUrl": "https://api.tjszsb.com",
"supabaseUrl": "https://supabase.tjszsb.com",
"supabasePublishableKey": "replace-with-supabase-publishable-key",
"tenantCode": "master"
}

View File

@@ -0,0 +1,7 @@
{
"portal": "student",
"apiBaseUrl": "https://api.tjszsb.com",
"supabaseUrl": "https://supabase.tjszsb.com",
"supabasePublishableKey": "replace-with-supabase-publishable-key",
"tenantCode": "master"
}

View File

@@ -0,0 +1,7 @@
{
"portal": "tenant-admin",
"apiBaseUrl": "https://api.tjszsb.com",
"supabaseUrl": "https://supabase.tjszsb.com",
"supabasePublishableKey": "replace-with-supabase-publishable-key",
"tenantCode": "master"
}

View File

@@ -0,0 +1,28 @@
[Unit]
Description=tiku-supabase API
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/opt/tiku-saas/repo
EnvironmentFile=/etc/tiku-saas/api.env
ExecStart=/usr/bin/node /opt/tiku-saas/repo/apps/api/dist/apps/api/src/server.js
Restart=always
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=30
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/srv/tiku-saas /opt/tiku-saas/repo
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true
[Install]
WantedBy=multi-user.target

View File

@@ -0,0 +1,28 @@
[Unit]
Description=tiku-supabase worker
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/opt/tiku-saas/repo
EnvironmentFile=/etc/tiku-saas/worker.env
ExecStart=/usr/bin/node /opt/tiku-saas/repo/apps/worker/dist/apps/worker/src/index.js --loop
Restart=always
RestartSec=5
KillSignal=SIGTERM
TimeoutStopSec=60
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=full
ProtectHome=true
ReadWritePaths=/srv/tiku-saas /opt/tiku-saas/repo
CapabilityBoundingSet=
AmbientCapabilities=
LockPersonality=true
[Install]
WantedBy=multi-user.target