fluxmail 0.2.1 → 0.4.0

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 (88) hide show
  1. package/README.md +15 -51
  2. package/dist/accounts/gmailConnection.d.ts +5 -1
  3. package/dist/accounts/gmailConnection.d.ts.map +1 -1
  4. package/dist/accounts/gmailConnection.js +14 -2
  5. package/dist/accounts/gmailConnection.js.map +1 -1
  6. package/dist/accounts/googleAuth.js +1 -1
  7. package/dist/accounts/googleAuth.js.map +1 -1
  8. package/dist/accounts/microsoftAuth.d.ts +21 -0
  9. package/dist/accounts/microsoftAuth.d.ts.map +1 -0
  10. package/dist/accounts/microsoftAuth.js +220 -0
  11. package/dist/accounts/microsoftAuth.js.map +1 -0
  12. package/dist/accounts/registry.d.ts +21 -8
  13. package/dist/accounts/registry.d.ts.map +1 -1
  14. package/dist/accounts/registry.js +190 -24
  15. package/dist/accounts/registry.js.map +1 -1
  16. package/dist/cli.d.ts +13 -0
  17. package/dist/cli.d.ts.map +1 -1
  18. package/dist/cli.js +881 -572
  19. package/dist/cli.js.map +1 -1
  20. package/dist/config.d.ts +105 -1
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +128 -13
  23. package/dist/config.js.map +1 -1
  24. package/dist/context.d.ts +2 -0
  25. package/dist/context.d.ts.map +1 -1
  26. package/dist/context.js +7 -1
  27. package/dist/context.js.map +1 -1
  28. package/dist/http/admin.d.ts +21 -0
  29. package/dist/http/admin.d.ts.map +1 -0
  30. package/dist/http/admin.js +618 -0
  31. package/dist/http/admin.js.map +1 -0
  32. package/dist/http/app.d.ts +2 -0
  33. package/dist/http/app.d.ts.map +1 -1
  34. package/dist/http/app.js +332 -45
  35. package/dist/http/app.js.map +1 -1
  36. package/dist/http/rest.d.ts +28 -0
  37. package/dist/http/rest.d.ts.map +1 -0
  38. package/dist/http/rest.js +1130 -0
  39. package/dist/http/rest.js.map +1 -0
  40. package/dist/index.d.ts +4 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +4 -2
  43. package/dist/index.js.map +1 -1
  44. package/dist/licensing/refresher.d.ts +23 -7
  45. package/dist/licensing/refresher.d.ts.map +1 -1
  46. package/dist/licensing/refresher.js +93 -37
  47. package/dist/licensing/refresher.js.map +1 -1
  48. package/dist/mcp/buildServer.d.ts +4 -2
  49. package/dist/mcp/buildServer.d.ts.map +1 -1
  50. package/dist/mcp/buildServer.js +286 -230
  51. package/dist/mcp/buildServer.js.map +1 -1
  52. package/dist/permissions.d.ts +30 -0
  53. package/dist/permissions.d.ts.map +1 -0
  54. package/dist/permissions.js +119 -0
  55. package/dist/permissions.js.map +1 -0
  56. package/dist/service/emailService.d.ts +16 -9
  57. package/dist/service/emailService.d.ts.map +1 -1
  58. package/dist/service/emailService.js +44 -19
  59. package/dist/service/emailService.js.map +1 -1
  60. package/dist/storage/adminAudit.d.ts +14 -0
  61. package/dist/storage/adminAudit.d.ts.map +1 -0
  62. package/dist/storage/adminAudit.js +32 -0
  63. package/dist/storage/adminAudit.js.map +1 -0
  64. package/dist/storage/apiKeys.d.ts +28 -5
  65. package/dist/storage/apiKeys.d.ts.map +1 -1
  66. package/dist/storage/apiKeys.js +153 -10
  67. package/dist/storage/apiKeys.js.map +1 -1
  68. package/dist/storage/db.d.ts +524 -0
  69. package/dist/storage/db.d.ts.map +1 -1
  70. package/dist/storage/db.js +184 -8
  71. package/dist/storage/db.js.map +1 -1
  72. package/dist/storage/gmailConnectionGrants.d.ts +9 -0
  73. package/dist/storage/gmailConnectionGrants.d.ts.map +1 -1
  74. package/dist/storage/gmailConnectionGrants.js +30 -7
  75. package/dist/storage/gmailConnectionGrants.js.map +1 -1
  76. package/dist/storage/members.d.ts +7 -4
  77. package/dist/storage/members.d.ts.map +1 -1
  78. package/dist/storage/members.js +34 -8
  79. package/dist/storage/members.js.map +1 -1
  80. package/dist/storage/restIdempotency.d.ts +28 -0
  81. package/dist/storage/restIdempotency.d.ts.map +1 -0
  82. package/dist/storage/restIdempotency.js +47 -0
  83. package/dist/storage/restIdempotency.js.map +1 -0
  84. package/dist/telemetry.d.ts +9 -2
  85. package/dist/telemetry.d.ts.map +1 -1
  86. package/dist/telemetry.js +117 -54
  87. package/dist/telemetry.js.map +1 -1
  88. package/package.json +7 -5
