@nocobase/app-plugin-api-keys 0.0.0-stage → 1.0.0-beta.13

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +418 -0
  2. package/README.md +165 -2
  3. package/dist/client/api-keys-client.d.ts +19 -0
  4. package/dist/client/api-keys-client.d.ts.map +1 -0
  5. package/dist/client/api-keys-client.js +7 -0
  6. package/dist/client/api-keys-client.js.map +1 -0
  7. package/dist/client/components/page-container.d.ts +4 -0
  8. package/dist/client/components/page-container.d.ts.map +1 -0
  9. package/dist/client/components/page-container.js +6 -0
  10. package/dist/client/components/page-container.js.map +1 -0
  11. package/dist/client/components/page-header.d.ts +8 -0
  12. package/dist/client/components/page-header.d.ts.map +1 -0
  13. package/dist/client/components/page-header.js +5 -0
  14. package/dist/client/components/page-header.js.map +1 -0
  15. package/dist/client/components/ui/alert-dialog.d.ts +19 -0
  16. package/dist/client/components/ui/alert-dialog.d.ts.map +1 -0
  17. package/dist/client/components/ui/alert-dialog.js +42 -0
  18. package/dist/client/components/ui/alert-dialog.js.map +1 -0
  19. package/dist/client/components/ui/badge.d.ts +6 -0
  20. package/dist/client/components/ui/badge.d.ts.map +1 -0
  21. package/dist/client/components/ui/badge.js +8 -0
  22. package/dist/client/components/ui/badge.js.map +1 -0
  23. package/dist/client/components/ui/button.d.ts +8 -0
  24. package/dist/client/components/ui/button.d.ts.map +1 -0
  25. package/dist/client/components/ui/button.js +27 -0
  26. package/dist/client/components/ui/button.js.map +1 -0
  27. package/dist/client/components/ui/dialog.d.ts +9 -0
  28. package/dist/client/components/ui/dialog.d.ts.map +1 -0
  29. package/dist/client/components/ui/dialog.js +29 -0
  30. package/dist/client/components/ui/dialog.js.map +1 -0
  31. package/dist/client/components/ui/input.d.ts +4 -0
  32. package/dist/client/components/ui/input.d.ts.map +1 -0
  33. package/dist/client/components/ui/input.js +8 -0
  34. package/dist/client/components/ui/input.js.map +1 -0
  35. package/dist/client/components/ui/label.d.ts +3 -0
  36. package/dist/client/components/ui/label.d.ts.map +1 -0
  37. package/dist/client/components/ui/label.js +6 -0
  38. package/dist/client/components/ui/label.js.map +1 -0
  39. package/dist/client/components/ui/select.d.ts +8 -0
  40. package/dist/client/components/ui/select.d.ts.map +1 -0
  41. package/dist/client/components/ui/select.js +19 -0
  42. package/dist/client/components/ui/select.js.map +1 -0
  43. package/dist/client/components/ui/spinner.d.ts +4 -0
  44. package/dist/client/components/ui/spinner.d.ts.map +1 -0
  45. package/dist/client/components/ui/spinner.js +11 -0
  46. package/dist/client/components/ui/spinner.js.map +1 -0
  47. package/dist/client/components/ui/table.d.ts +8 -0
  48. package/dist/client/components/ui/table.d.ts.map +1 -0
  49. package/dist/client/components/ui/table.js +21 -0
  50. package/dist/client/components/ui/table.js.map +1 -0
  51. package/dist/client/expiry.d.ts +23 -0
  52. package/dist/client/expiry.d.ts.map +1 -0
  53. package/dist/client/expiry.js +40 -0
  54. package/dist/client/expiry.js.map +1 -0
  55. package/dist/client/index.d.ts +6 -0
  56. package/dist/client/index.d.ts.map +1 -0
  57. package/dist/client/index.js +7 -0
  58. package/dist/client/index.js.map +1 -0
  59. package/dist/client/lib/utils.d.ts +2 -0
  60. package/dist/client/lib/utils.d.ts.map +1 -0
  61. package/dist/client/lib/utils.js +2 -0
  62. package/dist/client/lib/utils.js.map +1 -0
  63. package/dist/client/locales/en-US.d.ts +71 -0
  64. package/dist/client/locales/en-US.d.ts.map +1 -0
  65. package/dist/client/locales/en-US.js +62 -0
  66. package/dist/client/locales/en-US.js.map +1 -0
  67. package/dist/client/locales/index.d.ts +4 -0
  68. package/dist/client/locales/index.d.ts.map +1 -0
  69. package/dist/client/locales/index.js +7 -0
  70. package/dist/client/locales/index.js.map +1 -0
  71. package/dist/client/locales/zh-CN.d.ts +4 -0
  72. package/dist/client/locales/zh-CN.d.ts.map +1 -0
  73. package/dist/client/locales/zh-CN.js +62 -0
  74. package/dist/client/locales/zh-CN.js.map +1 -0
  75. package/dist/client/pages/api-keys-page.d.ts +3 -0
  76. package/dist/client/pages/api-keys-page.d.ts.map +1 -0
  77. package/dist/client/pages/api-keys-page.js +161 -0
  78. package/dist/client/pages/api-keys-page.js.map +1 -0
  79. package/dist/client/plugin.d.ts +9 -0
  80. package/dist/client/plugin.d.ts.map +1 -0
  81. package/dist/client/plugin.js +10 -0
  82. package/dist/client/plugin.js.map +1 -0
  83. package/dist/client/routes.d.ts +22 -0
  84. package/dist/client/routes.d.ts.map +1 -0
  85. package/dist/client/routes.js +42 -0
  86. package/dist/client/routes.js.map +1 -0
  87. package/dist/database/migrations/.manifest.json +9 -0
  88. package/dist/database/migrations/202609150001_create_api_key_table.js +58 -0
  89. package/dist/database/migrations/202609150001_create_api_key_table.js.map +1 -0
  90. package/dist/server/api-docs.d.ts +24 -0
  91. package/dist/server/api-docs.d.ts.map +1 -0
  92. package/dist/server/api-docs.js +73 -0
  93. package/dist/server/api-docs.js.map +1 -0
  94. package/dist/server/api-keys.d.ts +52 -0
  95. package/dist/server/api-keys.d.ts.map +1 -0
  96. package/dist/server/api-keys.js +199 -0
  97. package/dist/server/api-keys.js.map +1 -0
  98. package/dist/server/index.d.ts +23 -0
  99. package/dist/server/index.d.ts.map +1 -0
  100. package/dist/server/index.js +22 -0
  101. package/dist/server/index.js.map +1 -0
  102. package/dist/server/key-sessions.d.ts +35 -0
  103. package/dist/server/key-sessions.d.ts.map +1 -0
  104. package/dist/server/key-sessions.js +50 -0
  105. package/dist/server/key-sessions.js.map +1 -0
  106. package/dist/server/plugin.d.ts +4 -0
  107. package/dist/server/plugin.d.ts.map +1 -0
  108. package/dist/server/plugin.js +15 -0
  109. package/dist/server/plugin.js.map +1 -0
  110. package/dist/server/providers/index.d.ts +19 -0
  111. package/dist/server/providers/index.d.ts.map +1 -0
  112. package/dist/server/providers/index.js +72 -0
  113. package/dist/server/providers/index.js.map +1 -0
  114. package/dist/server/routes/errors.d.ts +8 -0
  115. package/dist/server/routes/errors.d.ts.map +1 -0
  116. package/dist/server/routes/errors.js +48 -0
  117. package/dist/server/routes/errors.js.map +1 -0
  118. package/dist/server/routes/index.d.ts +10 -0
  119. package/dist/server/routes/index.d.ts.map +1 -0
  120. package/dist/server/routes/index.js +177 -0
  121. package/dist/server/routes/index.js.map +1 -0
  122. package/dist/server/routes/schemas.d.ts +25 -0
  123. package/dist/server/routes/schemas.d.ts.map +1 -0
  124. package/dist/server/routes/schemas.js +127 -0
  125. package/dist/server/routes/schemas.js.map +1 -0
  126. package/dist/server/scoped-keys.d.ts +116 -0
  127. package/dist/server/scoped-keys.d.ts.map +1 -0
  128. package/dist/server/scoped-keys.js +451 -0
  129. package/dist/server/scoped-keys.js.map +1 -0
  130. package/dist/server/scopes.d.ts +70 -0
  131. package/dist/server/scopes.d.ts.map +1 -0
  132. package/dist/server/scopes.js +165 -0
  133. package/dist/server/scopes.js.map +1 -0
  134. package/dist/server/service.d.ts +28 -0
  135. package/dist/server/service.d.ts.map +1 -0
  136. package/dist/server/service.js +69 -0
  137. package/dist/server/service.js.map +1 -0
  138. package/dist/server/tokens.d.ts +6 -0
  139. package/dist/server/tokens.d.ts.map +1 -0
  140. package/dist/server/tokens.js +5 -0
  141. package/dist/server/tokens.js.map +1 -0
  142. package/dist/shared/scopes.d.ts +160 -0
  143. package/dist/shared/scopes.d.ts.map +1 -0
  144. package/dist/shared/scopes.js +83 -0
  145. package/dist/shared/scopes.js.map +1 -0
  146. package/package.json +91 -3
  147. package/skills/nocobase-app-plugin-api-keys/SKILL.md +166 -0
