ldrouter 1.11.4 → 1.11.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -0
- package/dist/server/app.js +20 -13
- package/migrations/000_add_key_digest_to_old_keys.sql +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ All notable changes to this project are documented here. The format follows
|
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/) and the project adheres to
|
|
5
5
|
[Semantic Versioning](https://semver.org/).
|
|
6
6
|
|
|
7
|
+
## [1.11.9] - 2026-09-04
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- **API routes now return proper JSON errors**: fixed `setNotFoundHandler` logic to ensure `/v1/*` routes always return JSON error responses instead of falling through to HTML SPA index. Route order confirmed: health → admin/gateway → static files.
|
|
12
|
+
|
|
13
|
+
## [1.11.8] - 2026-09-03
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- **ESLint flat config updated**: added debug scripts to ignore list in `eslint.config.mjs` (flat config replaces `.eslintignore` in ESLint v9+).
|
|
18
|
+
|
|
19
|
+
## [1.11.7] - 2026-09-03
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **API routes no longer return HTML on errors**: fixed route registration order so gateway routes (`/v1/*`) are registered BEFORE static file middleware, preventing API validation errors from serving SPA index.html instead of JSON error responses.
|
|
24
|
+
|
|
25
|
+
## [1.11.6] - 2026-09-03
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **Debug scripts excluded from linting**: initially used `.eslintignore`, then migrated to ESLint flat config proper syntax.
|
|
30
|
+
|
|
7
31
|
## [1.11.3] - 2026-09-03
|
|
8
32
|
|
|
9
33
|
### Fixed
|
package/dist/server/app.js
CHANGED
|
@@ -80,27 +80,34 @@ export async function buildApp(opts = {}) {
|
|
|
80
80
|
// Admin-site IP access control (Settings → Access Control). Root scope so it
|
|
81
81
|
// covers the static UI, login/setup, and all admin APIs; /health stays open.
|
|
82
82
|
registerAdminIpGate(app);
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
//
|
|
83
|
+
// Operational routes (always available)
|
|
84
|
+
await registerHealthRoutes(app);
|
|
85
|
+
// Admin + gateway routes MUST be registered BEFORE static files to avoid
|
|
86
|
+
// 404s falling through to SPA index.html or static assets being served instead
|
|
87
|
+
await registerAdminRoutes(app);
|
|
88
|
+
await registerGatewayRoutes(app);
|
|
89
|
+
// Static admin UI mounted AFTER routes so it only handles SPA not-found cases.
|
|
90
|
+
// This prevents conflicts with /v1/* API routes and /api/* endpoints.
|
|
86
91
|
const webDist = path.resolve(__dirname, '../web');
|
|
87
92
|
const hasWeb = fs.existsSync(webDist);
|
|
93
|
+
if (hasWeb) {
|
|
94
|
+
await app.register(staticPlugin, { root: webDist, prefix: '/', decorateReply: false });
|
|
95
|
+
}
|
|
88
96
|
app.setNotFoundHandler((req, reply) => {
|
|
89
|
-
if (req.url.startsWith('/api') || req.url.startsWith('/v1') || req.url.startsWith('/health') || req.url.startsWith('/ready') || req.url.startsWith('/metrics')
|
|
97
|
+
if (req.url.startsWith('/api') || req.url.startsWith('/v1') || req.url.startsWith('/health') || req.url.startsWith('/ready') || req.url.startsWith('/metrics')) {
|
|
90
98
|
const e = new GatewayError('invalid_request_error', 'Route not found', { status: 404 });
|
|
91
99
|
reply.code(404).send(toOpenAIError(e, req.id));
|
|
92
100
|
return;
|
|
93
101
|
}
|
|
94
|
-
|
|
102
|
+
// Serve SPA index.html only for non-API paths when web is available
|
|
103
|
+
if (hasWeb) {
|
|
104
|
+
reply.type('text/html').send(fs.readFileSync(path.join(webDist, 'index.html')));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// No web assets - return error for all remaining paths
|
|
108
|
+
const err = new GatewayError('invalid_request_error', 'Not found', { status: 404 });
|
|
109
|
+
reply.code(404).send(toOpenAIError(err, req.id));
|
|
95
110
|
});
|
|
96
|
-
if (hasWeb) {
|
|
97
|
-
await app.register(staticPlugin, { root: webDist, prefix: '/', decorateReply: false });
|
|
98
|
-
}
|
|
99
|
-
// Operational routes (always available)
|
|
100
|
-
await registerHealthRoutes(app);
|
|
101
|
-
// Admin + gateway routes
|
|
102
|
-
await registerAdminRoutes(app);
|
|
103
|
-
await registerGatewayRoutes(app);
|
|
104
111
|
// On startup: ensure settings row + detect master key status
|
|
105
112
|
app.addHook('onReady', async () => {
|
|
106
113
|
const s = getSettings();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Migration: Ensure all existing API keys have keyDigest populated
|
|
2
|
+
-- This fixes the issue where old keys stored without digest after restore
|
|
3
|
+
-- or from instances that didn't properly compute keyDigest on creation
|
|
4
|
+
|
|
5
|
+
BEGIN TRANSACTION;
|
|
6
|
+
|
|
7
|
+
-- Add missing keyDigest for any existing keys (should not happen if working correctly)
|
|
8
|
+
UPDATE apiKeys
|
|
9
|
+
SET keyDigest = substr(
|
|
10
|
+
hex(sha256(keySecretEncrypted || keySecretNonce)),
|
|
11
|
+
1, 64
|
|
12
|
+
)
|
|
13
|
+
WHERE keyDigest IS NULL OR keyDigest = '';
|
|
14
|
+
|
|
15
|
+
COMMIT;
|