@@ -0,0 +1,32 @@
1
+ import { asc, eq } from 'drizzle-orm';
2
+ import { adminAuditEvents } from './db.js';
3
+ export const ADMIN_AUDIT_RETENTION = 10_000;
4
+ /** Store identifiers and stable codes only. Callers must not pass request data or error text. */
5
+ export function recordAdminAuditEvent(db, input) {
6
+ db.transaction((tx) => {
7
+ tx.insert(adminAuditEvents)
8
+ .values({
9
+ timestamp: Date.now(),
10
+ operation: input.operation,
11
+ outcome: input.outcome,
12
+ actorKeyId: input.actorKeyId,
13
+ actorMemberId: input.actorMemberId,
14
+ resourceType: input.resourceType ?? null,
15
+ resourceId: input.resourceId ?? null,
16
+ errorCode: input.errorCode ?? null,
17
+ })
18
+ .run();
19
+ const excess = tx.select({ id: adminAuditEvents.id }).from(adminAuditEvents).all().length - ADMIN_AUDIT_RETENTION;
20
+ if (excess <= 0)
21
+ return;
22
+ const oldest = tx
23
+ .select({ id: adminAuditEvents.id })
24
+ .from(adminAuditEvents)
25
+ .orderBy(asc(adminAuditEvents.id))
26
+ .limit(excess)
27
+ .all();
28
+ for (const event of oldest)
29
+ tx.delete(adminAuditEvents).where(eq(adminAuditEvents.id, event.id)).run();
30
+ });
31
+ }
32
+ //# sourceMappingURL=adminAudit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminAudit.js","sourceRoot":"","sources":["../../src/storage/adminAudit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACtC,OAAO,EAAE,gBAAgB,EAAmB,MAAM,SAAS,CAAC;AAE5D,MAAM,CAAC,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAY5C,iGAAiG;AACjG,MAAM,UAAU,qBAAqB,CAAC,EAAc,EAAE,KAAsB;IAC1E,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE;QACpB,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC;aACxB,MAAM,CAAC;YACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;YACxC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;YACpC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;SACnC,CAAC;aACD,GAAG,EAAE,CAAC;QACT,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,GAAG,qBAAqB,CAAC;QAClH,IAAI,MAAM,IAAI,CAAC;YAAE,OAAO;QACxB,MAAM,MAAM,GAAG,EAAE;aACd,MAAM,CAAC,EAAE,EAAE,EAAE,gBAAgB,CAAC,EAAE,EAAE,CAAC;aACnC,IAAI,CAAC,gBAAgB,CAAC;aACtB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;aACjC,KAAK,CAAC,MAAM,CAAC;aACb,GAAG,EAAE,CAAC;QACT,KAAK,MAAM,KAAK,IAAI,MAAM;YAAE,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,EAAE,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;IACzG,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,27 +1,50 @@
1
1
  import { type FluxmailDb } from './db.js';