@@ -0,0 +1,50 @@
1
+ import { ApiError, apiErrorHandler } from '@nocobase/app-server/router';
2
+ /** The header a key arrives in; Better Auth's default `apiKeyHeaders`. */
3
+ export const API_KEY_HEADER = 'x-api-key';
4
+ /** The Better Auth endpoints no API key may reach, whatever its scope: every `/api-key/*` endpoint. */
5
+ export const KEY_MANAGEMENT_PATH_PREFIXES = ['/api-key/'];
6
+ /** Whether a Better Auth endpoint path manages API keys. */
7
+ export function isKeyManagementPath(path) {
8
+ return KEY_MANAGEMENT_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
9
+ }
10
+ /**
11
+ * Whether a session was made from an API key: Better Auth's key session carries the key as its token, which a
12
+ * sign-in session never matches.
13
+ */
14
+ export function isApiKeySession(session, headers, headerNames = [API_KEY_HEADER]) {
15
+ return headerNames.some((name) => {
16
+ const key = headers.get(name);
17
+ return Boolean(key) && session.session.token === key;
18
+ });
19
+ }
20
+ /**
21
+ * The Better Auth endpoints that review, approve or deny a device authorization (`deviceAuthorization()`): approving
22
+ * issues the device a session, so no API key reaches them.
23
+ */
24
+ export const DEVICE_APPROVAL_PATHS = [
25
+ '/device',
26
+ '/device/approve',
27
+ '/device/deny',
28
+ ];
29
+ /** Whether a Better Auth endpoint path reviews, approves or denies a device authorization. */
30
+ export function isDeviceApprovalPath(path) {
31
+ return DEVICE_APPROVAL_PATHS.includes(path);
32
+ }
33
+ /**
34
+ * Refuses a request whose session came from an API key with 403 `API_KEY_SESSION_FORBIDDEN`. Install after
35
+ * `auth.required()` on every route that manages keys, a service account's included.
36
+ */
37
+ export function requireSignInSession() {
38
+ return async (context, next) => {
39
+ const auth = context.get('auth');
40
+ if (auth && isApiKeySession(auth, context.req.raw.headers))
41
+ return apiErrorHandler(new ApiError({
42
+ status: 'PERMISSION_DENIED',
43
+ reason: 'API_KEY_SESSION_FORBIDDEN',
44
+ domain: 'apiKeys',
45
+ message: 'API keys are managed only from a signed-in session.',
46
+ }), context);
47
+ await next();
48
+ };
49
+ }
50
+ //# sourceMappingURL=key-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-sessions.js","sourceRoot":"","sources":["../../server/key-sessions.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAGxE,0EAA0E;AAC1E,MAAM,CAAC,MAAM,cAAc,GAAG,WAAW,CAAC;AAE1C,uGAAuG;AACvG,MAAM,CAAC,MAAM,4BAA4B,GAAsB,CAAC,WAAW,CAAC,CAAC;AAE7E,4DAA4D;AAC5D,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC9C,OAAO,4BAA4B,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAChF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAyD,EACzD,OAAgB,EAChB,cAAiC,CAAC,cAAc,CAAC;IAEjD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE;QAC/B,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,KAAK,KAAK,GAAG,CAAC;IACvD,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAsB;IACtD,SAAS;IACT,iBAAiB;IACjB,cAAc;CACf,CAAC;AAEF,8FAA8F;AAC9F,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,qBAAqB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,IAAI,IAAI,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC;YACxD,OAAO,eAAe,CACpB,IAAI,QAAQ,CAAC;gBACX,MAAM,EAAE,mBAAmB;gBAC3B,MAAM,EAAE,2BAA2B;gBACnC,MAAM,EAAE,SAAS;gBACjB,OAAO,EAAE,qDAAqD;aAC/D,CAAC,EACF,OAAO,CACR,CAAC;QACJ,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { type AppServerPlugin } from '@nocobase/app-server/plugins';
2
+ declare const apiKeysPlugin: AppServerPlugin;
3
+ export default apiKeysPlugin;
4
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../server/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAC;AAKtC,QAAA,MAAM,aAAa,EAAE,eAQnB,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -0,0 +1,15 @@
1
+ import path from 'node:path';
2
+ import { defineServerPlugin, } from '@nocobase/app-server/plugins';
3
+ import serviceProviders from './providers/index.js';
4
+ import routes from './routes/index.js';
5
+ const apiKeysPlugin = defineServerPlugin({
6
+ baseDir: path.resolve(import.meta.dirname, '..'),
7
+ packageName: '@nocobase/app-plugin-api-keys',
8
+ serviceProviders,
9
+ routes,
10
+ database: {
11
+ migrations: './database/migrations',
12
+ },
13
+ });
14
+ export default apiKeysPlugin;
15
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../server/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EACL,kBAAkB,GAEnB,MAAM,8BAA8B,CAAC;AAEtC,OAAO,gBAAgB,MAAM,sBAAsB,CAAC;AACpD,OAAO,MAAM,MAAM,mBAAmB,CAAC;AAEvC,MAAM,aAAa,GAAoB,kBAAkB,CAAC;IACxD,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC;IAChD,WAAW,EAAE,+BAA+B;IAC5C,gBAAgB;IAChB,MAAM;IACN,QAAQ,EAAE;QACR,UAAU,EAAE,uBAAuB;KACpC;CACF,CAAC,CAAC;AAEH,eAAe,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ import type { AppPluginApplication, AppPluginProviderConstructor } from '@nocobase/app-server/plugins';
2
+ import { ServiceProvider } from '@nocobase/service-provider';
3
+ /**
4
+ * Binds the key-scope registry and the scoped-key service, and at boot teaches authentication and authorization to
5
+ * recognize a scoped key: `auth.required()` refuses one unless the route opts in, and the authorization identity of a
6
+ * request made with one carries its `keyScope`. Also lets a request carrying a valid API key read the application's API
7
+ * documentation, and adds the API key header to the document as a security scheme.
8
+ */
9
+ export declare class ApiKeysProvider extends ServiceProvider<AppPluginApplication> {
10
+ readonly name: string;
11
+ private readonly releases;
12
+ private booted;
13
+ register(): void;
14
+ boot(): Promise<void>;
15
+ shutdown(): Promise<void>;
16
+ }
17
+ export declare const serviceProviders: readonly AppPluginProviderConstructor[];
18
+ export default serviceProviders;
19
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/providers/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,oBAAoB,EACpB,4BAA4B,EAC7B,MAAM,8BAA8B,CAAC;AAGtC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAc7D;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,eAAe,CAAC,oBAAoB,CAAC;IACxE,SAAgB,IAAI,EAAE,MAAM,CAAmC;IAC/D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,MAAM,CAAS;IAEP,QAAQ,IAAI,IAAI;IAmBV,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAuC3B,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAI1C;AAED,eAAO,MAAM,gBAAgB,EAAE,SAAS,4BAA4B,EAEnE,CAAC;AAEF,eAAe,gBAAgB,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { authenticationToken } from '@nocobase/app-plugin-authentication/server';
2
+ import { authorizationToken } from '@nocobase/app-plugin-authorization';
3
+ import { apiDocsToken } from '@nocobase/app-server/router';
4
+ import { databaseManagerToken } from '@nocobase/db';
5
+ import { ServiceProvider } from '@nocobase/service-provider';
6
+ import { createApiKeyApiDocsAccess, createApiKeySecurityFragment, } from '../api-docs.js';
7
+ import { connectOwnKeyPolicy, ScopedApiKeys, } from '../scoped-keys.js';
8
+ import { apiKeyScopesToken, createApiKeyScopes } from '../scopes.js';
9
+ import { scopedApiKeysToken } from '../tokens.js';
10
+ /**
11
+ * Binds the key-scope registry and the scoped-key service, and at boot teaches authentication and authorization to
12
+ * recognize a scoped key: `auth.required()` refuses one unless the route opts in, and the authorization identity of a
13
+ * request made with one carries its `keyScope`. Also lets a request carrying a valid API key read the application's API
14
+ * documentation, and adds the API key header to the document as a security scheme.
15
+ */
16
+ export class ApiKeysProvider extends ServiceProvider {
17
+ name = '@nocobase/app-plugin-api-keys';
18
+ releases = [];
19
+ booted = false;
20
+ register() {
21
+ const { container } = this.app;
22
+ container.singleton(apiKeyScopesToken, () => createApiKeyScopes());
23
+ container.singleton(scopedApiKeysToken, (resolver) => new ScopedApiKeys({
24
+ auth: resolver.resolve(authenticationToken),
25
+ connection: () => resolver.resolve(databaseManagerToken).connection(),
26
+ scopes: resolver.resolve(apiKeyScopesToken),
27
+ authorization: () => container.has(authorizationToken)
28
+ ? resolver.resolve(authorizationToken)
29
+ : undefined,
30
+ config: () => this.app.config.get('apiKeys'),
31
+ }));
32
+ }
33
+ async boot() {
34
+ const { container } = this.app;
35
+ if (this.booted ||
36
+ !container.has(authenticationToken) ||
37
+ !container.has(databaseManagerToken))
38
+ return;
39
+ this.booted = true;
40
+ if (container.has(apiDocsToken)) {
41
+ const apiDocs = container.resolve(apiDocsToken);
42
+ const resolveAuth = () => container.resolve(authenticationToken);
43
+ apiDocs.addAccess(createApiKeyApiDocsAccess(resolveAuth));
44
+ // Resolved on the first request for the document, like the access check, so starting the application builds nothing.
45
+ apiDocs.addFragment(() => createApiKeySecurityFragment(resolveAuth));
46
+ }
47
+ const keys = container.resolve(scopedApiKeysToken);
48
+ const auth = container.resolve(authenticationToken);
49
+ this.releases.push(await connectOwnKeyPolicy(auth, keys));
50
+ this.releases.push(auth.addScopedCredentialCheck(async (session, request) => (await keys.resolve(session, request)) !== null));
51
+ if (container.has(authorizationToken))
52
+ container.resolve(authorizationToken).use(async (request, next) => {
53
+ const session = request.http.get('auth');
54
+ if (session && typeof session === 'object' && 'session' in session) {
55
+ const scope = await keys.resolve(session, request.http.req.raw);
56
+ if (scope)
57
+ request.keyScope = scope;
58
+ }
59
+ await next();
60
+ });
61
+ }
62
+ shutdown() {
63
+ for (const release of this.releases.splice(0))
64
+ release();
65
+ return Promise.resolve();
66
+ }
67
+ }
68
+ export const serviceProviders = [
69
+ ApiKeysProvider,
70
+ ];
71
+ export default serviceProviders;
72
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/providers/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /** The error domain of the API keys plugin: its URL namespace. */
2
+ export declare const API_KEYS_ERROR_DOMAIN = "apiKeys";
3
+ /**
4
+ * The plugin's own errors as `ApiError`, domain `apiKeys`, with their code as the reason; anything else is returned
5
+ * unchanged for `apiErrorHandler`. Exported so an application's own key routes answer the same way.
6
+ */
7
+ export declare function toApiKeysApiError(error: unknown): unknown;
8
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../server/routes/errors.ts"],"names":[],"mappings":"AAKA,kEAAkE;AAClE,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAe/C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAwBzD"}
@@ -0,0 +1,48 @@
1
+ import { ApiError } from '@nocobase/app-server/router';
2
+ import { ApiKeyRequestError } from '../scoped-keys.js';
3
+ import { ApiKeyScopeError } from '../scopes.js';
4
+ /** The error domain of the API keys plugin: its URL namespace. */
5
+ export const API_KEYS_ERROR_DOMAIN = 'apiKeys';
6
+ const FIELD_OF = {
7
+ EXPIRY_REQUIRED: 'expiresInDays',
8
+ INVALID_EXPIRY: 'expiresInDays',
9
+ };
10
+ function statusOf(error) {
11
+ if (error.status === 403)
12
+ return 'PERMISSION_DENIED';
13
+ if (error.status === 404)
14
+ return 'NOT_FOUND';
15
+ return error.code === 'KEY_NOT_SCOPED'
16
+ ? 'FAILED_PRECONDITION'
17
+ : 'INVALID_ARGUMENT';
18
+ }
19
+ /**
20
+ * The plugin's own errors as `ApiError`, domain `apiKeys`, with their code as the reason; anything else is returned
21
+ * unchanged for `apiErrorHandler`. Exported so an application's own key routes answer the same way.
22
+ */
23
+ export function toApiKeysApiError(error) {
24
+ if (error instanceof ApiKeyScopeError)
25
+ return new ApiError({
26
+ status: 'INVALID_ARGUMENT',
27
+ reason: error.code,
28
+ domain: API_KEYS_ERROR_DOMAIN,
29
+ message: error.message,
30
+ fieldViolations: [{ field: 'scope', description: error.message }],
31
+ cause: error,
32
+ });
33
+ if (error instanceof ApiKeyRequestError) {
34
+ const field = FIELD_OF[error.code];
35
+ return new ApiError({
36
+ status: statusOf(error),
37
+ reason: error.code,
38
+ domain: API_KEYS_ERROR_DOMAIN,
39
+ message: error.message,
40
+ ...(field
41
+ ? { fieldViolations: [{ field, description: error.message }] }
42
+ : {}),
43
+ cause: error,
44
+ });
45
+ }
46
+ return error;
47
+ }
48
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../../server/routes/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAuB,MAAM,6BAA6B,CAAC;AAE5E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhD,kEAAkE;AAClE,MAAM,CAAC,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAE/C,MAAM,QAAQ,GAAqC;IACjD,eAAe,EAAE,eAAe;IAChC,cAAc,EAAE,eAAe;CAChC,CAAC;AAEF,SAAS,QAAQ,CAAC,KAAyB;IACzC,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,mBAAmB,CAAC;IACrD,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,WAAW,CAAC;IAC7C,OAAO,KAAK,CAAC,IAAI,KAAK,gBAAgB;QACpC,CAAC,CAAC,qBAAqB;QACvB,CAAC,CAAC,kBAAkB,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,KAAK,YAAY,gBAAgB;QACnC,OAAO,IAAI,QAAQ,CAAC;YAClB,MAAM,EAAE,kBAAkB;YAC1B,MAAM,EAAE,KAAK,CAAC,IAAI;YAClB,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,eAAe,EAAE,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YACjE,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,IAAI,KAAK,YAAY,kBAAkB,EAAE,CAAC;QACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,IAAI,QAAQ,CAAC;YAClB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC;YACvB,MAAM,EAAE,KAAK,CAAC,IAAI;YAClB,MAAM,EAAE,qBAAqB;YAC7B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,GAAG,CAAC,KAAK;gBACP,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC,EAAE;gBAC9D,CAAC,CAAC,EAAE,CAAC;YACP,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { AppPluginApplication } from '@nocobase/app-server/plugins';
2
+ import { type AppApiRouteContribution, type AppRouteContribution } from '@nocobase/app-server/router';
3
+ /**
4
+ * `/api/apiKeys`: a person's own keys, with or without a scope. Self-service: every endpoint acts on the caller's
5
+ * keys only, and only from a sign-in: a request made with any API key is refused (`requireSignInSession`).
6
+ */
7
+ export declare const apiRoutes: AppApiRouteContribution<AppPluginApplication>;
8
+ declare const routes: readonly AppRouteContribution<AppPluginApplication>[];
9
+ export default routes;
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../server/routes/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,EAWL,KAAK,uBAAuB,EAC5B,KAAK,oBAAoB,EAC1B,MAAM,6BAA6B,CAAC;AAqCrC;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,uBAAuB,CAAC,oBAAoB,CA+MhE,CAAC;AAEL,QAAA,MAAM,MAAM,EAAE,SAAS,oBAAoB,CAAC,oBAAoB,CAAC,EAEhE,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,177 @@
1
+ import { authenticationToken } from '@nocobase/app-plugin-authentication/server';
2
+ import { authorizationToken, } from '@nocobase/app-plugin-authorization';
3
+ import { apiErrorHandler, apiErrorResponse, apiErrorResponses, apiValidator, cliRoute, dataResponse, defineApiRoutes, describeRoute, emptyResponse, listResponse, } from '@nocobase/app-server/router';
4
+ import { Hono } from 'hono';
5
+ import { requireSignInSession } from '../key-sessions.js';
6
+ import { scopedApiKeysToken } from '../tokens.js';
7
+ import { toApiKeysApiError } from './errors.js';
8
+ import { ApiKeyParams, ApiKeysListMeta, ApiKeyViewSchema, CreateApiKeyInput, CreatedApiKeySchema, KeyScopeObjectSchema, KeyScopeOptionsSchema, ScopeGroupParams, ScopeObjectsQuery, } from './schemas.js';
9
+ const tags = ['ApiKeys'];
10
+ /** Every route refuses a request made with an API key: keys are managed from a sign-in. */
11
+ const signInOnly = 'Takes a signed-in session: a request made with any API key is refused (`403`).';
12
+ const keyNotFound = apiErrorResponse(404, 'The caller has no key with this id (`KEY_NOT_FOUND`).');
13
+ const creationForbidden = apiErrorResponse(403, 'The caller may not use API keys or may not create keys of their own (`API_KEY_CREATION_FORBIDDEN`).');
14
+ /**
15
+ * `/api/apiKeys`: a person's own keys, with or without a scope. Self-service: every endpoint acts on the caller's
16
+ * keys only, and only from a sign-in: a request made with any API key is refused (`requireSignInSession`).
17
+ */
18
+ export const apiRoutes = defineApiRoutes(({ container }) => {
19
+ const router = new Hono();
20
+ if (!container.has(authenticationToken))
21
+ return router;
22
+ const keys = container.resolve(scopedApiKeysToken);
23
+ const routes = new Hono();
24
+ routes.onError((error, context) => apiErrorHandler(toApiKeysApiError(error), context));
25
+ routes.use('*', container.resolve(authenticationToken).required());
26
+ // Managing keys takes a sign-in: no key, scoped or not, creates, rotates or revokes keys.
27
+ routes.use('*', requireSignInSession());
28
+ if (container.has(authorizationToken))
29
+ routes.use('*', container.resolve(authorizationToken).middleware());
30
+ const userId = (context) => context.get('auth').user.id;
31
+ const identity = async (context) => context.get('authz')?.identity ??
32
+ (await keys.identityOf(userId(context)));
33
+ // Creating and rotating are refused before the input is read when the person may not create keys.
34
+ const mayCreate = async (context, next) => {
35
+ await keys.requireOwnCreation(userId(context));
36
+ await next();
37
+ };
38
+ routes.get('/scopeOptions', describeRoute({
39
+ tags,
40
+ summary: 'List what a scoped key may be limited to',
41
+ operationId: 'apiKeysListScopeOptions',
42
+ ...cliRoute({ command: 'api-key scope-options' }),
43
+ description: signInOnly,
44
+ responses: {
45
+ 200: dataResponse(KeyScopeOptionsSchema),
46
+ ...apiErrorResponses,
47
+ },
48
+ }), async (context) => context.json({
49
+ data: {
50
+ ...(await keys.scopeOptions(userId(context))),
51
+ mayCreate: await keys.mayCreateOwn(userId(context)),
52
+ },
53
+ }));
54
+ // The records a person may pick for a group: bounded by what they see, and narrowed by `q` or `id`.
55
+ routes.get('/scopeObjects/:group', describeRoute({
56
+ tags,
57
+ summary: 'List the records a scope group may be limited to',
58
+ operationId: 'apiKeysListScopeObjects',
59
+ ...cliRoute({
60
+ command: 'api-key scope-objects',
61
+ columns: ['id', 'title', 'description'],
62
+ examples: ['api-key scope-objects pm.projects --q checkout'],
63
+ }),
64
+ description: `${signInOnly} Bounded by what the caller can see and narrowed by \`q\` or repeated \`id\`; not paged.`,
65
+ responses: {
66
+ 200: listResponse(KeyScopeObjectSchema, ApiKeysListMeta),
67
+ ...apiErrorResponses,
68
+ 404: apiErrorResponse(404, 'The scope group does not exist (`UNKNOWN_SCOPE_GROUP`).'),
69
+ },
70
+ }), apiValidator('param', ScopeGroupParams), apiValidator('query', ScopeObjectsQuery), async (context) => {
71
+ const { group } = context.req.valid('param');
72
+ const { q, id } = context.req.valid('query');
73
+ const data = await keys.scopeObjects(group, await identity(context), {
74
+ ...(q ? { search: q } : {}),
75
+ ...(id ? { ids: id } : {}),
76
+ });
77
+ return context.json({ data, meta: { total: data.length } });
78
+ });
79
+ // A person's own keys: a bounded list, not paged.
80
+ routes.get('/', describeRoute({
81
+ tags,
82
+ summary: 'List my API keys',
83
+ operationId: 'apiKeysListKeys',
84
+ ...cliRoute({
85
+ command: 'api-key list',
86
+ columns: [
87
+ 'id',
88
+ 'name',
89
+ 'start',
90
+ 'enabled',
91
+ 'expiresAt',
92
+ 'lastUsedAt',
93
+ ],
94
+ }),
95
+ description: `${signInOnly} Never returns a key's secret.`,
96
+ responses: {
97
+ 200: listResponse(ApiKeyViewSchema, ApiKeysListMeta),
98
+ ...apiErrorResponses,
99
+ },
100
+ }), async (context) => {
101
+ const data = await keys.list(userId(context));
102
+ return context.json({ data, meta: { total: data.length } });
103
+ });
104
+ routes.post('/', mayCreate, describeRoute({
105
+ tags,
106
+ summary: 'Create an API key',
107
+ operationId: 'apiKeysCreateKey',
108
+ ...cliRoute({
109
+ command: 'api-key create',
110
+ examples: [
111
+ 'api-key create --name ci --expires-in-days 90',
112
+ 'api-key create --file key.json',
113
+ ],
114
+ bodyFile: 'file',
115
+ }),
116
+ description: `${signInOnly} The secret is in this response only. \`scope: null\` makes a key that acts as the caller in full.`,
117
+ responses: {
118
+ 201: dataResponse(CreatedApiKeySchema, 'The created key.'),
119
+ ...apiErrorResponses,
120
+ 403: creationForbidden,
121
+ 400: apiErrorResponse(400, 'The expiry or the scope is not allowed (`EXPIRY_REQUIRED`, `INVALID_EXPIRY`, `INVALID_SCOPE`, `EMPTY_SCOPE`, `UNKNOWN_SCOPE_GROUP`, `SCOPE_LEVEL_NOT_OFFERED`, `SCOPE_OBJECTS_NOT_OFFERED`, `UNKNOWN_SCOPE_OBJECT`).'),
122
+ },
123
+ }), apiValidator('json', CreateApiKeyInput), async (context) => {
124
+ context.header('Cache-Control', 'no-store');
125
+ return context.json({
126
+ data: await keys.create(userId(context), context.req.valid('json'), await identity(context)),
127
+ }, 201);
128
+ });
129
+ routes.post('/:keyId/rotate', mayCreate, describeRoute({
130
+ tags,
131
+ summary: 'Rotate an API key',
132
+ operationId: 'apiKeysRotateKey',
133
+ ...cliRoute({
134
+ command: 'api-key rotate',
135
+ flags: { keyId: { name: 'key' } },
136
+ confirm: 'Rotate this key? The old secret stops working.',
137
+ }),
138
+ description: `${signInOnly} Replaces the key with a new secret, keeping its name, scope and lifetime; the old secret stops working.`,
139
+ responses: {
140
+ 200: dataResponse(CreatedApiKeySchema),
141
+ ...apiErrorResponses,
142
+ 403: creationForbidden,
143
+ 404: keyNotFound,
144
+ },
145
+ }), apiValidator('param', ApiKeyParams), async (context) => {
146
+ context.header('Cache-Control', 'no-store');
147
+ return context.json({
148
+ data: await keys.rotate(userId(context), context.req.valid('param').keyId),
149
+ });
150
+ });
151
+ routes.delete('/:keyId', describeRoute({
152
+ tags,
153
+ summary: 'Revoke an API key',
154
+ operationId: 'apiKeysRevokeKey',
155
+ ...cliRoute({
156
+ command: 'api-key delete',
157
+ flags: { keyId: { name: 'key' } },
158
+ confirm: 'Revoke this key? Whatever uses it stops working.',
159
+ }),
160
+ description: signInOnly,
161
+ responses: {
162
+ 204: emptyResponse('The key was revoked.'),
163
+ ...apiErrorResponses,
164
+ 404: keyNotFound,
165
+ },
166
+ }), apiValidator('param', ApiKeyParams), async (context) => {
167
+ await keys.revoke(userId(context), context.req.valid('param').keyId);
168
+ return context.body(null, 204);
169
+ });
170
+ router.route('/apiKeys', routes);
171
+ return router;
172
+ });
173
+ const routes = [
174
+ apiRoutes,
175
+ ];
176
+ export default routes;
177
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../server/routes/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EACL,kBAAkB,GAEnB,MAAM,oCAAoC,CAAC;AAG5C,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,YAAY,EACZ,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,aAAa,EACb,aAAa,EACb,YAAY,GAGb,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,IAAI,EAAwC,MAAM,MAAM,CAAC;AAElE,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,oBAAoB,EACpB,qBAAqB,EACrB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,cAAc,CAAC;AAEtB,MAAM,IAAI,GAAG,CAAC,SAAS,CAAC,CAAC;AAEzB,2FAA2F;AAC3F,MAAM,UAAU,GACd,gFAAgF,CAAC;AAEnF,MAAM,WAAW,GAAG,gBAAgB,CAClC,GAAG,EACH,uDAAuD,CACxD,CAAC;AACF,MAAM,iBAAiB,GAAG,gBAAgB,CACxC,GAAG,EACH,qGAAqG,CACtG,CAAC;AAMF;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GACpB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE;IAChC,MAAM,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;IAC1B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,mBAAmB,CAAC;QAAE,OAAO,MAAM,CAAC;IACvD,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACnD,MAAM,MAAM,GAAG,IAAI,IAAI,EAAO,CAAC;IAC/B,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,CAChC,eAAe,CAAC,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,CACnD,CAAC;IACF,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,mBAAmB,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnE,0FAA0F;IAC1F,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,oBAAoB,EAAE,CAAC,CAAC;IACxC,IAAI,SAAS,CAAC,GAAG,CAAC,kBAAkB,CAAC;QACnC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,SAAS,CAAC,OAAO,CAAC,kBAAkB,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IACtE,MAAM,MAAM,GAAG,CAAC,OAAqB,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC,IAAI,CAAC,EAAE,CAAC;IACvE,MAAM,QAAQ,GAAG,KAAK,EAAE,OAAqB,EAAE,EAAE,CAC/C,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,QAAQ;QAC9B,CAAC,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3C,kGAAkG;IAClG,MAAM,SAAS,GAA2B,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE;QAChE,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/C,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;IAEF,MAAM,CAAC,GAAG,CACR,eAAe,EACf,aAAa,CAAC;QACZ,IAAI;QACJ,OAAO,EAAE,0CAA0C;QACnD,WAAW,EAAE,yBAAyB;QACtC,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,uBAAuB,EAAE,CAAC;QACjD,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE;YACT,GAAG,EAAE,YAAY,CAAC,qBAAqB,CAAC;YACxC,GAAG,iBAAiB;SACrB;KACF,CAAC,EACF,KAAK,EAAE,OAAO,EAAE,EAAE,CAChB,OAAO,CAAC,IAAI,CAAC;QACX,IAAI,EAAE;YACJ,GAAG,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC7C,SAAS,EAAE,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;SACpD;KACF,CAAC,CACL,CAAC;IACF,oGAAoG;IACpG,MAAM,CAAC,GAAG,CACR,sBAAsB,EACtB,aAAa,CAAC;QACZ,IAAI;QACJ,OAAO,EAAE,kDAAkD;QAC3D,WAAW,EAAE,yBAAyB;QACtC,GAAG,QAAQ,CAAC;YACV,OAAO,EAAE,uBAAuB;YAChC,OAAO,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,aAAa,CAAC;YACvC,QAAQ,EAAE,CAAC,gDAAgD,CAAC;SAC7D,CAAC;QACF,WAAW,EAAE,GAAG,UAAU,0FAA0F;QACpH,SAAS,EAAE;YACT,GAAG,EAAE,YAAY,CAAC,oBAAoB,EAAE,eAAe,CAAC;YACxD,GAAG,iBAAiB;YACpB,GAAG,EAAE,gBAAgB,CACnB,GAAG,EACH,yDAAyD,CAC1D;SACF;KACF,CAAC,EACF,YAAY,CAAC,OAAO,EAAE,gBAAgB,CAAC,EACvC,YAAY,CAAC,OAAO,EAAE,iBAAiB,CAAC,EACxC,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,EAAE,CAAC,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,QAAQ,CAAC,OAAO,CAAC,EAAE;YACnE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC3B,CAAC,CAAC;QACH,OAAO,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC,CACF,CAAC;IACF,kDAAkD;IAClD,MAAM,CAAC,GAAG,CACR,GAAG,EACH,aAAa,CAAC;QACZ,IAAI;QACJ,OAAO,EAAE,kBAAkB;QAC3B,WAAW,EAAE,iBAAiB;QAC9B,GAAG,QAAQ,CAAC;YACV,OAAO,EAAE,cAAc;YACvB,OAAO,EAAE;gBACP,IAAI;gBACJ,MAAM;gBACN,OAAO;gBACP,SAAS;gBACT,WAAW;gBACX,YAAY;aACb;SACF,CAAC;QACF,WAAW,EAAE,GAAG,UAAU,gCAAgC;QAC1D,SAAS,EAAE;YACT,GAAG,EAAE,YAAY,CAAC,gBAAgB,EAAE,eAAe,CAAC;YACpD,GAAG,iBAAiB;SACrB;KACF,CAAC,EACF,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;QAC9C,OAAO,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC9D,CAAC,CACF,CAAC;IACF,MAAM,CAAC,IAAI,CACT,GAAG,EACH,SAAS,EACT,aAAa,CAAC;QACZ,IAAI;QACJ,OAAO,EAAE,mBAAmB;QAC5B,WAAW,EAAE,kBAAkB;QAC/B,GAAG,QAAQ,CAAC;YACV,OAAO,EAAE,gBAAgB;YACzB,QAAQ,EAAE;gBACR,+CAA+C;gBAC/C,gCAAgC;aACjC;YACD,QAAQ,EAAE,MAAM;SACjB,CAAC;QACF,WAAW,EAAE,GAAG,UAAU,oGAAoG;QAC9H,SAAS,EAAE;YACT,GAAG,EAAE,YAAY,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;YAC1D,GAAG,iBAAiB;YACpB,GAAG,EAAE,iBAAiB;YACtB,GAAG,EAAE,gBAAgB,CACnB,GAAG,EACH,sNAAsN,CACvN;SACF;KACF,CAAC,EACF,YAAY,CAAC,MAAM,EAAE,iBAAiB,CAAC,EACvC,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAC5C,OAAO,OAAO,CAAC,IAAI,CACjB;YACE,IAAI,EAAE,MAAM,IAAI,CAAC,MAAM,CACrB,MAAM,CAAC,OAAO,CAAC,EACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,EACzB,MAAM,QAAQ,CAAC,OAAO,CAAC,CACxB;SACF,EACD,GAAG,CACJ,CAAC;IACJ,CAAC,CACF,CAAC;IACF,MAAM,CAAC,IAAI,CACT,gBAAgB,EAChB,SAAS,EACT,aAAa,CAAC;QACZ,IAAI;QACJ,OAAO,EAAE,mBAAmB;QAC5B,WAAW,EAAE,kBAAkB;QAC/B,GAAG,QAAQ,CAAC;YACV,OAAO,EAAE,gBAAgB;YACzB,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YACjC,OAAO,EAAE,gDAAgD;SAC1D,CAAC;QACF,WAAW,EAAE,GAAG,UAAU,0GAA0G;QACpI,SAAS,EAAE;YACT,GAAG,EAAE,YAAY,CAAC,mBAAmB,CAAC;YACtC,GAAG,iBAAiB;YACpB,GAAG,EAAE,iBAAiB;YACtB,GAAG,EAAE,WAAW;SACjB;KACF,CAAC,EACF,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,EACnC,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,OAAO,CAAC,MAAM,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAC5C,OAAO,OAAO,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,MAAM,IAAI,CAAC,MAAM,CACrB,MAAM,CAAC,OAAO,CAAC,EACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CACjC;SACF,CAAC,CAAC;IACL,CAAC,CACF,CAAC;IACF,MAAM,CAAC,MAAM,CACX,SAAS,EACT,aAAa,CAAC;QACZ,IAAI;QACJ,OAAO,EAAE,mBAAmB;QAC5B,WAAW,EAAE,kBAAkB;QAC/B,GAAG,QAAQ,CAAC;YACV,OAAO,EAAE,gBAAgB;YACzB,KAAK,EAAE,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE;YACjC,OAAO,EAAE,kDAAkD;SAC5D,CAAC;QACF,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE;YACT,GAAG,EAAE,aAAa,CAAC,sBAAsB,CAAC;YAC1C,GAAG,iBAAiB;YACpB,GAAG,EAAE,WAAW;SACjB;KACF,CAAC,EACF,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,EACnC,KAAK,EAAE,OAAO,EAAE,EAAE;QAChB,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC;QACrE,OAAO,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACjC,CAAC,CACF,CAAC;IAEF,MAAM,CAAC,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC,CAAC;AAEL,MAAM,MAAM,GAA0D;IACpE,SAAS;CACV,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ import { type ApiKeyView, type CreateApiKeyRequest, type CreatedApiKey, type KeyScopeObject, type KeyScopeOptions, type KeyScopeInput } from '../../shared/scopes.js';
3
+ /** A key's scope; which groups exist and what they offer is checked by `ApiKeyScopes.validate`. */
4
+ export declare const KeyScopeInputSchema: z.ZodType<KeyScopeInput>;
5
+ /** The body of `POST /apiKeys`. */
6
+ export declare const CreateApiKeyInput: z.ZodType<CreateApiKeyRequest>;
7
+ export declare const ApiKeyParams: z.ZodObject<{
8
+ keyId: z.ZodString;
9
+ }>;
10
+ export declare const ScopeGroupParams: z.ZodObject<{
11
+ group: z.ZodString;
12
+ }>;
13
+ /** The query of `GET /apiKeys/scopeObjects/:group`: a search, or the records to look up by id (`id` repeated). */
14
+ export declare const ScopeObjectsQuery: z.ZodType<{
15
+ q?: string | undefined;
16
+ id?: string[] | undefined;
17
+ }>;
18
+ export declare const KeyScopeOptionsSchema: z.ZodType<KeyScopeOptions>;
19
+ export declare const KeyScopeObjectSchema: z.ZodType<KeyScopeObject>;
20
+ export declare const ApiKeyViewSchema: z.ZodType<ApiKeyView>;
21
+ export declare const CreatedApiKeySchema: z.ZodType<CreatedApiKey>;
22
+ export declare const ApiKeysListMeta: z.ZodType<{
23
+ total: number;
24
+ }>;
25
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../../server/routes/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,mBAAmB,EACxB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,eAAe,EAGpB,KAAK,aAAa,EAEnB,MAAM,wBAAwB,CAAC;AAahC,mGAAmG;AACnG,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAEvD,CAAC;AAEH,mCAAmC;AACnC,eAAO,MAAM,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAO5D,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,CAAC,CAAC,SAAS,CAAC;IAAE,KAAK,EAAE,CAAC,CAAC,SAAS,CAAA;CAAE,CAE3D,CAAC;AAEH,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,SAAS,CAAC;IAAE,KAAK,EAAE,CAAC,CAAC,SAAS,CAAA;CAAE,CAE/D,CAAC;AAIH,kHAAkH;AAClH,eAAO,MAAM,iBAAiB,EAAE,CAAC,CAAC,OAAO,CAAC;IACxC,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvB,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC3B,CAQC,CAAC;AAgCH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CA2C3D,CAAC;AAEH,eAAO,MAAM,oBAAoB,EAAE,CAAC,CAAC,OAAO,CAAC,cAAc,CAIzD,CAAC;AAEH,eAAO,MAAM,gBAAgB,EAAE,CAAC,CAAC,OAAO,CAAC,UAAU,CAgBrB,CAAC;AAE/B,eAAO,MAAM,mBAAmB,EAAE,CAAC,CAAC,OAAO,CAAC,aAAa,CAOpB,CAAC;AAEtC,eAAO,MAAM,eAAe,EAAE,CAAC,CAAC,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAEvD,CAAC"}
@@ -0,0 +1,127 @@
1
+ import { z } from 'zod';
2
+ import { KEY_SCOPE_LEVELS, } from '../../shared/scopes.js';
3
+ const KeyScopeLevelSchema = z.enum(KEY_SCOPE_LEVELS);
4
+ const KeyScopeGroupGrantSchema = z.strictObject({
5
+ level: KeyScopeLevelSchema,
6
+ objects: z
7
+ .union([z.literal('all'), z.array(z.string().min(1)).min(1)])
8
+ .optional(),
9
+ });
10
+ /** A key's scope; which groups exist and what they offer is checked by `ApiKeyScopes.validate`. */
11
+ export const KeyScopeInputSchema = z.strictObject({
12
+ groups: z.record(z.string().min(1), KeyScopeGroupGrantSchema),
13
+ });
14
+ /** The body of `POST /apiKeys`. */
15
+ export const CreateApiKeyInput = z.strictObject({
16
+ name: z.string(),
17
+ description: z.string().nullable().optional(),
18
+ expiresInDays: z.number().int().nullable(),
19
+ scope: KeyScopeInputSchema.nullable(),
20
+ });
21
+ export const ApiKeyParams = z.object({
22
+ keyId: z.string().min(1),
23
+ });
24
+ export const ScopeGroupParams = z.object({
25
+ group: z.string().min(1),
26
+ });
27
+ const MAX_PICKED_IDS = 100;
28
+ /** The query of `GET /apiKeys/scopeObjects/:group`: a search, or the records to look up by id (`id` repeated). */
29
+ export const ScopeObjectsQuery = z.object({
30
+ q: z.string().max(200).optional(),
31
+ id: z
32
+ .union([z.string().min(1), z.array(z.string().min(1)).max(MAX_PICKED_IDS)])
33
+ .optional()
34
+ .transform((value) => value === undefined ? undefined : Array.isArray(value) ? value : [value]),
35
+ });
36
+ // Response schemas: they document what the routes send; nothing validates a response against them.
37
+ const dateTime = () => z.string().meta({ format: 'date-time' });
38
+ const LocalizedTextSchema = z
39
+ .union([z.string(), z.object({ key: z.string(), ns: z.string() })])
40
+ .meta({
41
+ ref: 'ApiKeysLocalizedText',
42
+ description: 'Plain text, or an i18n key in a namespace.',
43
+ });
44
+ const AccessRefSchema = z.discriminatedUnion('kind', [
45
+ z.object({ kind: z.literal('page'), id: z.string() }),
46
+ z.object({ kind: z.literal('settings'), id: z.string(), action: z.string() }),
47
+ z.object({ kind: z.literal('business'), id: z.string(), action: z.string() }),
48
+ ]);
49
+ const GrantResponseSchema = z.object({
50
+ level: KeyScopeLevelSchema,
51
+ objects: z.union([z.literal('all'), z.array(z.string())]).optional(),
52
+ });
53
+ const KeyScopeResponseSchema = z
54
+ .object({ groups: z.record(z.string(), GrantResponseSchema) })
55
+ .meta({
56
+ ref: 'ApiKeysKeyScope',
57
+ description: 'What the key may do, group by group; a group left out grants nothing.',
58
+ });
59
+ export const KeyScopeOptionsSchema = z.object({
60
+ groups: z
61
+ .array(z.object({
62
+ id: z.string(),
63
+ category: z.enum(['business', 'administration', 'account']),
64
+ title: LocalizedTextSchema,
65
+ description: LocalizedTextSchema.nullable(),
66
+ levels: z.array(KeyScopeLevelSchema),
67
+ access: z.partialRecord(KeyScopeLevelSchema, z.array(AccessRefSchema)),
68
+ held: z.partialRecord(KeyScopeLevelSchema, z.boolean()).meta({
69
+ description: 'Whether the caller holds everything up to each level; false shows it greyed out.',
70
+ }),
71
+ objects: z
72
+ .object({ business: z.string(), title: LocalizedTextSchema })
73
+ .nullable(),
74
+ }))
75
+ .meta({ description: 'Empty when scoped keys are not offered.' }),
76
+ presets: z.array(z.object({
77
+ id: z.string(),
78
+ title: LocalizedTextSchema,
79
+ description: LocalizedTextSchema.nullable(),
80
+ groups: z.record(z.string(), z.object({
81
+ level: KeyScopeLevelSchema,
82
+ objects: z.enum(['all', 'pick']).optional(),
83
+ })),
84
+ expiresInDays: z.number().int().nullable(),
85
+ })),
86
+ maxScopedKeyDays: z.number().int().nullable().meta({
87
+ description: 'The longest a scoped key may live, in days; null when "never expires" is allowed.',
88
+ }),
89
+ defaultExpiresInDays: z.number().int(),
90
+ mayCreate: z.boolean().optional().meta({
91
+ description: 'Whether the caller may create keys of their own.',
92
+ }),
93
+ });
94
+ export const KeyScopeObjectSchema = z.object({
95
+ id: z.string(),
96
+ title: z.string(),
97
+ description: z.string().optional(),
98
+ });
99
+ export const ApiKeyViewSchema = z
100
+ .object({
101
+ id: z.string(),
102
+ name: z.string().nullable(),
103
+ description: z.string().nullable(),
104
+ start: z.string().nullable().meta({
105
+ description: 'The first characters of the key, to recognize it.',
106
+ }),
107
+ enabled: z.boolean(),
108
+ createdAt: dateTime(),
109
+ expiresAt: dateTime().nullable(),
110
+ lastUsedAt: dateTime().nullable(),
111
+ scope: KeyScopeResponseSchema.nullable().meta({
112
+ description: 'Null for a key that acts as its owner in full.',
113
+ }),
114
+ })
115
+ .meta({ ref: 'ApiKeysKey' });
116
+ export const CreatedApiKeySchema = z
117
+ .object({
118
+ key: ApiKeyViewSchema,
119
+ secret: z.string().meta({
120
+ description: 'The key itself, shown only in this response.',
121
+ }),
122
+ })
123
+ .meta({ ref: 'ApiKeysCreatedKey' });
124
+ export const ApiKeysListMeta = z.object({
125
+ total: z.number().int(),
126
+ });
127
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../../server/routes/schemas.ts"],"names":[],"mappings":"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"}