REST Service for POPCORN - ILIAS
6138ffe99d016cc311f250bc1555b5403b8b1216..fc63b45717e336e06fd766f492271384cfff6ebc
15 hours ago alex
GS-2555
fc63b4 diff | tree
15 hours ago alex
0.6.0
527b9f diff | tree
15 hours ago alex
0.5.0
3c6c66 diff | tree
15 hours ago alex
GS-2555
60c189 diff | tree
15 hours ago alex
GS-2555
5dae0c diff | tree
2 files modified
1 files added
217 ■■■■■ changed files
UPDATE.md 180 ●●●●● patch | view | raw | blame | history
package-lock.json 29 ●●●●● patch | view | raw | blame | history
package.json 8 ●●●●● patch | view | raw | blame | history
UPDATE.md
New file
@@ -0,0 +1,180 @@
# 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).
package-lock.json
@@ -1,12 +1,12 @@
{
   "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",
@@ -24,7 +24,6 @@
            "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",
@@ -32,6 +31,7 @@
         },
         "devDependencies": {
            "@vitejs/plugin-vue": "^6.0.9",
            "nodemon": "^3.1.14",
            "vite": "^8.3.3",
            "vite-plugin-vue-devtools": "^8.2.1"
         }
@@ -1531,6 +1531,7 @@
         "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",
@@ -1544,6 +1545,7 @@
         "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"
@@ -1681,6 +1683,7 @@
         "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"
@@ -1714,6 +1717,7 @@
         "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"
@@ -2322,6 +2326,7 @@
         "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"
@@ -2463,6 +2468,7 @@
         "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"
@@ -2546,6 +2552,7 @@
         "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": {
@@ -2576,6 +2583,7 @@
         "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"
@@ -2604,6 +2612,7 @@
         "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"
@@ -2622,6 +2631,7 @@
         "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"
@@ -2666,6 +2676,7 @@
         "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"
@@ -3443,6 +3454,7 @@
         "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",
@@ -3471,6 +3483,7 @@
         "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",
@@ -3495,6 +3508,7 @@
         "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"
@@ -3504,6 +3518,7 @@
         "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"
@@ -3516,6 +3531,7 @@
         "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"
@@ -3528,6 +3544,7 @@
         "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"
@@ -3540,6 +3557,7 @@
         "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"
@@ -3787,6 +3805,7 @@
         "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": {
@@ -4100,6 +4119,7 @@
         "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"
@@ -4511,6 +4531,7 @@
         "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"
@@ -4551,6 +4572,7 @@
         "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"
@@ -4566,6 +4588,7 @@
         "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": {
package.json
@@ -1,6 +1,6 @@
{
   "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",
@@ -10,8 +10,8 @@
      "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",
@@ -30,7 +30,6 @@
      "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",
@@ -38,6 +37,7 @@
   },
   "devDependencies": {
      "@vitejs/plugin-vue": "^6.0.9",
      "nodemon": "^3.1.14",
      "vite": "^8.3.3",
      "vite-plugin-vue-devtools": "^8.2.1"
   }