2 files modified
1 files added
| New file |
| | |
| | | # Updating Globus-ILIAS-Rest in production |
| | | |
| | | This document describes how to deploy a new version of **Globus-ILIAS-Rest** to |
| | | the production installation (`https://www.lernen-mit-globus-baumarkt.de`). |
| | | |
| | | > Production host in this setup is `hati` (the ILIAS host of |
| | | > `www.lernen-mit-globus-baumarkt.de`). |
| | | > Test/dev deployments use the same steps with a different `NODE_ENV` |
| | | > (`test` → `globusfm-dev2`, `dev`/`dev2` → local). |
| | | |
| | | ## Overview of what has to be updated |
| | | |
| | | Production consists of **three parts** that may change independently: |
| | | |
| | | | Part | Location | Updated with | |
| | | |----------------------|----------------------------------------------|--------------------------------| |
| | | | Node.js REST service | the project dir on the server, run via `pm2` | code + `npm install` + restart | |
| | | | Frontend (Vue SPA) | `vue/dist` (built, **not** in git) | `npm run build` | |
| | | | PHP component | `$ILIASBASEDIR/globus-ilias-rest` | `copyPhpHati` | |
| | | |
| | | A full update therefore touches all three; a code-only change may only need the |
| | | first two. |
| | | |
| | | ## Prerequisites |
| | | |
| | | - Node.js **v22.x** installed on the server (`node -v`). |
| | | - `pm2` installed and the service already registered as `globus-ilias-rest`. |
| | | - `NODE_ENV=prod` is set for the service (e.g. in `~/.bashrc` or in the pm2 |
| | | environment). **It must be set before starting the app.** |
| | | - `settings.prod.json` exists and is correct (DB credentials, ports, tokens). |
| | | - The PHP component is already installed under the ILIAS base dir. |
| | | |
| | | ## Before you start |
| | | |
| | | 1. **Announce a short maintenance window** — restarting the service briefly |
| | | interrupts the POPCORN sync and the `/popcorn` SPA. |
| | | 2. **Back up the current state** (so you can roll back): |
| | | - note the current git revision (`git rev-parse HEAD`) or tag |
| | | - the settings are in git, but keep a copy of the server's |
| | | `settings.prod.json` if it was modified on the server |
| | | |
| | | ## Update steps |
| | | |
| | | Run the following on the production host, inside the project directory. |
| | | |
| | | ### 1. Get the new code |
| | | |
| | | Always update via git: |
| | | |
| | | git fetch --all |
| | | git checkout <tag-or-branch> # e.g. v0.4.0 or master |
| | | git pull |
| | | |
| | | > Always deploy via git. Only files tracked in git are deployed; untracked or |
| | | > locally modified files must not reach production. Make sure the working copy |
| | | > is clean (`git status`) and on the intended revision before proceeding. |
| | | |
| | | ### 2. Install dependencies |
| | | |
| | | npm install |
| | | |
| | | (Use `npm ci` if you want a clean, lockfile-exact install.) |
| | | |
| | | ### 3. Build the frontend |
| | | |
| | | The production build must use the production env values |
| | | (`vue/.env.production`). `vite.config.js` selects that file when |
| | | `VITE_ENV=production` is set: |
| | | |
| | | VITE_ENV=production npm run build |
| | | |
| | | This writes the SPA to `vue/dist/`, which `app.js` serves under `/popcorn/ui`. |
| | | |
| | | > Do **not** rely on `cp vue/.env.$NODE_ENV vue/.env` for production: with |
| | | > `NODE_ENV=prod` there is no `vue/.env.prod` file. Use `VITE_ENV=production`. |
| | | |
| | | ### 4. Update the PHP component (only if `php/` changed) |
| | | |
| | | ./copyPhpHati |
| | | |
| | | `copyPhpHati` copies `php/globus-ilias-rest/*` and its `.htaccess` into |
| | | `$ILIASBASEDIR/globus-ilias-rest` (`/var/local/ilias/globusfach/ilias/...` on |
| | | `hati`). |
| | | |
| | | > **Check the target path first.** Open `copyPhpHati` and verify that `P` |
| | | > (`/var/local/ilias/globusfach/ilias/globus-ilias-rest`) still matches the |
| | | > actual ILIAS base dir on the server. The path can change between ILIAS |
| | | > installations/upgrades — if it does, fix it in the script before running it. |
| | | |
| | | > The `token` file protects the PHP endpoint and is covered by `.htaccess` |
| | | > (`Require all denied`). After copying, make sure the file is still present |
| | | > and the owner/permissions are correct (`chown -R www-data:www-data`). |
| | | |
| | | > Note: the repo currently has **no** `copyPhp` script, although `package.json` |
| | | > defines `"php": "bash copyPhp"`. On `hati` use `copyPhpHati` directly. The |
| | | > final line of `copyPhpHati` |
| | | > (`sudo cp $P/api.php $P/login.php`) refers to the old `api.php` name and will |
| | | > fail / is a no-op now that the file is called `login.php`; the preceding |
| | | > `cp` already installs `login.php`. |
| | | |
| | | ### 5. Restart the service |
| | | |
| | | pm2 restart globus-ilias-rest |
| | | pm2 save |
| | | |
| | | Check that it is online: |
| | | |
| | | pm2 ls |
| | | |
| | | ### 6. Run the self test |
| | | |
| | | NODE_ENV=prod npm run test-connect |
| | | |
| | | All tests must succeed. (The tests for "the rest service" require the service to |
| | | be running — see step 5.) |
| | | |
| | | ## Verify the deployment |
| | | |
| | | Quick manual checks against production: |
| | | |
| | | # service version |
| | | curl https://www.lernen-mit-globus-baumarkt.de/popcorn/api/version |
| | | |
| | | > The `/api/version` endpoint must return the version from `package.json` |
| | | > (e.g. `{"version":"0.4.0"}`). If it differs, the deployed code is not the |
| | | > revision you expected — check the update/restart steps above before |
| | | > continuing. |
| | | |
| | | # authenticated API |
| | | curl "https://www.lernen-mit-globus-baumarkt.de/popcorn/api/user?token=$TOKEN" |
| | | |
| | | # PHP component |
| | | curl "https://www.lernen-mit-globus-baumarkt.de/globus-ilias-rest/login.php?token=$ILIASTOKEN&command=ping" |
| | | |
| | | # frontend (SPA) |
| | | curl -I https://www.lernen-mit-globus-baumarkt.de/popcorn/ui/ |
| | | |
| | | Then load the SPA in the browser and verify the user search / course pages work. |
| | | |
| | | ## Rollback |
| | | |
| | | If something is wrong: |
| | | |
| | | 1. Restore the previous revision: |
| | | - `git checkout <previous-tag-or-commit>` |
| | | 2. `npm install` |
| | | 3. `VITE_ENV=production npm run build` |
| | | 4. `./copyPhpHati` (if the PHP component changed) |
| | | 5. `pm2 restart globus-ilias-rest` |
| | | 6. Re-run `NODE_ENV=prod npm run test-connect` |
| | | |
| | | ## Notes / gotchas |
| | | |
| | | - **Node version:** production requires Node v22.x. Do not install Node from an |
| | | OS package manager (usually too old). |
| | | - **`NODE_ENV`** must be `prod` for the service, otherwise the wrong |
| | | `settings.*.json` is loaded. |
| | | - **Tokens:** two different tokens are used — the REST `authtoken` |
| | | (`settings.prod.json`) and the PHP `iliastoken` (also stored in |
| | | `php/globus-ilias-rest/token`). If one changes, update both. |
| | | - **HTTPS only:** the service must be reached via https, otherwise the auth |
| | | token can be read. |
| | | - **Frontend is not in git** (`vue/dist` and `node_modules` are ignored) — it |
| | | must be built on the server (or explicitly synced). |
| | | - **`npm run php`** is currently broken (no `copyPhp` script); use |
| | | `copyPhpHati` for production. |
| | | |
| | | ## Logs |
| | | |
| | | - Application log: `$INSTALLDIR/log.log` — should be included in a logrotation |
| | | job. |
| | | - Log level is configured via `settings.prod.json` → `log.level` (default |
| | | `info`); the `LOG_LEVEL` env var overrides it (`debug`, `info`, `warn`, |
| | | `error`, `silent`/`off`). |
| | | - pm2 logs: |
| | | |
| | | pm2 logs globus-ilias-rest |
| | | pm2 flush # clear pm2 logs |
| | | |
| | | pm2 logging can be turned off with `--disable-logs` (not advised). |
| | |
| | | { |
| | | "name": "globus-ilias-rest", |
| | | "version": "0.4.0", |
| | | "version": "0.6.0", |
| | | "lockfileVersion": 3, |
| | | "requires": true, |
| | | "packages": { |
| | | "": { |
| | | "name": "globus-ilias-rest", |
| | | "version": "0.4.0", |
| | | "version": "0.6.0", |
| | | "dependencies": { |
| | | "@fastify/compress": "^9.2.0", |
| | | "@fastify/static": "^10.1.5", |
| | |
| | | "mocha": "^12.0.3", |
| | | "mysql2": "^3.24.5", |
| | | "nconf": "^0.13.0", |
| | | "nodemon": "^3.1.14", |
| | | "stylus": "^0.64.0", |
| | | "vue": "^3.5.43", |
| | | "vue-router": "^5.3.1", |
| | |
| | | }, |
| | | "devDependencies": { |
| | | "@vitejs/plugin-vue": "^6.0.9", |
| | | "nodemon": "^3.1.14", |
| | | "vite": "^8.3.3", |
| | | "vite-plugin-vue-devtools": "^8.2.1" |
| | | } |
| | |
| | | "version": "3.1.3", |
| | | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", |
| | | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", |
| | | "dev": true, |
| | | "license": "ISC", |
| | | "dependencies": { |
| | | "normalize-path": "^3.0.0", |
| | |
| | | "version": "2.3.2", |
| | | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", |
| | | "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=8.6" |
| | |
| | | "version": "2.3.0", |
| | | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", |
| | | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=8" |
| | |
| | | "version": "3.0.3", |
| | | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", |
| | | "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "fill-range": "^7.1.1" |
| | |
| | | "version": "7.1.1", |
| | | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", |
| | | "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "to-regex-range": "^5.0.1" |
| | |
| | | "version": "5.1.2", |
| | | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", |
| | | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", |
| | | "dev": true, |
| | | "license": "ISC", |
| | | "dependencies": { |
| | | "is-glob": "^4.0.1" |
| | |
| | | "version": "1.0.1", |
| | | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", |
| | | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", |
| | | "dev": true, |
| | | "license": "ISC" |
| | | }, |
| | | "node_modules/inherits": { |
| | |
| | | "version": "2.1.0", |
| | | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", |
| | | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "binary-extensions": "^2.0.0" |
| | |
| | | "version": "2.1.1", |
| | | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", |
| | | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=0.10.0" |
| | |
| | | "version": "4.0.3", |
| | | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", |
| | | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "is-extglob": "^2.1.1" |
| | |
| | | "version": "7.0.0", |
| | | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", |
| | | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=0.12.0" |
| | |
| | | "version": "3.1.14", |
| | | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz", |
| | | "integrity": "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "chokidar": "^3.5.2", |
| | |
| | | "version": "3.6.0", |
| | | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", |
| | | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "anymatch": "~3.1.2", |
| | |
| | | "version": "3.0.0", |
| | | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", |
| | | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=4" |
| | |
| | | "version": "2.3.2", |
| | | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", |
| | | "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=8.6" |
| | |
| | | "version": "3.6.0", |
| | | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", |
| | | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "picomatch": "^2.2.1" |
| | |
| | | "version": "5.5.0", |
| | | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", |
| | | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "has-flag": "^3.0.0" |
| | |
| | | "version": "3.0.0", |
| | | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", |
| | | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "engines": { |
| | | "node": ">=0.10.0" |
| | |
| | | "version": "1.1.8", |
| | | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", |
| | | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", |
| | | "dev": true, |
| | | "license": "MIT" |
| | | }, |
| | | "node_modules/quansync": { |
| | |
| | | "version": "2.0.0", |
| | | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", |
| | | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "semver": "^7.5.3" |
| | |
| | | "version": "5.0.1", |
| | | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", |
| | | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", |
| | | "dev": true, |
| | | "license": "MIT", |
| | | "dependencies": { |
| | | "is-number": "^7.0.0" |
| | |
| | | "version": "3.1.1", |
| | | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", |
| | | "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", |
| | | "dev": true, |
| | | "license": "ISC", |
| | | "bin": { |
| | | "nodetouch": "bin/nodetouch.js" |
| | |
| | | "version": "2.0.5", |
| | | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", |
| | | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", |
| | | "dev": true, |
| | | "license": "MIT" |
| | | }, |
| | | "node_modules/undici-types": { |
| | |
| | | { |
| | | "name": "globus-ilias-rest", |
| | | "version": "0.4.0", |
| | | "version": "0.6.0", |
| | | "private": true, |
| | | "author": "Alexander Löhr", |
| | | "description": "REST API for ILIAS used by POPCORN", |
| | |
| | | "dev": "nodemon app.js", |
| | | "dev-ui": "vite", |
| | | "build": "vite build", |
| | | "php": "bash copyPhp", |
| | | "test-connect": "mocha test/testConnect.js" |
| | | "test-connect": "mocha test/testConnect.js", |
| | | "test": "npx mocha" |
| | | }, |
| | | "dependencies": { |
| | | "@fastify/compress": "^9.2.0", |
| | |
| | | "mocha": "^12.0.3", |
| | | "mysql2": "^3.24.5", |
| | | "nconf": "^0.13.0", |
| | | "nodemon": "^3.1.14", |
| | | "stylus": "^0.64.0", |
| | | "vue": "^3.5.43", |
| | | "vue-router": "^5.3.1", |
| | |
| | | }, |
| | | "devDependencies": { |
| | | "@vitejs/plugin-vue": "^6.0.9", |
| | | "nodemon": "^3.1.14", |
| | | "vite": "^8.3.3", |
| | | "vite-plugin-vue-devtools": "^8.2.1" |
| | | } |