PersonalWebApplication/docs/superpowers/plans/2026-06-18-deploy.md

319 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# InternetProject Deployment Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Deploy the React + Rust full-stack app to `root@8.130.143.54`, accessible at `http://8.130.143.54:8548`.
**Architecture:** Rust binary (`RustServer`) serves both the REST API and React static files from a single process. React is built locally, Rust is cross-compiled in WSL, both artifacts are uploaded via scp and started with nohup.
**Tech Stack:** React 19 + Vite 8 (frontend), Rust + Axum 0.8 (backend), Ubuntu 22.04 (server), WSL2 mirrored network (build env)
## Global Constraints
- Server: `root@8.130.143.54`, Ubuntu 22.04, x86_64
- Deploy path: `/opt/internet-project/` on server
- Static files served from: `/opt/internet-project/static/`
- Port: `8548` (via `PORT=8548` env var at runtime)
- WSL mirrored network + `autoProxy=true` — no manual proxy config needed
- `main.rs` already updated to read `PORT` env var (default `8080`)
- Do NOT install any toolchain on the remote server
---
### Task 1: Build React Frontend
**Files:**
- Build: `Client/``Client/dist/`
**Interfaces:**
- Produces: `Client/dist/index.html` + `Client/dist/assets/*` (consumed by Task 3)
- [ ] **Step 1: Install dependencies (if not already installed)**
Run in PowerShell from `F:\Code\InternetProject\Client`:
```powershell
npm install
```
Expected: `node_modules/` populated, no errors.
- [ ] **Step 2: Build the frontend**
```powershell
npm run build
```
Expected output ends with something like:
```
dist/index.html x.xx kB
dist/assets/index-[hash].js xxx.xx kB
✓ built in x.xxs
```
- [ ] **Step 3: Verify dist output**
```powershell
Get-ChildItem Client\dist
```
Expected: `index.html`, `assets/`, `favicon.svg`, `icons.svg` present.
- [ ] **Step 4: Commit the main.rs port change**
```powershell
git add Server/src/main.rs
git commit -m "feat: read PORT from env var, default 8080"
```
---
### Task 2: Build Rust Binary in WSL
**Files:**
- Build: `Server/``Server/target/release/RustServer`
**Interfaces:**
- Produces: `/mnt/f/Code/InternetProject/Server/target/release/RustServer` — Linux x86_64 ELF binary (consumed by Task 3)
- [ ] **Step 1: Open WSL and navigate to project**
In a WSL terminal:
```bash
cd /mnt/f/Code/InternetProject/Server
```
- [ ] **Step 2: Check Rust toolchain is installed**
```bash
rustc --version
```
If not installed:
```bash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source "$HOME/.cargo/env"
```
Expected: `rustc 1.xx.x (...)` printed.
- [ ] **Step 3: Build release binary**
```bash
cargo build --release
```
First build takes 25 minutes (downloads + compiles dependencies). Expected final line:
```
Finished `release` profile [optimized] target(s) in Xs
```
- [ ] **Step 4: Verify binary exists and is Linux ELF**
```bash
file target/release/RustServer
```
Expected:
```
target/release/RustServer: ELF 64-bit LSB pie executable, x86-64, ...
```
---
### Task 3: Upload Artifacts to Server
**Files:**
- Remote create: `/opt/internet-project/` (directory)
- Remote create: `/opt/internet-project/RustServer` (binary)
- Remote create: `/opt/internet-project/static/` (frontend files)
**Interfaces:**
- Consumes: `Server/target/release/RustServer` (from Task 2), `Client/dist/*` (from Task 1)
- Produces: server-side deploy directory ready to run (consumed by Task 4)
- [ ] **Step 1: Create deploy directory on server**
Run in WSL:
```bash
ssh root@8.130.143.54 "mkdir -p /opt/internet-project/static"
```
Expected: no output, exit 0.
- [ ] **Step 2: Upload the Rust binary**
```bash
scp /mnt/f/Code/InternetProject/Server/target/release/RustServer \
root@8.130.143.54:/opt/internet-project/RustServer
```
Expected: progress bar, then returns to prompt.
- [ ] **Step 3: Upload frontend static files**
```bash
scp -r /mnt/f/Code/InternetProject/Client/dist/* \
root@8.130.143.54:/opt/internet-project/static/
```
Expected: multiple files transferred (index.html, assets/, etc.).
- [ ] **Step 4: Make binary executable**
```bash
ssh root@8.130.143.54 "chmod +x /opt/internet-project/RustServer"
```
- [ ] **Step 5: Verify remote layout**
```bash
ssh root@8.130.143.54 "ls -lh /opt/internet-project/ && ls /opt/internet-project/static/"
```
Expected:
```
total XX
-rwxr-xr-x 1 root root XXM ... RustServer
drwxr-xr-x 2 root root ... static/
assets favicon.svg icons.svg index.html
```
---
### Task 4: Start the Server and Verify
**Files:**
- Remote: `/opt/internet-project/server.log` (created on start)
**Interfaces:**
- Consumes: deploy directory from Task 3
- Produces: running service at `http://8.130.143.54:8548`
- [ ] **Step 1: Start server with nohup**
```bash
ssh root@8.130.143.54 "cd /opt/internet-project && nohup PORT=8548 ./RustServer > server.log 2>&1 &"
```
Expected: no output (background process started).
- [ ] **Step 2: Check server started successfully**
```bash
ssh root@8.130.143.54 "sleep 1 && cat /opt/internet-project/server.log"
```
Expected:
```
backend listening on http://0.0.0.0:8548
```
- [ ] **Step 3: Verify API endpoint responds**
```bash
curl http://8.130.143.54:8548/api/client/health
```
Expected: HTTP 200 with a JSON response (or empty 200).
- [ ] **Step 4: Verify frontend loads**
Open `http://8.130.143.54:8548` in a browser. Expected: React app renders correctly (login page / blog page visible).
- [ ] **Step 5: Verify API stats endpoint**
```bash
curl http://8.130.143.54:8548/api/web/stats
```
Expected: JSON with system metrics (CPU, memory, etc.).
---
## Operations Reference
**Stop server:**
```bash
ssh root@8.130.143.54 "pkill RustServer"
```
**View live logs:**
```bash
ssh root@8.130.143.54 "tail -f /opt/internet-project/server.log"
```
**Redeploy after code changes:**
Re-run Tasks 14. For frontend-only changes, skip Task 2.
**Frontend-only redeploy (re-verified 2026-06-24 — 照片墙 `#photo` 上线即走此路径):**
The active WSL checkout is at `/home/cc/InternetProject` (not the `/mnt/f/...`
path in the original tasks above). Since the Rust process serves `static/` from
disk, a frontend-only change needs **no server restart** — just rebuild and
replace the static files:
```bash
cd /home/cc/InternetProject/Client
npm run build
# clear stale hashed assets so old bundles don't accumulate
ssh root@8.130.143.54 "rm -rf /opt/internet-project/static/assets && mkdir -p /opt/internet-project/static"
scp -r dist/* root@8.130.143.54:/opt/internet-project/static/
# verify live
curl -s http://8.130.143.54:8548/ | grep -o '<title>[^<]*</title>'
curl -s -o /dev/null -w '%{http_code}\n' http://8.130.143.54:8548/api/client/health
```
**Restart the Rust binary (backend changes):**
A helper exists on the server: `/opt/internet-project/restart_rust.sh`. The
previous binary is kept as `RustServer.bak` for rollback.
**照片墙服务器存图2026-06-24后端改动**
照片墙从纯前端 localStorage 改为按用户存服务器(`#photo` 入口,详见
[photo-wall.md](../../photo-wall.md))。这引入了后端改动,需**全量重部署**(非
frontend-only重编 Rust → 传二进制 → `restart_rust.sh` 重启。新增两个运行时配置:
- `JWT_SECRET`(写在 `/opt/internet-project/weather.env`)—— 登录 token 改为 HS256
JWT 的签名密钥,未设则用内置开发密钥并告警。**token 格式变了,上线后用户需重登一次。**
- `PHOTO_DIR`(可选,默认 `photo-wall`,即 `/opt/internet-project/photo-wall/`)——
图片按用户存到其下子目录,首次上传自动创建。
**特殊文件夹视频媒体(规划,后端改动 + ffmpeg 运行时依赖):**
特殊文件夹支持视频上传后,服务器需要安装 `ffmpeg` 并确保 Rust 服务进程能在 `PATH` 中调用。
这次改动会新增 `/api/web/folder/{name}/media*` 接口和 multipart 上传,需**全量重部署**
重编 Rust → 传二进制和前端 `dist/*``restart_rust.sh` 重启。
服务器安装检查:
```bash
ssh root@8.130.143.54 "ffmpeg -version | head -1"
```
如未安装,按服务器系统包管理器安装,例如 Debian/Ubuntu
```bash
ssh root@8.130.143.54 "apt-get update && apt-get install -y ffmpeg"
```
如果通过 Nginx 的 80 端口访问线上站点,还要把反代请求体上限放大到与 Rust
multipart 路由一致,否则 30MB 级视频会先被 Nginx 默认上限拦截并返回 413
```nginx
server {
listen 80;
server_name _;
client_max_body_size 700m;
location / {
proxy_pass http://127.0.0.1:8548;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
修改后验证并重载:
```bash
ssh root@8.130.143.54 "nginx -t && systemctl reload nginx"
```
转码目标固定为 720p 档、24fps、视频约 900kbps、音频 96kbps、MP4 faststart
用于适配 3M 带宽播放。不提供显式视频下载入口;`controlsList="nodownload"` 只是 UI 降噪,
不是防下载安全边界。
**写博客2026-06-27后端改动**
个人博客(登录后默认页,`#post/<id>` 文章路由,详见 [blog.md](../../blog.md))引入了后端
改动(新增 `posts.rs` + 5 个 `/api/web/posts*` 接口、CORS 放行 PUT需**全量重部署**
(重编 Rust → 传二进制 → `restart_rust.sh` 重启),非 frontend-only。新增一个可选运行时配置
- `BLOG_DIR`(可选,默认 `blog`,即 `/opt/internet-project/blog/`)—— 文章按用户存到其下
子目录,每篇一个 `<hex>.json`,首次写文章自动创建。
- 复用现有 `weather.env``DATABASE_URL`(登录校验)与 `JWT_SECRET`token 签名),
无需新增密钥。
> Startpage backgrounds: large PNG/JPG source images are pre-compressed to WebP
> (quality ~82) before building — keeps each background under ~600 KB for the
> 3 Mbps server link. Two sets live under `Client/src/assets/startpage/`:
> `landscape/` and `portrait/` (same filenames `No.1~5.webp` + `Night.webp`).
> As of 2026-06-23 both sets are fully populated; `sharp` is the converter (no
> system `cwebp`/ImageMagick in this WSL — install it on the fly if needed).