Files
gymflow/deployment_guide.md

176 lines
5.9 KiB
Markdown

# GymFlow Deployment Guide (NAS / Existing Container)
This guide explains how to deploy GymFlow to your Ugreen NAS by integrating it into your existing `nodejs-apps` container.
## 1. Preparation (Build)
You need to build the application on your local machine before transferring it to the NAS.
### Build Frontend
Run the following in the project root:
```bash
npm install
npm install
npm run build
```
> [!IMPORTANT]
> If you encounter a `TypeError: Missing parameter name` in your logs, ensure you have rebuilt the app with the latest changes (`app.get('*all', ...)`).
This creates a `dist` folder with the compiled frontend.
### Build Backend
1. Update your `server/package.json` to have these scripts for production:
```json
"scripts": {
"start": "node dist/index.js",
"start:prod": "node dist/index.js",
...
}
```
2. Run the following locally:
```bash
cd server
npm install
npm run build
cd ..
```
## 2. Transfer Files
Copy the entire `gymflow` folder to your NAS.
Destination: `nodejs_data/gymflow` (inside the folder mounted to `/usr/src/app`).
Ensure the following files/folders are present on the NAS:
- `gymflow/dist/` (Frontend assets)
- `gymflow/server/dist/` (Backend code)
- `gymflow/server/package.json`
- `gymflow/server/prisma/` (Schema for database)
- `gymflow/ecosystem.config.cjs` (PM2 config)
*Note: You do not need to copy `node_modules`. We will install production dependencies on the NAS to ensure compatibility.*
Update your `docker-compose.yml` to use a custom Dockerfile (to support building native modules like `better-sqlite3`) and map the new port.
### 2a. Use Custom Dockerfile
The standard `node:lts-slim` image lacks Python and build tools. Use the provided [Dockerfile.node-apps](file:///d:/Coding/gymflow/Dockerfile.node-apps):
1. Copy `Dockerfile.node-apps` to your NAS alongside `docker-compose.yml`.
2. Modify `docker-compose.yml`:
```yaml
services:
nodejs-apps:
build:
context: .
dockerfile: Dockerfile.node-apps
container_name: node-apps
# image: node:lts-slim <-- Comment this out
...
```
### 3. Integration
### Update `command`
Add the following to your existing command string. We added `npx prisma generate` explicitly to ensure the Linux client is built correctly, and we ensure `DATABASE_URL` is consistent:
```bash
... && cd ../gymflow/server && npm install --omit=dev && APP_MODE=prod DATABASE_URL=file:./prod.db npx prisma generate && APP_MODE=prod DATABASE_URL=file:./prod.db npx prisma db push && cd .. && pm2 start ecosystem.config.cjs && ...
```
*Note: We assume `gymflow` is a sibling of `ag-beats` etc.*
**Full Command Example:**
```yaml
command: /bin/sh -c "npm install -g pm2 && cd ag-beats && npm install && cd ../ball-shooting && npm install && cd ../gymflow/server && npm install --omit=dev && APP_MODE=prod DATABASE_URL=file:./prod.db npx prisma generate && APP_MODE=prod DATABASE_URL=file:./prod.db npx prisma db push && cd .. && pm2 start ecosystem.config.cjs && cd ../ag-beats && pm2 start ecosystem.config.js && cd ../ball-shooting && pm2 start npm --name ag-ball -- start && pm2 logs --raw"
```
> [!IMPORTANT]
> Since we updated the `Dockerfile.node-apps` to include `openssl` (required by Prisma), you **must** rebuild the image:
> `docker-compose up -d --build nodejs-apps`
### Update `ports`
Add the GymFlow port (3003 inside container, mapped to your choice, e.g., 3033):
```yaml
ports:
- "3030:3000"
- "3031:3001"
- "3032:3002"
- "3033:3003" # GymFlow
```
## 4. Unified Ecosystem Config
Your common `ecosystem.config.js` should look like this to ensure GymFlow has the correct production environment:
```javascript
module.exports = {
apps: [
{
name: "ag-home",
script: "server.js",
cwd: "/usr/src/app",
exec_mode: "fork",
},
{
name: "ag-beats",
script: "npm",
args: "start",
cwd: "/usr/src/app/ag-beats",
},
{
name: "ag-ball",
script: "npm",
args: "start",
cwd: "/usr/src/app/ball-shooting",
},
{
name: "gymflow",
script: "dist/index.js",
cwd: "/usr/src/app/gymflow/server",
env: {
NODE_ENV: "production",
APP_MODE: "prod",
PORT: 3003,
DATABASE_URL: "file:./prod.db",
DATABASE_URL_PROD: "file:./prod.db"
}
}
]
};
```
## 5. Nginx Proxy Manager
Point your domain (e.g., `gym.yourdomain.com`) to the NAS IP and the mapped port (`3033`).
- Scheme: `http`
- Forward Hostname / IP: `[NAS_IP]`
- Forward Port: `3033`
- Websockets Support: Enable (if needed for future features).
## 6. Troubleshooting
### "Readonly Database" Error
If you see an error like `Invalid prisma.userProfile.upsert() invocation: attempt to write a readonly database`:
1. **Verify Permissions:** Run the diagnostic script inside your container:
```bash
docker exec -it node-apps node /usr/src/app/gymflow/server/check_db_perms.js
```
2. **Fix Permissions:** If the checks fail, run these commands on your NAS inside the `gymflow/server` directory:
```bash
sudo chmod 777 .
sudo chmod 666 prod.db
```
*Note: SQLite needs write access to the directory itself to create temporary journaling files (`-wal`, `-shm`).*
3. **Check Docker User:** Alternatively, ensure your Docker container is running as a user who owns these files (e.g., set `user: "1000:1000"` in `docker-compose.yml` if your NAS user has that ID).
### "Invalid ELF Header" Error
If you see an error like `invalid ELF header` for `better-sqlite3.node`:
This happens because the `node_modules` contains Windows binaries (from your local machine) instead of Linux binaries.
1. **Fix Inside Container:** Run the following command to force a rebuild of native modules for Linux:
```bash
docker exec -it node-apps /bin/sh -c "cd /usr/src/app/gymflow/server && npm rebuild better-sqlite3"
```
2. **Restart Container:** After rebuilding, restart the container:
```bash
docker-compose restart nodejs-apps
```