@vltpkg/vsr 0.0.0-26
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/DEPLOY.md +163 -0
- package/LICENSE +119 -0
- package/README.md +314 -0
- package/config.ts +221 -0
- package/drizzle.config.js +40 -0
- package/info/COMPARISONS.md +37 -0
- package/info/CONFIGURATION.md +143 -0
- package/info/CONTRIBUTING.md +32 -0
- package/info/DATABASE_SETUP.md +108 -0
- package/info/GRANULAR_ACCESS_TOKENS.md +160 -0
- package/info/PROJECT_STRUCTURE.md +291 -0
- package/info/ROADMAP.md +27 -0
- package/info/SUPPORT.md +39 -0
- package/info/TESTING.md +301 -0
- package/info/USER_SUPPORT.md +31 -0
- package/package.json +77 -0
- package/scripts/build-assets.js +31 -0
- package/scripts/build-bin.js +62 -0
- package/scripts/prepack.js +27 -0
- package/src/assets/public/images/bg.png +0 -0
- package/src/assets/public/images/clients/logo-bun.png +0 -0
- package/src/assets/public/images/clients/logo-deno.png +0 -0
- package/src/assets/public/images/clients/logo-npm.png +0 -0
- package/src/assets/public/images/clients/logo-pnpm.png +0 -0
- package/src/assets/public/images/clients/logo-vlt.png +0 -0
- package/src/assets/public/images/clients/logo-yarn.png +0 -0
- package/src/assets/public/images/favicon/apple-touch-icon.png +0 -0
- package/src/assets/public/images/favicon/favicon-96x96.png +0 -0
- package/src/assets/public/images/favicon/favicon.ico +0 -0
- package/src/assets/public/images/favicon/favicon.svg +3 -0
- package/src/assets/public/images/favicon/site.webmanifest +21 -0
- package/src/assets/public/images/favicon/web-app-manifest-192x192.png +0 -0
- package/src/assets/public/images/favicon/web-app-manifest-512x512.png +0 -0
- package/src/assets/public/styles/styles.css +231 -0
- package/src/bin/demo/package.json +6 -0
- package/src/bin/demo/vlt.json +1 -0
- package/src/bin/vsr.ts +484 -0
- package/src/db/client.ts +590 -0
- package/src/db/migrations/0000_faulty_ricochet.sql +14 -0
- package/src/db/migrations/0000_initial.sql +29 -0
- package/src/db/migrations/0001_uuid_validation.sql +35 -0
- package/src/db/migrations/0001_wealthy_magdalene.sql +7 -0
- package/src/db/migrations/drop.sql +3 -0
- package/src/db/migrations/meta/0000_snapshot.json +104 -0
- package/src/db/migrations/meta/0001_snapshot.json +155 -0
- package/src/db/migrations/meta/_journal.json +20 -0
- package/src/db/schema.ts +43 -0
- package/src/index.ts +434 -0
- package/src/middleware/config.ts +79 -0
- package/src/middleware/telemetry.ts +43 -0
- package/src/queue/index.ts +97 -0
- package/src/routes/access.ts +852 -0
- package/src/routes/docs.ts +63 -0
- package/src/routes/misc.ts +469 -0
- package/src/routes/packages.ts +2823 -0
- package/src/routes/ping.ts +39 -0
- package/src/routes/search.ts +131 -0
- package/src/routes/static.ts +74 -0
- package/src/routes/tokens.ts +259 -0
- package/src/routes/users.ts +68 -0
- package/src/utils/auth.ts +202 -0
- package/src/utils/cache.ts +587 -0
- package/src/utils/config.ts +50 -0
- package/src/utils/database.ts +69 -0
- package/src/utils/docs.ts +146 -0
- package/src/utils/packages.ts +453 -0
- package/src/utils/response.ts +125 -0
- package/src/utils/routes.ts +64 -0
- package/src/utils/spa.ts +52 -0
- package/src/utils/tracing.ts +52 -0
- package/src/utils/upstream.ts +172 -0
- package/test/access.test.ts +705 -0
- package/test/audit.test.ts +828 -0
- package/test/dashboard.test.ts +693 -0
- package/test/dist-tags.test.ts +678 -0
- package/test/manifest.test.ts +436 -0
- package/test/packument.test.ts +530 -0
- package/test/ping.test.ts +41 -0
- package/test/search.test.ts +472 -0
- package/test/setup.ts +130 -0
- package/test/static.test.ts +646 -0
- package/test/tokens.test.ts +389 -0
- package/test/utils/auth.test.ts +214 -0
- package/test/utils/packages.test.ts +235 -0
- package/test/utils/response.test.ts +184 -0
- package/test/whoami.test.ts +119 -0
- package/tsconfig.json +16 -0
- package/tsconfig.worker.json +3 -0
- package/typedoc.mjs +2 -0
- package/types.ts +598 -0
- package/vitest.config.ts +25 -0
- package/vlt.json.example +56 -0
- package/wrangler.json +65 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Scalar } from '@scalar/hono-api-reference'
|
|
2
|
+
import { SCALAR_API_CONFIG } from '../../config.ts'
|
|
3
|
+
|
|
4
|
+
export const getDocs = Scalar(async c => {
|
|
5
|
+
try {
|
|
6
|
+
// Use the current request's origin instead of hardcoded localhost URL
|
|
7
|
+
const origin = new URL(c.req.url).origin
|
|
8
|
+
const api = await fetch(`${origin}/-/api`)
|
|
9
|
+
|
|
10
|
+
if (!api.ok) {
|
|
11
|
+
throw new Error(
|
|
12
|
+
`API fetch failed: ${api.status} ${api.statusText}`,
|
|
13
|
+
)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
17
|
+
const result = (await api.json()) as Record<string, any>
|
|
18
|
+
|
|
19
|
+
// Merge dynamic API spec with static config, with static config taking precedence
|
|
20
|
+
const {
|
|
21
|
+
authentication,
|
|
22
|
+
defaultHttpClient,
|
|
23
|
+
...configWithoutAuth
|
|
24
|
+
} = SCALAR_API_CONFIG
|
|
25
|
+
|
|
26
|
+
// Fix hardcoded localhost URLs in static config
|
|
27
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
28
|
+
const staticContent = {
|
|
29
|
+
...SCALAR_API_CONFIG.spec.content,
|
|
30
|
+
servers: [
|
|
31
|
+
{
|
|
32
|
+
url: origin,
|
|
33
|
+
description: 'Current deployment',
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const options = {
|
|
39
|
+
// Start with static config to preserve ALL your settings
|
|
40
|
+
...configWithoutAuth,
|
|
41
|
+
spec: {
|
|
42
|
+
...SCALAR_API_CONFIG.spec,
|
|
43
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
44
|
+
content: {
|
|
45
|
+
// Dynamic API content first
|
|
46
|
+
...result,
|
|
47
|
+
// Static config overrides any conflicts (info, servers, security, etc.)
|
|
48
|
+
...staticContent,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
// Fix custom CSS URL to use current origin
|
|
52
|
+
customCss: `@import '${origin}/public/styles/styles.css';`,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
56
|
+
return options as any
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// Fallback to static config if API fetch fails
|
|
59
|
+
// eslint-disable-next-line no-console
|
|
60
|
+
console.error('Failed to fetch API spec:', error)
|
|
61
|
+
return SCALAR_API_CONFIG
|
|
62
|
+
}
|
|
63
|
+
})
|
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
import { createRoute, z } from '@hono/zod-openapi'
|
|
2
|
+
import type { Context } from 'hono'
|
|
3
|
+
import type { Environment } from '../../types.ts'
|
|
4
|
+
|
|
5
|
+
// Route definitions for miscellaneous endpoints
|
|
6
|
+
|
|
7
|
+
export const auditRoute = createRoute({
|
|
8
|
+
method: 'post',
|
|
9
|
+
path: '/-/npm/audit',
|
|
10
|
+
tags: ['Security'],
|
|
11
|
+
summary: 'Security Audit (Not Implemented)',
|
|
12
|
+
description: `⚠️ **NOT IMPLEMENTED** - This endpoint is planned for future implementation.
|
|
13
|
+
|
|
14
|
+
Currently returns a placeholder response indicating the feature is not available.
|
|
15
|
+
|
|
16
|
+
For actual security auditing, use:
|
|
17
|
+
- \`npm audit\` with the official npm registry
|
|
18
|
+
- Third-party security scanning tools
|
|
19
|
+
- Socket.dev integration (planned for v1.x)
|
|
20
|
+
|
|
21
|
+
\`\`\`bash
|
|
22
|
+
$ npm audit # Will hit this endpoint but get "not implemented" response
|
|
23
|
+
\`\`\``,
|
|
24
|
+
request: {
|
|
25
|
+
body: {
|
|
26
|
+
content: {
|
|
27
|
+
'application/json': {
|
|
28
|
+
schema: z.object({
|
|
29
|
+
name: z.string().optional(),
|
|
30
|
+
version: z.string().optional(),
|
|
31
|
+
requires: z.record(z.string()).optional(),
|
|
32
|
+
dependencies: z.record(z.any()).optional(),
|
|
33
|
+
}),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
responses: {
|
|
39
|
+
501: {
|
|
40
|
+
content: {
|
|
41
|
+
'application/json': {
|
|
42
|
+
schema: z.object({
|
|
43
|
+
error: z.string(),
|
|
44
|
+
message: z.string(),
|
|
45
|
+
feature: z.literal('security_audit'),
|
|
46
|
+
status: z.literal('not_implemented'),
|
|
47
|
+
}),
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
description:
|
|
51
|
+
'Feature not implemented - security auditing is planned for future release',
|
|
52
|
+
},
|
|
53
|
+
400: {
|
|
54
|
+
content: {
|
|
55
|
+
'application/json': {
|
|
56
|
+
schema: z.object({
|
|
57
|
+
error: z.string(),
|
|
58
|
+
}),
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
description: 'Invalid request format',
|
|
62
|
+
},
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
export const auditQuickRoute = createRoute({
|
|
67
|
+
method: 'post',
|
|
68
|
+
path: '/-/npm/v1/security/audits/quick',
|
|
69
|
+
tags: ['NPM Compatibility'],
|
|
70
|
+
summary: 'Quick Security Audit',
|
|
71
|
+
description: `⚠️ **REDIRECTS TO /-/npm/audit** - This endpoint redirects to the main audit endpoint with a 308 status.
|
|
72
|
+
|
|
73
|
+
The main audit endpoint is currently not implemented and returns a "not implemented" response.`,
|
|
74
|
+
request: {
|
|
75
|
+
body: {
|
|
76
|
+
content: {
|
|
77
|
+
'application/json': {
|
|
78
|
+
schema: z.object({
|
|
79
|
+
name: z.string().optional(),
|
|
80
|
+
version: z.string().optional(),
|
|
81
|
+
requires: z.record(z.string()).optional(),
|
|
82
|
+
dependencies: z.record(z.any()).optional(),
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
responses: {
|
|
89
|
+
308: {
|
|
90
|
+
description:
|
|
91
|
+
'Permanent redirect to /-/npm/audit (which returns 501 Not Implemented)',
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
export const advisoriesBulkRoute = createRoute({
|
|
97
|
+
method: 'post',
|
|
98
|
+
path: '/-/npm/v1/security/advisories/bulk',
|
|
99
|
+
tags: ['NPM Compatibility'],
|
|
100
|
+
summary: 'Bulk Security Advisories',
|
|
101
|
+
description: `⚠️ **REDIRECTS TO /-/npm/audit** - This endpoint redirects to the main audit endpoint with a 308 status.
|
|
102
|
+
|
|
103
|
+
The main audit endpoint is currently not implemented and returns a "not implemented" response.`,
|
|
104
|
+
request: {
|
|
105
|
+
body: {
|
|
106
|
+
content: {
|
|
107
|
+
'application/json': {
|
|
108
|
+
schema: z.record(z.array(z.string())),
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
responses: {
|
|
114
|
+
308: {
|
|
115
|
+
description:
|
|
116
|
+
'Permanent redirect to /-/npm/audit (which returns 501 Not Implemented)',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Dashboard/GUI routes
|
|
122
|
+
export const dashboardDataRoute = createRoute({
|
|
123
|
+
method: 'get',
|
|
124
|
+
path: '/dashboard.json',
|
|
125
|
+
tags: ['Dashboard'],
|
|
126
|
+
summary: 'Dashboard Data',
|
|
127
|
+
description: `Get dashboard configuration data for the VSR web interface`,
|
|
128
|
+
request: {},
|
|
129
|
+
responses: {
|
|
130
|
+
200: {
|
|
131
|
+
content: {
|
|
132
|
+
'application/json': {
|
|
133
|
+
schema: z.object({
|
|
134
|
+
registry: z.object({
|
|
135
|
+
url: z.string(),
|
|
136
|
+
name: z.string(),
|
|
137
|
+
}),
|
|
138
|
+
features: z.object({
|
|
139
|
+
search: z.boolean(),
|
|
140
|
+
publish: z.boolean(),
|
|
141
|
+
access: z.boolean(),
|
|
142
|
+
}),
|
|
143
|
+
}),
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
description: 'Dashboard configuration',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
export const appDataRoute = createRoute({
|
|
152
|
+
method: 'get',
|
|
153
|
+
path: '/app-data.json',
|
|
154
|
+
tags: ['Dashboard'],
|
|
155
|
+
summary: 'App Data',
|
|
156
|
+
description: `Get application data for the VSR web interface`,
|
|
157
|
+
request: {},
|
|
158
|
+
responses: {
|
|
159
|
+
200: {
|
|
160
|
+
content: {
|
|
161
|
+
'application/json': {
|
|
162
|
+
schema: z.object({
|
|
163
|
+
packages: z.array(
|
|
164
|
+
z.object({
|
|
165
|
+
name: z.string(),
|
|
166
|
+
version: z.string(),
|
|
167
|
+
description: z.string().optional(),
|
|
168
|
+
}),
|
|
169
|
+
),
|
|
170
|
+
stats: z.object({
|
|
171
|
+
totalPackages: z.number(),
|
|
172
|
+
totalDownloads: z.number(),
|
|
173
|
+
}),
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
description: 'Application data',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
// npm v1 API compatibility redirects
|
|
183
|
+
export const searchRedirectRoute = createRoute({
|
|
184
|
+
method: 'get',
|
|
185
|
+
path: '/-/v1/search',
|
|
186
|
+
tags: ['NPM Compatibility'],
|
|
187
|
+
summary: 'Search',
|
|
188
|
+
description: `Redirect to current search endpoint for npm v1 compatibility`,
|
|
189
|
+
request: {},
|
|
190
|
+
responses: {
|
|
191
|
+
308: {
|
|
192
|
+
description: 'Permanent redirect to /-/search',
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
export const userRedirectRoute = createRoute({
|
|
198
|
+
method: 'get',
|
|
199
|
+
path: '/-/npm/v1/user',
|
|
200
|
+
tags: ['NPM Compatibility'],
|
|
201
|
+
summary: 'User Profile',
|
|
202
|
+
description: `Redirect to current user endpoint for npm v1 compatibility`,
|
|
203
|
+
request: {},
|
|
204
|
+
responses: {
|
|
205
|
+
308: {
|
|
206
|
+
description: 'Permanent redirect to /-/user',
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
export const tokensRedirectRoute = createRoute({
|
|
212
|
+
method: 'get',
|
|
213
|
+
path: '/-/npm/v1/tokens',
|
|
214
|
+
tags: ['NPM Compatibility'],
|
|
215
|
+
summary: 'Get Tokens',
|
|
216
|
+
description: `Redirect to current tokens endpoint for npm v1 compatibility`,
|
|
217
|
+
request: {},
|
|
218
|
+
responses: {
|
|
219
|
+
308: {
|
|
220
|
+
description: 'Permanent redirect to /-/tokens',
|
|
221
|
+
},
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
|
|
225
|
+
export const createTokenRedirectRoute = createRoute({
|
|
226
|
+
method: 'post',
|
|
227
|
+
path: '/-/npm/v1/tokens',
|
|
228
|
+
tags: ['NPM Compatibility'],
|
|
229
|
+
summary: 'Create Token',
|
|
230
|
+
description: `Redirect to current create token endpoint for npm v1 compatibility`,
|
|
231
|
+
request: {},
|
|
232
|
+
responses: {
|
|
233
|
+
308: {
|
|
234
|
+
description: 'Permanent redirect to /-/tokens',
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
export const updateTokenRedirectRoute = createRoute({
|
|
240
|
+
method: 'put',
|
|
241
|
+
path: '/-/npm/v1/tokens',
|
|
242
|
+
tags: ['NPM Compatibility'],
|
|
243
|
+
summary: 'Update Token',
|
|
244
|
+
description: `Redirect to current update token endpoint for npm v1 compatibility`,
|
|
245
|
+
request: {},
|
|
246
|
+
responses: {
|
|
247
|
+
308: {
|
|
248
|
+
description: 'Permanent redirect to /-/tokens',
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
export const deleteTokenRedirectRoute = createRoute({
|
|
254
|
+
method: 'delete',
|
|
255
|
+
path: '/-/npm/v1/tokens/token/{token}',
|
|
256
|
+
tags: ['NPM Compatibility'],
|
|
257
|
+
summary: 'Delete Token',
|
|
258
|
+
description: `Redirect to current delete token endpoint for npm v1 compatibility`,
|
|
259
|
+
request: {
|
|
260
|
+
params: z.object({
|
|
261
|
+
token: z.string(),
|
|
262
|
+
}),
|
|
263
|
+
},
|
|
264
|
+
responses: {
|
|
265
|
+
308: {
|
|
266
|
+
description: 'Permanent redirect to /-/tokens/:token',
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
// Static asset routes (for documentation purposes)
|
|
272
|
+
export const staticAssetsRoute = createRoute({
|
|
273
|
+
method: 'get',
|
|
274
|
+
path: '/public/{path}',
|
|
275
|
+
tags: ['Static Assets'],
|
|
276
|
+
summary: 'Static Assets',
|
|
277
|
+
description: `Serve static assets (CSS, JS, images, etc.)`,
|
|
278
|
+
request: {
|
|
279
|
+
params: z.object({
|
|
280
|
+
path: z.string(),
|
|
281
|
+
}),
|
|
282
|
+
},
|
|
283
|
+
responses: {
|
|
284
|
+
200: {
|
|
285
|
+
content: {
|
|
286
|
+
'application/octet-stream': {
|
|
287
|
+
schema: z.string().openapi({ format: 'binary' }),
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
description: 'Static file content',
|
|
291
|
+
},
|
|
292
|
+
404: {
|
|
293
|
+
description: 'File not found',
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
export const faviconRoute = createRoute({
|
|
299
|
+
method: 'get',
|
|
300
|
+
path: '/favicon.ico',
|
|
301
|
+
tags: ['Static Assets'],
|
|
302
|
+
summary: 'Favicon',
|
|
303
|
+
description: `Serve the favicon for the registry`,
|
|
304
|
+
request: {},
|
|
305
|
+
responses: {
|
|
306
|
+
200: {
|
|
307
|
+
content: {
|
|
308
|
+
'image/x-icon': {
|
|
309
|
+
schema: z.string().openapi({ format: 'binary' }),
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
description: 'Favicon file',
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
})
|
|
316
|
+
|
|
317
|
+
export const robotsRoute = createRoute({
|
|
318
|
+
method: 'get',
|
|
319
|
+
path: '/robots.txt',
|
|
320
|
+
tags: ['Static Assets'],
|
|
321
|
+
summary: 'Robots.txt',
|
|
322
|
+
description: `Serve robots.txt for web crawlers`,
|
|
323
|
+
request: {},
|
|
324
|
+
responses: {
|
|
325
|
+
200: {
|
|
326
|
+
content: {
|
|
327
|
+
'text/plain': {
|
|
328
|
+
schema: z.string(),
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
description: 'Robots.txt content',
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
})
|
|
335
|
+
|
|
336
|
+
export const webManifestRoute = createRoute({
|
|
337
|
+
method: 'get',
|
|
338
|
+
path: '/manifest.json',
|
|
339
|
+
tags: ['Static Assets'],
|
|
340
|
+
summary: 'Web App Manifest',
|
|
341
|
+
description: `Serve web app manifest for PWA support`,
|
|
342
|
+
request: {},
|
|
343
|
+
responses: {
|
|
344
|
+
200: {
|
|
345
|
+
content: {
|
|
346
|
+
'application/json': {
|
|
347
|
+
schema: z.object({
|
|
348
|
+
name: z.string(),
|
|
349
|
+
short_name: z.string(),
|
|
350
|
+
description: z.string(),
|
|
351
|
+
start_url: z.string(),
|
|
352
|
+
display: z.string(),
|
|
353
|
+
theme_color: z.string(),
|
|
354
|
+
background_color: z.string(),
|
|
355
|
+
icons: z.array(
|
|
356
|
+
z.object({
|
|
357
|
+
src: z.string(),
|
|
358
|
+
sizes: z.string(),
|
|
359
|
+
type: z.string(),
|
|
360
|
+
}),
|
|
361
|
+
),
|
|
362
|
+
}),
|
|
363
|
+
},
|
|
364
|
+
},
|
|
365
|
+
description: 'Web app manifest',
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
})
|
|
369
|
+
|
|
370
|
+
// Dashboard route handlers
|
|
371
|
+
export async function handleDashboardData(
|
|
372
|
+
c: Context,
|
|
373
|
+
): Promise<Response> {
|
|
374
|
+
const env = c.env as Environment
|
|
375
|
+
|
|
376
|
+
// If daemon is enabled, proxy to daemon service
|
|
377
|
+
|
|
378
|
+
if (env.DAEMON_ENABLED) {
|
|
379
|
+
try {
|
|
380
|
+
const DAEMON_URL = 'http://localhost:3000'
|
|
381
|
+
const data = await fetch(`${DAEMON_URL}/dashboard.json`)
|
|
382
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
383
|
+
const jsonData = (await data.json()) as Record<string, any>
|
|
384
|
+
return c.json(jsonData)
|
|
385
|
+
} catch (_error) {
|
|
386
|
+
// Fall through to standalone mode if daemon is unreachable
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// Standalone mode: provide dashboard data directly
|
|
391
|
+
const dashboardData = {
|
|
392
|
+
registry: {
|
|
393
|
+
url: env.URL || 'http://localhost:1337',
|
|
394
|
+
name: 'vlt serverless registry',
|
|
395
|
+
},
|
|
396
|
+
features: {
|
|
397
|
+
search: true,
|
|
398
|
+
publish: true,
|
|
399
|
+
access: true,
|
|
400
|
+
},
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
return c.json(dashboardData)
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export async function handleAppData(c: Context): Promise<Response> {
|
|
407
|
+
const env = c.env as Environment
|
|
408
|
+
|
|
409
|
+
// If daemon is enabled, proxy to daemon service
|
|
410
|
+
|
|
411
|
+
if (env.DAEMON_ENABLED) {
|
|
412
|
+
try {
|
|
413
|
+
const DAEMON_URL = 'http://localhost:3000'
|
|
414
|
+
const data = await fetch(`${DAEMON_URL}/app-data.json`)
|
|
415
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
|
|
416
|
+
const jsonData = (await data.json()) as Record<string, any>
|
|
417
|
+
return c.json(jsonData)
|
|
418
|
+
} catch (_error) {
|
|
419
|
+
// Fall through to standalone mode if daemon is unreachable
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Standalone mode: provide app data directly from the registry database
|
|
424
|
+
try {
|
|
425
|
+
// For now, return empty data structure - in a full implementation,
|
|
426
|
+
// we would query the database for actual package data
|
|
427
|
+
const packages: {
|
|
428
|
+
name: string
|
|
429
|
+
version: string
|
|
430
|
+
description?: string
|
|
431
|
+
}[] = []
|
|
432
|
+
|
|
433
|
+
const appData = {
|
|
434
|
+
packages,
|
|
435
|
+
stats: {
|
|
436
|
+
totalPackages: packages.length,
|
|
437
|
+
totalDownloads: 0, // This would need to be tracked separately
|
|
438
|
+
},
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return c.json(appData)
|
|
442
|
+
} catch (_error) {
|
|
443
|
+
// If database is not available, return empty data
|
|
444
|
+
const appData = {
|
|
445
|
+
packages: [],
|
|
446
|
+
stats: {
|
|
447
|
+
totalPackages: 0,
|
|
448
|
+
totalDownloads: 0,
|
|
449
|
+
},
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
return c.json(appData)
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Security audit handler (not implemented)
|
|
457
|
+
export async function handleSecurityAudit(
|
|
458
|
+
c: Context,
|
|
459
|
+
): Promise<Response> {
|
|
460
|
+
return c.json(
|
|
461
|
+
{
|
|
462
|
+
error: 'Security audit not implemented',
|
|
463
|
+
message: 'This feature is not yet available in VSR',
|
|
464
|
+
status: 'not_implemented',
|
|
465
|
+
feature: 'security_audit',
|
|
466
|
+
},
|
|
467
|
+
501,
|
|
468
|
+
)
|
|
469
|
+
}
|