Add deployment guide and pm2 process file
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+193
@@ -0,0 +1,193 @@
|
||||
# Deploying Two Keys
|
||||
|
||||
How to run the site under pm2 on an app server and publish it at
|
||||
`https://two-keys.crosscode.co.za` through frp.
|
||||
|
||||
```
|
||||
browser ──https──▶ central server (frps, TLS) ──frp tunnel──▶ app server (frpc) ──▶ 127.0.0.1:3010 (pm2: two-keys)
|
||||
```
|
||||
|
||||
## One port, one domain
|
||||
|
||||
Two Keys is a **single Node process**. The same port serves the web pages, the `/api` routes and
|
||||
the websocket (`/socket.io`), and the browser connects back to whatever address it loaded the page
|
||||
from. So only `two-keys.crosscode.co.za` is needed.
|
||||
|
||||
`two-keys-server.crosscode.co.za` is **not required**. There is no separate backend port to expose,
|
||||
and the client has no setting for a different server address, so pointing a second domain at it
|
||||
would change nothing.
|
||||
|
||||
The examples below use these values. Change them to suit:
|
||||
|
||||
| Value | Used here |
|
||||
|-------|-----------|
|
||||
| Install folder | `/var/www/two-keys` |
|
||||
| Local port | `3010` (set in `ecosystem.config.cjs`) |
|
||||
| pm2 app name | `two-keys` |
|
||||
| Public domain | `two-keys.crosscode.co.za` |
|
||||
|
||||
## 1. Prerequisites (app server)
|
||||
|
||||
- Node.js 20 or newer (`node -v`)
|
||||
- git
|
||||
- pm2: `sudo npm install -g pm2`
|
||||
- frpc, already connected to your central frps
|
||||
|
||||
## 2. Create the folder and give your user ownership
|
||||
|
||||
Run these as your normal login user, not as root. `$USER` is the user you are logged in as.
|
||||
|
||||
```bash
|
||||
sudo mkdir -p /var/www/two-keys
|
||||
sudo chown -R "$USER":"$USER" /var/www/two-keys
|
||||
```
|
||||
|
||||
Everything after this runs **without** `sudo`. If you ever run `git`, `npm` or `pm2` in this folder
|
||||
with `sudo`, files end up owned by root and later builds fail with `EACCES`. Fix it by running the
|
||||
`chown` line again.
|
||||
|
||||
The app writes its room snapshots to `server/data/rooms.json`, so the pm2 user must own the folder.
|
||||
|
||||
## 3. Get the code and build it
|
||||
|
||||
```bash
|
||||
git clone https://c-git.crosscode.co.za/joshua/two-keys.git /var/www/two-keys
|
||||
cd /var/www/two-keys
|
||||
git checkout v1.0.0 # or stay on main for the latest
|
||||
|
||||
npm ci # do NOT add --omit=dev: the build needs the dev dependencies
|
||||
npm run validate-content # must print "All 8 games are valid."
|
||||
npm run build
|
||||
```
|
||||
|
||||
## 4. Start it with pm2
|
||||
|
||||
The port is set in `ecosystem.config.cjs` (`PORT: 3010`). Edit that file first if 3010 is taken.
|
||||
|
||||
```bash
|
||||
cd /var/www/two-keys
|
||||
pm2 start ecosystem.config.cjs
|
||||
pm2 save
|
||||
```
|
||||
|
||||
Make pm2 come back after a reboot (once per server):
|
||||
|
||||
```bash
|
||||
pm2 startup
|
||||
# pm2 prints one "sudo env PATH=... pm2 startup ..." line. Copy and run that line, then:
|
||||
pm2 save
|
||||
```
|
||||
|
||||
Check it locally:
|
||||
|
||||
```bash
|
||||
pm2 status two-keys
|
||||
curl http://127.0.0.1:3010/api/health
|
||||
pm2 logs two-keys --lines 20 # expect: "Two Keys server on http://localhost:3010 (8 games, ...)"
|
||||
```
|
||||
|
||||
## 5. Publish it through frp
|
||||
|
||||
### DNS
|
||||
|
||||
Point `two-keys.crosscode.co.za` at the **central server** (the one running frps), the same way
|
||||
your other frp-hosted sites are set up.
|
||||
|
||||
### frpc on the app server
|
||||
|
||||
Add one HTTP proxy to the frpc config, next to your existing ones.
|
||||
|
||||
`frpc.toml` (frp 0.52 and newer):
|
||||
|
||||
```toml
|
||||
[[proxies]]
|
||||
name = "two-keys"
|
||||
type = "http"
|
||||
localIP = "127.0.0.1"
|
||||
localPort = 3010
|
||||
customDomains = ["two-keys.crosscode.co.za"]
|
||||
```
|
||||
|
||||
`frpc.ini` (older frp):
|
||||
|
||||
```ini
|
||||
[two-keys]
|
||||
type = http
|
||||
local_ip = 127.0.0.1
|
||||
local_port = 3010
|
||||
custom_domains = two-keys.crosscode.co.za
|
||||
```
|
||||
|
||||
Then restart frpc, for example:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart frpc
|
||||
```
|
||||
|
||||
### Central server
|
||||
|
||||
Nothing Two Keys-specific is needed in frps itself: it routes by host name on its `vhostHTTPPort`,
|
||||
and websockets pass through an frp `http` proxy without extra settings.
|
||||
|
||||
What does need attention is whatever terminates HTTPS in front of frps:
|
||||
|
||||
- **Certificate:** add `two-keys.crosscode.co.za` to it, the same way as your other sites.
|
||||
- **Websocket upgrade:** if nginx (or similar) sits in front of frps, its server block for this
|
||||
domain must forward upgrades, or the game falls back to slow polling:
|
||||
|
||||
```nginx
|
||||
server {
|
||||
server_name two-keys.crosscode.co.za;
|
||||
# listen 443 ssl; plus your certificate lines
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080; # your frps vhostHTTPPort
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_read_timeout 3600s; # keep long games connected
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `Host` header must be passed through unchanged: frps uses it to pick the tunnel.
|
||||
|
||||
## 6. Check it from outside
|
||||
|
||||
```bash
|
||||
curl https://two-keys.crosscode.co.za/api/health
|
||||
```
|
||||
|
||||
Then open `https://two-keys.crosscode.co.za` on two phones, start a game on one, and join with the
|
||||
room code on the other. If both lobbies update when a role is picked, the websocket is working
|
||||
end to end.
|
||||
|
||||
## Updating to a new version
|
||||
|
||||
```bash
|
||||
cd /var/www/two-keys
|
||||
git pull # or: git fetch --tags && git checkout v1.1.0
|
||||
npm ci
|
||||
npm run validate-content
|
||||
npm run build
|
||||
pm2 restart two-keys
|
||||
```
|
||||
|
||||
Games in progress survive the restart: rooms are saved to `server/data/rooms.json` on shutdown and
|
||||
restored on start. Players reconnect by themselves within a few seconds.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause |
|
||||
|---------|--------------|
|
||||
| `EACCES` during `npm ci`, build, or at start-up | Folder not owned by your user. Re-run the `chown` in step 2. |
|
||||
| `EADDRINUSE` in `pm2 logs` | Port 3010 is taken. Change `PORT` in `ecosystem.config.cjs` and `localPort` in frpc, then `pm2 restart two-keys --update-env` and restart frpc. |
|
||||
| Local `curl` works, domain gives frp's "page not found" | `customDomains` doesn't match the host name exactly, frpc wasn't restarted, or the front proxy rewrites `Host`. |
|
||||
| Site loads but "Can't reach the server" toasts, or games feel laggy | Websocket upgrade headers missing on the central server's HTTPS proxy. |
|
||||
| Site loads but no games listed | `npm run build` or `validate-content` failed. Check `pm2 logs two-keys`. |
|
||||
|
||||
Do not run more than one instance (no pm2 cluster mode, no second copy behind a load balancer):
|
||||
room state is held in memory by the one process.
|
||||
@@ -24,3 +24,6 @@ Co-op mystery games for couples. Two players, different clues, every lock needs
|
||||
## Adding a new game later
|
||||
Copy a YAML in `content/games/`, change the `id` to match the filename, write your chapters,
|
||||
and run `npm run validate-content`. No code changes needed for new story games.
|
||||
|
||||
## Deploying
|
||||
See [DEPLOYMENT.md](DEPLOYMENT.md) for running it under pm2 and publishing it through frp.
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// pm2 process file. See DEPLOYMENT.md.
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: 'two-keys',
|
||||
script: 'server/dist/index.js',
|
||||
cwd: __dirname,
|
||||
// One instance only: rooms live in this process's memory.
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
PORT: 3010,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
Reference in New Issue
Block a user