# 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 ```