2
+ import { type MemberRole } from './members.js';
3
+ import { type AdminCapability, type Capability, type PermissionPolicy, type PermissionProfile } from '../permissions.js';
2
4
  export interface ApiKeyInfo {
3
5
  id: string;
4
6
  name: string;
5
7
  createdAt: number;
6
8
  lastUsedAt: number | null;
7
- /** Member the key was issued to; null means an unscoped admin key. */
9
+ /** Member the key was issued to; null identifies a migrated system credential. */
8
10
  memberId: string | null;
11
+ permissionProfile: PermissionProfile;
12
+ capabilities: Capability[];
13
+ supplementalCapabilities: AdminCapability[];
14
+ /** Additional mailbox narrowing. null means all mailboxes granted to the member. */
15
+ accountIds: string[] | null;
9
16
  }
10
17
  /** Returns the plaintext key exactly once; only its hash is stored. */
11
- export declare function createApiKey(db: FluxmailDb, name: string, memberId?: string): {
18
+ export declare function createApiKey(db: FluxmailDb, name: string, memberId?: string, permissions?: PermissionPolicy, accountIds?: readonly string[] | null): {
12
19
  key: string;
13
20
  info: ApiKeyInfo;
14
21
  };
15
22
  export interface ApiKeyAuth {
16
- /** Member the key was issued to; null for an unscoped admin key. */
23
+ /** Stable key id used to scope REST idempotency records. */
24
+ keyId: string;
25
+ /** Member the key was issued to; null for a migrated system credential. */
17
26
  memberId: string | null;
27
+ /** Current member role. null identifies a migrated management-only system key. */
28
+ role: MemberRole | null;
29
+ permissions: PermissionPolicy;
30
+ accountIds: string[] | null;
18
31
  }
19
32
  /**
20
- * Verify a key, record its use, and return the scope it authorizes. Callers must
21
- * honour `memberId`: a member-scoped key may only reach shared or owned mailboxes.
33
+ * Verify a key, record its use, and return its live member role and mailbox
34
+ * narrowing. Memberless migrated keys authenticate for management only.
22
35
  */
23
36
  export declare function authenticateApiKey(db: FluxmailDb, key: string): ApiKeyAuth | null;
24
37
  export declare function verifyApiKey(db: FluxmailDb, key: string): boolean;
25
38
  export declare function listApiKeys(db: FluxmailDb): ApiKeyInfo[];
39
+ export declare function updateApiKeyAccounts(db: FluxmailDb, id: string, accountIds: readonly string[] | null): boolean;
40
+ export declare function updateApiKeyPermissions(db: FluxmailDb, id: string, permissions: PermissionPolicy): boolean;
41
+ export interface ApiKeyUpdate {
42
+ permissions?: PermissionPolicy;
43
+ accountIds?: readonly string[] | null;
44
+ }
45
+ /** Replace requested key fields in one write after all validation succeeds. */
46
+ export declare function updateApiKey(db: FluxmailDb, id: string, update: ApiKeyUpdate): ApiKeyInfo | undefined;
47
+ export declare function isUsableRootKey(db: FluxmailDb, id: string): boolean;
48
+ export declare function countUsableRootKeys(db: FluxmailDb, excludingId?: string): number;
26
49
  export declare function revokeApiKey(db: FluxmailDb, id: string): boolean;
27
50
  //# sourceMappingURL=apiKeys.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"apiKeys.d.ts","sourceRoot":"","sources":["../../src/storage/apiKeys.ts"],"names":[],"mappings":"AAEA,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAGnD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,sEAAsE;IACtE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAMD,uEAAuE;AACvE,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAS/G;AAED,MAAM,WAAW,UAAU;IACzB,oEAAoE;IACpE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CASjF;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,EAAE,CAMxD;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAGhE"}
1
+ {"version":3,"file":"apiKeys.d.ts","sourceRoot":"","sources":["../../src/storage/apiKeys.ts"],"names":[],"mappings":"AAGA,OAAO,EAAW,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAa,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAOL,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,kFAAkF;IAClF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,wBAAwB,EAAE,eAAe,EAAE,CAAC;IAC5C,oFAAoF;IACpF,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC7B;AAuBD,uEAAuE;AACvE,wBAAgB,YAAY,CAC1B,EAAE,EAAE,UAAU,EACd,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,EACjB,WAAW,GAAE,gBAAyC,EACtD,UAAU,GAAE,SAAS,MAAM,EAAE,GAAG,IAAW,GAC1C;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAA;CAAE,CAuCnC;AAED,MAAM,WAAW,UAAU;IACzB,4DAA4D;IAC5D,KAAK,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,kFAAkF;IAClF,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,gBAAgB,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CAC7B;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAqBjF;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjE;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,UAAU,GAAG,UAAU,EAAE,CAwBxD;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,GAAG,OAAO,CAO9G;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,GAAG,OAAO,CAkB1G;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,CAAC,EAAE,gBAAgB,CAAC;IAC/B,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;CACvC;AAED,+EAA+E;AAC/E,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,UAAU,GAAG,SAAS,CAsBrG;AAED,wBAAgB,eAAe,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAcnE;AAED,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,UAAU,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAMhF;AAED,wBAAgB,YAAY,CAAC,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAGhE"}
@@ -1,25 +1,71 @@
1
1
  import { createHash, randomBytes } from 'node:crypto';
2
2
  import { eq } from 'drizzle-orm';
3
+ import { EmailError } from '@fluxmail/core';
3
4
  import { apiKeys } from './db.js';
4
5
  import { getMember } from './members.js';
6
+ import { deserializePermissionPolicy, FULL_PERMISSION_POLICY, normalizePermissionPolicy, serializeCustomCapabilities, serializeSupplementalCapabilities, hasCapability, } from '../permissions.js';
7
+ function uniqueAccountIds(accountIds) {
8
+ return [...new Set(accountIds)];
9
+ }
10
+ function serializeAccountIds(accountIds) {
11
+ return accountIds === null ? null : JSON.stringify(uniqueAccountIds(accountIds));
12
+ }
13
+ function deserializeAccountIds(value) {
14
+ if (value === null)
15
+ return null;
16
+ const parsed = JSON.parse(value);
17
+ if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === 'string')) {
18
+ throw new Error('API key account scope must be a JSON array of strings.');
19
+ }
20
+ return uniqueAccountIds(parsed);
21
+ }
5
22
  function hashKey(key) {
6
23
  return createHash('sha256').update(key).digest('hex');
7
24
  }
