[haxcms-nodejs] v1 API conformance normalization (canonical spec + Node fixes) - #20
Merged
Conversation
Spec (canonical): - LoginRequest adds jwt (revalidate); auth failures 401 per spec - POST settings aliases documented as read-only (no requestBody) - /session security [] (anon login probe) - provider-search requires bearerAuth + siteTokenHeader - GET /x/api/v1/files documents filename param - single-user assumption notes on config-write ops Node code: - views/blocks: anonymous-visibility enforcement on results/usage - files dateCreated in seconds (was milliseconds) - content concat HTML escapes record.id/title - views tags source honors ?sort (default -count) - exports: supportedFormats inside data; html export as file download - archiveSite uniquifies collided archive names - connectionSettings adds no-store cache headers - login failures 401 + JSON envelopes; keeps JWT-revalidate - removes POST skeletons/:name read alias - provider-search site token enforced in router - downloadSite excludes node_modules; drops dead getUserData code Refs: haxtheweb/issues conformance audit (site/system parity) Co-Authored-By: Oz <oz-agent@warp.dev>
…c + Node fixes) Co-Authored-By: Oz <oz-agent@warp.dev>
Review or Edit in CodeSandboxOpen the branch in Web Editor • VS Code • Insiders |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
This PR aligns NodeJS v1 system/site APIs more closely with the documented envelope/security behavior, tightening anonymous visibility, improving export/download responses, and enforcing site-token authorization for certain system routes.
Changes:
- Standardize several auth-related responses (login, system routing) to JSON envelopes and updated status codes.
- Enforce anonymous visibility rules across views/blocks/tags/search results.
- Improve export/download behaviors (HTML export download, zip excludes node_modules/.git) and update OpenAPI specs accordingly.
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/systemRoutes/v1/routes/login.js | Switches auth failure responses to JSON envelopes and adjusts status codes. |
| src/systemRoutes/v1/routes/getUserData.js | Removes a bespoke header-token helper (likely consolidating header parsing elsewhere). |
| src/systemRoutes/v1/routes/downloadSite.js | Changes zip behavior to glob files while excluding node_modules/.git. |
| src/systemRoutes/v1/routes/connectionSettings.js | Adds no-cache headers for token/JWT-bearing JS response. |
| src/systemRoutes/v1/routes/archiveSite.js | Prevents archive name collisions by uniquifying destination folder names. |
| src/siteRoutes/v1/views.js | Enforces anonymous visibility in view sources and adds sort support for tag records. |
| src/siteRoutes/v1/files.js | Normalizes dateCreated to seconds instead of milliseconds. |
| src/siteRoutes/v1/exports.js | Fixes envelope shape for supportedFormats and sends HTML export as a download. |
| src/siteRoutes/v1/content.js | Escapes authored fields when concatenating HTML to prevent injection. |
| src/siteRoutes/v1/blocks.js | Enforces anonymous visibility when computing block usage/usage details. |
| src/openapi/system-spec.yaml | Updates system API security/semantics (session probe, provider search auth, POST-as-read aliases, schema docs). |
| src/openapi/site-spec.yaml | Documents new filename query filter parameter for file listing. |
| src/lib/SystemRoutesMap.js | Removes open route and redundant POST handler mapping. |
| src/app.js | Adds site-token policy enforcement for authenticated-site system routes; makes route handlers async. |
Suppressed comments (1)
src/app.js:1
- These handlers are now
async, but there is no try/catch around awaited logic. In Express 4-style middleware, rejected promises from an async handler (or from awaited calls) may not be routed to error middleware reliably. Wrap the handler body intry { ... } catch (e) { next(e) }(or use a shared async-middleware wrapper) to ensure errors propagate vianext().
#!/usr/bin/env node
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+74
to
+77
| res.status(401).json({ | ||
| status: 401, | ||
| data: { message: 'Authentication required' }, | ||
| }); |
Comment on lines
+126
to
+134
| .glob('**/*', { | ||
| cwd: sourceDir, | ||
| ignore: [ | ||
| 'node_modules/**', | ||
| 'node_modules', | ||
| '.git/**', | ||
| '.git', | ||
| ], | ||
| }) |
Comment on lines
+1847
to
+1887
| async function enforceSystemApiSiteTokenPolicy(req, res, op, method, basicAuth) { | ||
| const policy = getSystemApiRouteAuthPolicy(op, method); | ||
| if (policy !== 'authenticated-site') { | ||
| return true; | ||
| } | ||
| const siteToken = getRequestHeaderValue(req, 'x-haxcms-site-token'); | ||
| if (siteToken === '') { | ||
| res.status(403).json({ | ||
| status: 403, | ||
| data: { message: 'X-HAXCMS-Site-Token header is required for this endpoint' }, | ||
| }); | ||
| return false; | ||
| } | ||
| const userName = resolveSystemApiAuthenticatedUserName(req, basicAuth); | ||
| if (userName === '') { | ||
| res.status(403).json({ | ||
| status: 403, | ||
| data: { message: 'Unable to resolve authenticated user context' }, | ||
| }); | ||
| return false; | ||
| } | ||
| const siteName = await resolveSiteApiRequestSiteName(req, { | ||
| userName: userName, | ||
| siteToken: siteToken, | ||
| }); | ||
| if (!siteName) { | ||
| res.status(403).json({ | ||
| status: 403, | ||
| data: { message: 'Unable to resolve site token context' }, | ||
| }); | ||
| return false; | ||
| } | ||
| if (!HAXCMS.validateRequestToken(siteToken, `${userName}:${siteName}`)) { | ||
| res.status(403).json({ | ||
| status: 403, | ||
| data: { message: 'Invalid X-HAXCMS-Site-Token header' }, | ||
| }); | ||
| return false; | ||
| } | ||
| return true; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Normalizes the haxcms-nodejs v1 API surface against the PHP backend and the shared OpenAPI specs, addressing all High and Medium findings from the conformance audit. This PR carries the canonical spec — the PHP PR syncs from it.
Changes
Spec (canonical —
src/openapi/):LoginRequestaddsjwt(revalidate support); auth-failure statuses aligned to 401 per specconfiguration/api-keys,configuration/media,blocks,skeletons,themes) documented as read-only (norequestBody); token stance consistent between GET and POST/system/api/v1/sessionsecurity set to[](anonymous login probe)providers/{provider}/searchnow requiresbearerAuth+siteTokenHeaderGET /x/api/v1/filesdocuments thefilenamequery paramNode code:
files.dateCreatedin seconds (was milliseconds)supportedFormatsmoved insidedataContent-Disposition)record.id/record.title(output-injection fix)?sort(default-count)archiveSiteuniquifies collided archive names (name-1,name-2…)connectionSettingsaddsno-storecache headers (token-bearing JS)POST skeletons/:skeletonNameread aliasdownloadSiteexcludesnode_modules; drops deadgetUserTokenFromHeaderDecisions applied
validateHaxiamManagedUserIdentityForRequestleft as-is (S5)Validation
node --checkon all 14 changed files → OKRelated
Co-Authored-By: Oz oz-agent@warp.dev