From 7535c1724a5c2a1840cb25c0ed24c16f408d284a Mon Sep 17 00:00:00 2001 From: Bryan Haberberger Date: Mon, 18 Aug 2025 16:36:05 -0400 Subject: [PATCH 01/13] Users reported that the access token validation endpoint was always failing. (#209) --- routes/client.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/routes/client.js b/routes/client.js index 869b3899..0713ce68 100644 --- a/routes/client.js +++ b/routes/client.js @@ -1,6 +1,7 @@ import express from 'express' const router = express.Router() import auth from '../auth/index.js' +import { getAgentClaim } from '../controllers/utils.js' router.get('/register', (req, res, next) => { //Register means register with the RERUM Server Auth0 client and get a new code for a refresh token. @@ -18,6 +19,13 @@ router.get('/register', (req, res, next) => { router.post('/request-new-access-token',auth.generateNewAccessToken) router.post('/request-new-refresh-token',auth.generateNewRefreshToken) -router.get('/verify',auth.checkJwt) + +// Verifies good tokens are from RERUM. Fails with 401 on tokens from other platforms, or bad tokens in genreal. +router.get('/verify', auth.checkJwt, (req, res, next) => { + const generatorAgent = getAgentClaim(req, next) + res.set("Content-Type", "text/plain") + res.status(200) + res.send("The token was verified by Auth0") +}) export default router From 29db244b1b10fc4bb1c41e97f3082ce34536a65b Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Mon, 18 Aug 2025 21:47:14 +0100 Subject: [PATCH 02/13] Add comprehensive GitHub Copilot instructions with coding style guidelines for RERUM API development (#208) * Initial plan * Create comprehensive GitHub Copilot instructions with validated commands and timing Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> * Add coding style guidelines to Copilot instructions Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cubap <1119165+cubap@users.noreply.github.com> --- .github/copilot-instructions.md | 201 ++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 .github/copilot-instructions.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..9a7cdef9 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,201 @@ +# RERUM API v1 Server +Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. + +RERUM API v1 is a NodeJS web service for interaction with the RERUM digital object repository. It stores JSON-LD objects including Web Annotations, SharedCanvas/IIIF objects, FOAF Agents, and any valid JSON objects. Visit [rerum.io](https://rerum.io) for general information and [store.rerum.io](https://store.rerum.io/) for the hosted public instance. + +## Working Effectively + +### Prerequisites +- **CRITICAL**: Node.js version 22.17.1 or higher is required (specified in package.json engines: ">=22.12.0") +- MongoDB database connection for full API functionality +- Git for version control + +### Bootstrap and Setup +1. **Install Node.js 22.17.1**: + ```bash + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash + source ~/.bashrc + nvm install 22.17.1 + nvm use 22.17.1 + ``` + +2. **Install dependencies**: + ```bash + npm install + ``` + - NEVER CANCEL: Takes 2-5 seconds. Set timeout to 30+ seconds. + +3. **Create .env configuration file** (required for operation): + ```bash + # Create .env file in repository root + RERUM_API_VERSION=1.0.0 + RERUM_BASE=http://localhost:3005 + RERUM_PREFIX=http://localhost:3005/v1/ + RERUM_ID_PREFIX=http://localhost:3005/v1/id/ + RERUM_AGENT_CLAIM=http://localhost:3005/agent + RERUM_CONTEXT=http://localhost:3005/v1/context.json + RERUM_API_DOC=http://localhost:3005/v1/API.html + MONGO_CONNECTION_STRING=mongodb://localhost:27017/test + MONGODBNAME=test_rerum + MONGODBCOLLECTION=test_collection + DOWN=false + READONLY=false + PORT=3005 + CLIENTID=test_client_id + RERUMSECRET=test_secret + BOT_TOKEN=test_bot_token + BOT_AGENT=test_bot_agent + AUDIENCE=test_audience + ISSUER_BASE_URL=https://test.auth0.com/ + ``` + +### Testing +- **Route and Static File Tests** (work without database): + ```bash + npm run runtest -- __tests__/routes_mounted.test.js + ``` + - NEVER CANCEL: Takes 30 seconds. Set timeout to 60+ seconds. + +- **Full Test Suite** (requires MongoDB connection): + ```bash + npm run runtest + ``` + - NEVER CANCEL: Takes 25+ minutes (many tests timeout without MongoDB). Set timeout to 45+ minutes. + - **CRITICAL**: Most tests fail with 5-second timeouts if MongoDB is not connected - this is expected behavior in development environment. + +### Running the Application +- **Start the server**: + ```bash + npm start + ``` + - Server runs on `http://localhost:3005` (configurable via PORT in .env) + - Takes 2-3 seconds to start + - Displays "LISTENING ON 3005" when ready + +- **Stop the server**: `CTRL + C` or `CTRL + X` + +## Validation + +### Manual Testing Scenarios +After making changes, ALWAYS validate these scenarios: + +1. **Server Startup**: + ```bash + npm start + # Should display "LISTENING ON 3005" within 3 seconds + ``` + +2. **Static File Serving**: + ```bash + curl -I http://localhost:3005/v1/API.html + curl -I http://localhost:3005/v1/context.json + curl -I http://localhost:3005/ + # Should return 200 OK responses + ``` + +3. **Route Mounting**: + ```bash + npm run runtest -- __tests__/routes_mounted.test.js + # Should pass all 17 tests in ~30 seconds + ``` + +4. **API Authentication Behavior**: + ```bash + # Test create endpoint (matches example from Talend API Tester) + curl -X POST http://localhost:3005/v1/api/create -H "Content-Type: application/json" -d '{"@":"5"}' + # Should return 401 Unauthorized with proper auth message + + # Test with invalid token + curl -X POST http://localhost:3005/v1/api/create -H "Authorization: Bearer fake_token" -H "Content-Type: application/json" -d '{"@":"5"}' + # Should return 401 with "This token did not contain a known RERUM agent" + ``` + +5. **Database-dependent endpoints** (if MongoDB available): + ```bash + curl -X POST http://localhost:3005/v1/api/query -H "Content-Type: application/json" -d '{"test": "value"}' + # Should either work with 200 OK or return "Topology is closed" error without MongoDB + ``` + +### Working Without MongoDB +- **WORKS**: Server startup, static file serving, route mounting tests, authentication handling +- **FAILS**: All database operations (/query, /create, /update, /delete, /id/{id}, etc.) return "Topology is closed" errors +- **FOR DEVELOPMENT**: Use route mounting tests to validate routing changes without requiring database setup + +## Common Tasks + +### Key Directories +- `/routes/` - Route handlers and API endpoints (Express routes) +- `/controllers/` - Business logic controllers (CRUD operations, GOG-specific controllers) +- `/database/` - Database connection and utilities (MongoDB integration) +- `/auth/` - Authentication middleware (Auth0 JWT handling) +- `/public/` - Static files (API.html, context.json, etc.) +- `/__tests__/` - Test files (Jest test suites) +- `/bin/rerum_v1.js` - Main application entry point + +### Important Files to Monitor +- `package.json` - Dependencies and scripts (requires Node.js 22+) +- `app.js` - Express application setup and middleware configuration +- `routes/api-routes.js` - Main API route definitions and mounting +- `.env` - Configuration (create from template above) +- `jest.config.js` - Test configuration (VM modules experimental) + +### Architecture Notes +- **RESTful API**: Standard HTTP methods (GET, POST, PUT, PATCH, DELETE) +- **Authentication**: Auth0 JWT bearer tokens required for write operations +- **Database**: MongoDB for persistent storage, versioned objects +- **Static Files**: Served directly from `/public` directory +- **CORS**: Fully open ("*") for cross-origin requests +- **Specialized Routes**: Gallery of Glosses (GOG) specific endpoints in `_gog_*.js` files + +### Coding Style Guidelines +- **Semicolons**: Avoid unnecessary semicolons (e.g., at the end of most lines) +- **Control Flow**: Prefer guard clauses over if/else statements when the meaning is clear +- **Modern JavaScript**: Use optional chaining (`?.`) and nullish coalescing (`??`) operators and ternaries for concise code when it doesn't compromise readability +- **Language**: Use inclusive language and labels throughout the codebase +- **Attribution**: Include attribution for contributed code or borrowed code at the top of each file + +### Development Workflow +1. **After routing changes**: Always run basic route tests: `npm run runtest -- __tests__/routes_mounted.test.js` +2. **After configuration changes**: Test server startup: `npm start` (should display "LISTENING ON 3005") +3. **After API changes**: Test endpoints with curl as shown in validation scenarios +4. **For Auth0 integration testing**: Contact Research Computing Group at research.computing@slu.edu +5. **API reference**: Use documentation at http://localhost:3005/v1/API.html + +### Complete Change Validation Example +```bash +# 1. Make your changes to routes/controllers +# 2. Test route mounting +npm run runtest -- __tests__/routes_mounted.test.js + +# 3. Test server startup +npm start +# Should show "LISTENING ON 3005" + +# 4. In another terminal, test endpoints +curl -I http://localhost:3005/v1/API.html +curl -X POST http://localhost:3005/v1/api/create -H "Content-Type: application/json" -d '{"test": "value"}' + +# 5. Stop server: CTRL + C +``` + +### CI/CD Notes +- GitHub Actions run on Node.js 22 +- Tests run with: `npm install && npm run runtest` +- Deploy uses PM2 for process management +- Development environment available via PR to main branch +- Production deploys on push to main branch + +### Troubleshooting +- **"Unsupported engine" warnings**: Expected with Node.js 22+ due to express-oauth2-jwt-bearer package +- **Test timeouts**: Expected without MongoDB connection - focus on route mounting tests for development +- **Port conflicts**: Change PORT in .env file if 3005 is taken +- **JWT/Auth errors**: Expected in development - requires Auth0 setup for full functionality +- **"Cannot use import statement outside a module"**: Use `npm run runtest` (not `npm test`) - requires experimental VM modules +- **"Jest did not exit" warning**: Normal behavior - tests complete successfully despite this warning + +## Build and Timing Expectations +- **npm install**: 2-5 seconds, NEVER CANCEL (timeout: 30+ seconds) +- **Basic tests**: 30 seconds, NEVER CANCEL (timeout: 60+ seconds) +- **Full test suite**: 25+ minutes with timeouts, NEVER CANCEL (timeout: 45+ minutes) +- **Server startup**: 2-3 seconds +- **No build step required** - Direct Node.js execution \ No newline at end of file From b127eb17d92ed1be57e28b828e907a95822c0697 Mon Sep 17 00:00:00 2001 From: Bryan Haberberger Date: Sun, 24 Aug 2025 23:09:52 -0400 Subject: [PATCH 03/13] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 862eb083..603df373 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ npm install ``` #### Create the Configuration File -Create a file named `.env` in the root folder. In the above example, the root is `/code_folder/tiny_things`. `/code_folder/tiny_things/.env` looks like this: +Create a file named `.env` in the root folder. In the above example, the root is `/code_folder/rerum_api`. `/code_folder/rerum_api/.env` looks like this: ```shell RERUM_API_VERSION = 1.0.0 From 7a50ecf038ba690c3f5f263e8a09c599433c9f11 Mon Sep 17 00:00:00 2001 From: Devayani1612 Date: Tue, 9 Sep 2025 11:56:29 -0500 Subject: [PATCH 04/13] Bring in changes from old repo --- package-lock.json | 103 --------- public/index.html | 529 +++++++++++++++++++++++----------------------- 2 files changed, 261 insertions(+), 371 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6cd31a2d..6fbe9f1c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2509,29 +2509,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ip-address/node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause", - "optional": true, - "peer": true - }, "node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -3302,14 +3279,6 @@ "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -4212,33 +4181,6 @@ "node": ">=8" } }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true, - "peer": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -6596,26 +6538,6 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "optional": true, - "peer": true, - "requires": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "dependencies": { - "sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "optional": true, - "peer": true - } - } - }, "ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", @@ -7198,13 +7120,6 @@ "esprima": "^4.0.0" } }, - "jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "optional": true, - "peer": true - }, "jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -7845,24 +7760,6 @@ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==" }, - "smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true, - "peer": true - }, - "socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", - "optional": true, - "peer": true, - "requires": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - } - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", diff --git a/public/index.html b/public/index.html index 28eee8c7..72870ef6 100644 --- a/public/index.html +++ b/public/index.html @@ -18,18 +18,19 @@ color: green; font-weight: bold; } - + #intro { color: #979A9E; font-size: 12pt; } - + body { font-family: 'Open Sans', sans-serif; color: #979A9E; background-color: #2F353E; + padding: 20px; } - + input[type="text"] { background-color: #ccc; color: black; @@ -37,48 +38,48 @@ font-family: serif; font-size: 14pt; } - + h1 { cursor: pointer; font-weight: 300; font-family: 'Raleway', sans-serif; margin-bottom: 10px; } - + .navbar-brand { float: none; font-size: 2rem; line-height: 1.5; margin-bottom: 20px; } - + #login { display: none; } - + .panel-body { color: initial; } - + .panel { word-break: break-word; } - + .status_header { color: gray; } - + #a_t { height: 170px; margin-bottom: 8px; } - + #a_t, #r_t_4_a_t, #new_refresh_token { margin-bottom: 8px; } - + #code_for_refresh_token { margin-bottom: -13px; } @@ -106,287 +107,279 @@

-
Application Registration
-
-

- Interacting with RERUM requires server-to-server communication, so we suggest the registrant be the - application developer. - You may want to - learn more about the concepts around RERUM - before reading the API. -

-

- If you are here for the first time and think you want to use RERUM, please - read the API first. -

-

- If you like what you read in our API documentation - and want to begin using RERUM as a back stack service please register by clicking below. - Be prepared to be routed to Auth0 (don't know why? - Read the API). -

-

- After registering, you will be returned to this page with an Auth0 Authorization code. Use that code at - the bottom of this page to get a refresh token - and an access token so you can use the API. You may notice the page has already populated known - information for you. -

-
- - -
-
Auth0 Authorization Status
-
-

- If you believe you are already registered and want to check on your status, follow the prompts below. - You will be routed to Auth0 so we can verify who you are. -

-
- Auth0 Status - UNKNOWN + +
+
+
+
Application Registration
+
+

+ Interacting with RERUM requires server-to-server communication, so we suggest the registrant be the + application developer. + You may want to + learn more about the concepts around RERUM + before reading the API. +

+

+ If you are here for the first time and think you want to use RERUM, please + read the API first. +

+

+ If you like what you read in our API documentation + and want to begin using RERUM as a back stack service please register by clicking below. + Be prepared to be routed to Auth0 (don't know why? + Read the API). +

+

+ After registering, you will be returned to this page with an Auth0 Authorization code. +

+
+
- -
-
-
Test RERUM API Access
-
-

- Provide your access token below to check if it is still valid. If so, your access to RERUM will be - authorized. Otherwise, you will see an "unauthorized" message. -

-

- If the token you have is not working, it may be because access tokens expire every 30 days. You can use - your refresh token to get a new access token. -

- -
- RERUM status - UNKNOWN +
+
+
Already Registered Information
+
+

+ If you are already registered and need to manage your tokens or check your status, click the button below. +

+
+
-
-
-
Get A New Access Token
-
-

- Your access token to use RERUM expires every 30 days. Has it been that long or longer? Provide your - refresh token below to get a new access token. - If you lost your refresh token, you can get a new one in "Get A New Refresh Token" below. -

- -
- Status - UNKNOWN + +