8
25
  /** Returns the plaintext key exactly once; only its hash is stored. */
9
- export function createApiKey(db, name, memberId) {
10
- if (memberId)
11
- getMember(db, memberId);
26
+ export function createApiKey(db, name, memberId, permissions = FULL_PERMISSION_POLICY, accountIds = null) {
27
+ if (!memberId) {
28
+ throw new EmailError('invalid_request', 'A member is required when creating an API key.');
29
+ }
30
+ const member = getMember(db, memberId);
31
+ const policy = normalizePermissionPolicy(permissions);
32
+ if (member.role !== 'admin' && policy.capabilities.some((capability) => capability.startsWith('admin.'))) {
33
+ throw new EmailError('permission_denied', 'Administrative capabilities can only be issued to an admin member.');
34
+ }
12
35
  const id = `key_${randomBytes(8).toString('hex')}`;
13
36
  const key = `fmk_${randomBytes(24).toString('hex')}`;
14
37
  const createdAt = Date.now();
15
38
  db.insert(apiKeys)
16
- .values({ id, name, keyHash: hashKey(key), createdAt, memberId: memberId ?? null })
39
+ .values({
40
+ id,
41
+ name,
42
+ keyHash: hashKey(key),
43
+ createdAt,
44
+ memberId,
45
+ permissionProfile: policy.profile,
46
+ customCapabilities: serializeCustomCapabilities(policy),
47
+ supplementalCapabilities: serializeSupplementalCapabilities(policy),
48
+ accountIds: serializeAccountIds(accountIds),
49
+ })
17
50
  .run();
18
- return { key, info: { id, name, createdAt, lastUsedAt: null, memberId: memberId ?? null } };
51
+ return {
52
+ key,
53
+ info: {
54
+ id,
55
+ name,
56
+ createdAt,
57
+ lastUsedAt: null,
58
+ memberId,
59
+ permissionProfile: policy.profile,
60
+ capabilities: policy.capabilities,
61
+ supplementalCapabilities: policy.supplementalCapabilities,
62
+ accountIds: accountIds === null ? null : uniqueAccountIds(accountIds),
63
+ },
64
+ };
19
65
  }
20
66
  /**
21
- * Verify a key, record its use, and return the scope it authorizes. Callers must
22
- * honour `memberId`: a member-scoped key may only reach shared or owned mailboxes.
67
+ * Verify a key, record its use, and return its live member role and mailbox
68
+ * narrowing. Memberless migrated keys authenticate for management only.
23
69
  */
24
70
  export function authenticateApiKey(db, key) {
25
71
  const row = db
@@ -29,8 +75,17 @@ export function authenticateApiKey(db, key) {
29
75
  .get();
30
76
  if (!row)
31
77
  return null;
32
- db.update(apiKeys).set({ lastUsedAt: Date.now() }).where(eq(apiKeys.id, row.id)).run();
33
- return { memberId: row.memberId };
78
+ let permissions;
79
+ try {
80
+ permissions = deserializePermissionPolicy(row.permissionProfile, row.customCapabilities, row.supplementalCapabilities);
81
+ const accountIds = deserializeAccountIds(row.accountIds);
82
+ const member = row.memberId ? getMember(db, row.memberId) : null;
83
+ db.update(apiKeys).set({ lastUsedAt: Date.now() }).where(eq(apiKeys.id, row.id)).run();
84
+ return { keyId: row.id, memberId: row.memberId, role: member?.role ?? null, permissions, accountIds };
85
+ }
86
+ catch {
87
+ return null;
88
+ }
34
89
  }
35
90
  export function verifyApiKey(db, key) {
36
91
  return authenticateApiKey(db, key) !== null;
@@ -40,7 +95,95 @@ export function listApiKeys(db) {
40
95
  .select()
41
96
  .from(apiKeys)
42
97
  .all()
43
- .map((r) => ({ id: r.id, name: r.name, createdAt: r.createdAt, lastUsedAt: r.lastUsedAt, memberId: r.memberId }));
98
+ .map((r) => {
99
+ const permissions = deserializePermissionPolicy(r.permissionProfile, r.customCapabilities, r.supplementalCapabilities);
100
+ const accountIds = deserializeAccountIds(r.accountIds);
101
+ return {
102
+ id: r.id,
103
+ name: r.name,
104
+ createdAt: r.createdAt,
105
+ lastUsedAt: r.lastUsedAt,
106
+ memberId: r.memberId,
107
+ permissionProfile: permissions.profile,
108
+ capabilities: permissions.capabilities,
109
+ supplementalCapabilities: permissions.supplementalCapabilities,
110
+ accountIds,
111
+ };
112
+ });
113
+ }
114
+ export function updateApiKeyAccounts(db, id, accountIds) {
115
+ const result = db
116
+ .update(apiKeys)
117
+ .set({ accountIds: serializeAccountIds(accountIds) })
118
+ .where(eq(apiKeys.id, id))
119
+ .run();
120
+ return result.changes > 0;
121
+ }
122
+ export function updateApiKeyPermissions(db, id, permissions) {
123
+ const policy = normalizePermissionPolicy(permissions);
124
+ const existing = db.select().from(apiKeys).where(eq(apiKeys.id, id)).get();
125
+ if (!existing)
126
+ return false;
127
+ const member = existing.memberId ? getMember(db, existing.memberId) : null;
128
+ if (member && member.role !== 'admin' && policy.capabilities.some((capability) => capability.startsWith('admin.'))) {
129
+ throw new EmailError('permission_denied', 'Administrative capabilities can only be issued to an admin member.');
130
+ }
131
+ const result = db
132
+ .update(apiKeys)
133
+ .set({
134
+ permissionProfile: policy.profile,
135
+ customCapabilities: serializeCustomCapabilities(policy),
136
+ supplementalCapabilities: serializeSupplementalCapabilities(policy),
137
+ })
138
+ .where(eq(apiKeys.id, id))
139
+ .run();
140
+ return result.changes > 0;
141
+ }
142
+ /** Replace requested key fields in one write after all validation succeeds. */
143
+ export function updateApiKey(db, id, update) {
144
+ const existing = db.select().from(apiKeys).where(eq(apiKeys.id, id)).get();
145
+ if (!existing)
146
+ return undefined;
147
+ const policy = update.permissions ? normalizePermissionPolicy(update.permissions) : undefined;
148
+ const member = existing.memberId ? getMember(db, existing.memberId) : null;
149
+ if (policy && member && member.role !== 'admin' && policy.capabilities.some((item) => item.startsWith('admin.'))) {
150
+ throw new EmailError('permission_denied', 'Administrative capabilities can only be issued to an admin member.');
151
+ }
152
+ db.update(apiKeys)
153
+ .set({
154
+ ...(policy
155
+ ? {
156
+ permissionProfile: policy.profile,
157
+ customCapabilities: serializeCustomCapabilities(policy),
158
+ supplementalCapabilities: serializeSupplementalCapabilities(policy),
159
+ }
160
+ : {}),
161
+ ...(update.accountIds !== undefined ? { accountIds: serializeAccountIds(update.accountIds) } : {}),
162
+ })
163
+ .where(eq(apiKeys.id, id))
164
+ .run();
165
+ return listApiKeys(db).find((key) => key.id === id);
166
+ }
167
+ export function isUsableRootKey(db, id) {
168
+ const row = db.select().from(apiKeys).where(eq(apiKeys.id, id)).get();
169
+ if (!row)
170
+ return false;
171
+ try {
172
+ const permissions = deserializePermissionPolicy(row.permissionProfile, row.customCapabilities, row.supplementalCapabilities);
173
+ if (!hasCapability(permissions, 'admin.api_keys'))
174
+ return false;
175
+ return row.memberId === null || getMember(db, row.memberId).role === 'admin';
176
+ }
177
+ catch {
178
+ return false;
179
+ }
180
+ }
181
+ export function countUsableRootKeys(db, excludingId) {
182
+ return db
183
+ .select({ id: apiKeys.id })
184
+ .from(apiKeys)
185
+ .all()
186
+ .filter((row) => row.id !== excludingId && isUsableRootKey(db, row.id)).length;
44
187
  }
45
188
  export function revokeApiKey(db, id) {
46
189
  const result = db.delete(apiKeys).where(eq(apiKeys.id, id)).run();
@@ -1 +1 @@
1
- {"version":3,"file":"apiKeys.js","sourceRoot":"","sources":["../../src/storage/apiKeys.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"apiKeys.js","sourceRoot":"","sources":["../../src/storage/apiKeys.ts"],"names":[],"mappings":"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"}