@undefineds.co/xpod 0.3.31 → 0.3.32

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 (135) hide show
  1. package/dist/api/auth/AuthContext.d.ts +3 -2
  2. package/dist/api/auth/AuthContext.js +2 -1
  3. package/dist/api/auth/AuthContext.js.map +1 -1
  4. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +2 -12
  5. package/dist/api/auth/ClientCredentialsAuthenticator.js +4 -4
  6. package/dist/api/auth/ClientCredentialsAuthenticator.js.map +1 -1
  7. package/dist/api/auth/ServiceTokenAuthenticator.d.ts +2 -2
  8. package/dist/api/auth/ServiceTokenAuthenticator.js.map +1 -1
  9. package/dist/api/container/business-token.d.ts +1 -1
  10. package/dist/api/container/business-token.js +5 -1
  11. package/dist/api/container/business-token.js.map +1 -1
  12. package/dist/api/container/common.js +14 -10
  13. package/dist/api/container/common.js.map +1 -1
  14. package/dist/api/container/routes.js +16 -3
  15. package/dist/api/container/routes.js.map +1 -1
  16. package/dist/api/container/types.d.ts +2 -4
  17. package/dist/api/container/types.js.map +1 -1
  18. package/dist/api/handlers/ChatHandler.d.ts +1 -1
  19. package/dist/api/handlers/ChatHandler.js +1 -1
  20. package/dist/api/handlers/ChatHandler.js.map +1 -1
  21. package/dist/api/handlers/EdgeNodeSignalHandler.js +3 -1
  22. package/dist/api/handlers/EdgeNodeSignalHandler.js.map +1 -1
  23. package/dist/api/handlers/PodManagementHandler.d.ts +2 -0
  24. package/dist/api/handlers/PodManagementHandler.js +114 -12
  25. package/dist/api/handlers/PodManagementHandler.js.map +1 -1
  26. package/dist/api/handlers/ProvisionHandler.d.ts +27 -0
  27. package/dist/api/handlers/ProvisionHandler.js +339 -32
  28. package/dist/api/handlers/ProvisionHandler.js.map +1 -1
  29. package/dist/api/handlers/QuotaHandler.js +0 -12
  30. package/dist/api/handlers/QuotaHandler.js.map +1 -1
  31. package/dist/api/handlers/index.d.ts +0 -1
  32. package/dist/api/handlers/index.js +0 -1
  33. package/dist/api/handlers/index.js.map +1 -1
  34. package/dist/api/runtime.js +3 -3
  35. package/dist/api/runtime.js.map +1 -1
  36. package/dist/components/context.jsonld +12 -0
  37. package/dist/edge/EdgeNodeAgent.d.ts +1 -1
  38. package/dist/edge/EdgeNodeAgent.js +1 -1
  39. package/dist/edge/EdgeNodeAgent.js.map +1 -1
  40. package/dist/edge/EdgeNodeDnsCoordinator.d.ts +1 -0
  41. package/dist/edge/EdgeNodeDnsCoordinator.js +9 -3
  42. package/dist/edge/EdgeNodeDnsCoordinator.js.map +1 -1
  43. package/dist/edge/EdgeNodeDnsCoordinator.jsonld +4 -0
  44. package/dist/edge/EdgeNodeHealthProbeService.d.ts +3 -0
  45. package/dist/edge/EdgeNodeHealthProbeService.js +22 -2
  46. package/dist/edge/EdgeNodeHealthProbeService.js.map +1 -1
  47. package/dist/edge/EdgeNodeHealthProbeService.jsonld +12 -0
  48. package/dist/http/ClusterIngressRouter.js +6 -3
  49. package/dist/http/ClusterIngressRouter.js.map +1 -1
  50. package/dist/http/ClusterWebSocketConfigurator.js +6 -2
  51. package/dist/http/ClusterWebSocketConfigurator.js.map +1 -1
  52. package/dist/http/EdgeNodeDirectDebugHttpHandler.d.ts +2 -0
  53. package/dist/http/EdgeNodeDirectDebugHttpHandler.js +18 -3
  54. package/dist/http/EdgeNodeDirectDebugHttpHandler.js.map +1 -1
  55. package/dist/http/EdgeNodeDirectDebugHttpHandler.jsonld +8 -0
  56. package/dist/http/EdgeNodeProxyHttpHandler.js +6 -2
  57. package/dist/http/EdgeNodeProxyHttpHandler.js.map +1 -1
  58. package/dist/http/cluster/PodMigrationHttpHandler.d.ts +2 -2
  59. package/dist/http/cluster/PodMigrationHttpHandler.js +2 -2
  60. package/dist/http/cluster/PodMigrationHttpHandler.js.map +1 -1
  61. package/dist/http/quota/QuotaAdminHttpHandler.js +27 -21
  62. package/dist/http/quota/QuotaAdminHttpHandler.js.map +1 -1
  63. package/dist/identity/drizzle/AccountRepository.d.ts +4 -22
  64. package/dist/identity/drizzle/AccountRepository.js +9 -113
  65. package/dist/identity/drizzle/AccountRepository.js.map +1 -1
  66. package/dist/identity/drizzle/AccountRoleRepository.d.ts +5 -5
  67. package/dist/identity/drizzle/AccountRoleRepository.js +204 -97
  68. package/dist/identity/drizzle/AccountRoleRepository.js.map +1 -1
  69. package/dist/identity/drizzle/DdnsRepository.d.ts +5 -20
  70. package/dist/identity/drizzle/DdnsRepository.js +13 -49
  71. package/dist/identity/drizzle/DdnsRepository.js.map +1 -1
  72. package/dist/identity/drizzle/EdgeNodeRepository.d.ts +13 -6
  73. package/dist/identity/drizzle/EdgeNodeRepository.js +167 -66
  74. package/dist/identity/drizzle/EdgeNodeRepository.js.map +1 -1
  75. package/dist/identity/drizzle/PodLookupRepository.d.ts +7 -36
  76. package/dist/identity/drizzle/PodLookupRepository.js +103 -126
  77. package/dist/identity/drizzle/PodLookupRepository.js.map +1 -1
  78. package/dist/identity/drizzle/ServiceTokenRepository.d.ts +13 -1
  79. package/dist/identity/drizzle/ServiceTokenRepository.js +7 -0
  80. package/dist/identity/drizzle/ServiceTokenRepository.js.map +1 -1
  81. package/dist/identity/drizzle/db.d.ts +2 -1
  82. package/dist/identity/drizzle/db.js +173 -297
  83. package/dist/identity/drizzle/db.js.map +1 -1
  84. package/dist/identity/drizzle/schema.pg.d.ts +3 -11
  85. package/dist/identity/drizzle/schema.pg.js +10 -45
  86. package/dist/identity/drizzle/schema.pg.js.map +1 -1
  87. package/dist/identity/drizzle/schema.sqlite.d.ts +88 -531
  88. package/dist/identity/drizzle/schema.sqlite.js +13 -46
  89. package/dist/identity/drizzle/schema.sqlite.js.map +1 -1
  90. package/dist/identity/oidc/ScopedPickWebIdHandler.d.ts +3 -0
  91. package/dist/identity/oidc/ScopedPickWebIdHandler.js +18 -6
  92. package/dist/identity/oidc/ScopedPickWebIdHandler.js.map +1 -1
  93. package/dist/identity/oidc/ScopedPickWebIdHandler.jsonld +22 -0
  94. package/dist/provision/ProvisionCodeCodec.js +10 -1
  95. package/dist/provision/ProvisionCodeCodec.js.map +1 -1
  96. package/dist/provision/ProvisionPodCreator.d.ts +8 -2
  97. package/dist/provision/ProvisionPodCreator.js +134 -41
  98. package/dist/provision/ProvisionPodCreator.js.map +1 -1
  99. package/dist/provision/ProvisionPodCreator.jsonld +38 -3
  100. package/dist/quota/DrizzleQuotaService.d.ts +0 -4
  101. package/dist/quota/DrizzleQuotaService.js +1 -21
  102. package/dist/quota/DrizzleQuotaService.js.map +1 -1
  103. package/dist/quota/DrizzleQuotaService.jsonld +0 -16
  104. package/dist/quota/NoopQuotaService.d.ts +0 -4
  105. package/dist/quota/NoopQuotaService.js +0 -8
  106. package/dist/quota/NoopQuotaService.js.map +1 -1
  107. package/dist/quota/NoopQuotaService.jsonld +0 -16
  108. package/dist/quota/QuotaService.d.ts +0 -4
  109. package/dist/quota/QuotaService.js.map +1 -1
  110. package/dist/quota/QuotaService.jsonld +0 -16
  111. package/dist/service/EdgeNodeSignalClient.d.ts +0 -2
  112. package/dist/service/EdgeNodeSignalClient.js +0 -4
  113. package/dist/service/EdgeNodeSignalClient.js.map +1 -1
  114. package/dist/service/PodMigrationService.d.ts +2 -2
  115. package/dist/service/PodMigrationService.js +4 -4
  116. package/dist/service/PodMigrationService.js.map +1 -1
  117. package/dist/setup/LocalSetupServiceTokenRepository.d.ts +22 -0
  118. package/dist/setup/LocalSetupServiceTokenRepository.js +68 -0
  119. package/dist/setup/LocalSetupServiceTokenRepository.js.map +1 -0
  120. package/dist/storage/quota/PerAccountQuotaStrategy.js +2 -2
  121. package/dist/storage/quota/PerAccountQuotaStrategy.js.map +1 -1
  122. package/dist/storage/quota/UsageRepository.d.ts +10 -32
  123. package/dist/storage/quota/UsageRepository.js +84 -281
  124. package/dist/storage/quota/UsageRepository.js.map +1 -1
  125. package/dist/subdomain/SubdomainService.d.ts +1 -1
  126. package/dist/subdomain/SubdomainService.js +1 -1
  127. package/dist/subdomain/SubdomainService.js.map +1 -1
  128. package/dist/subdomain/SubdomainService.jsonld +1 -1
  129. package/package.json +1 -1
  130. package/dist/api/handlers/ApiKeyHandler.d.ts +0 -15
  131. package/dist/api/handlers/ApiKeyHandler.js +0 -153
  132. package/dist/api/handlers/ApiKeyHandler.js.map +0 -1
  133. package/dist/api/store/DrizzleClientCredentialsStore.d.ts +0 -51
  134. package/dist/api/store/DrizzleClientCredentialsStore.js +0 -115
  135. package/dist/api/store/DrizzleClientCredentialsStore.js.map +0 -1
@@ -1,130 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AccountRepository = void 0;
4
- const drizzle_orm_1 = require("drizzle-orm");
5
- const db_1 = require("./db");
4
+ const PodLookupRepository_1 = require("./PodLookupRepository");
6
5
  /**
7
- * Repository for account and pod quota operations.
8
- *
9
- * Reads account/pod data from CSS's internal_kv table.
10
- * Quota limits are stored in identity_account_usage / identity_pod_usage tables.
6
+ * Reads account/pod ownership data from the CSS identity facts.
7
+ * Quota values live in identity_usage and are managed by UsageRepository.
11
8
  */
12
9
  class AccountRepository {
13
10
  constructor(db, kvTableName) {
14
- this.db = db;
15
- this.kvTableName = kvTableName ?? 'internal_kv';
16
- this.accountUsageTable = 'identity_account_usage';
17
- this.podUsageTable = 'identity_pod_usage';
18
- }
19
- async getAccountQuota(accountId) {
20
- try {
21
- const tableId = drizzle_orm_1.sql.identifier([this.accountUsageTable]);
22
- const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `SELECT storage_limit_bytes FROM ${tableId} WHERE account_id = ${accountId} LIMIT 1`);
23
- if (result.rows.length === 0) {
24
- return undefined;
25
- }
26
- const limit = result.rows[0].storage_limit_bytes;
27
- return limit != null ? limit : undefined;
28
- }
29
- catch {
30
- // Table might not exist
31
- return undefined;
32
- }
11
+ this.podLookupRepo = new PodLookupRepository_1.PodLookupRepository(db, kvTableName);
33
12
  }
34
13
  async getPodInfo(podId) {
35
- // Get pod info from CSS's internal_kv
36
- const kvTableId = drizzle_orm_1.sql.identifier([this.kvTableName]);
37
- const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
38
- SELECT key, value FROM ${kvTableId}
39
- WHERE key LIKE 'accounts/data/%'
40
- `);
41
- for (const row of result.rows) {
42
- try {
43
- const accountId = row.key.replace('accounts/data/', '');
44
- const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;
45
- const podMap = data['**pod**'] || data.pod || {};
46
- if (podMap[podId]) {
47
- const pod = podMap[podId];
48
- const podQuota = await this.getPodQuota(podId);
49
- return {
50
- accountId,
51
- baseUrl: typeof pod.baseUrl === 'string' ? pod.baseUrl : undefined,
52
- podQuota,
53
- };
54
- }
55
- }
56
- catch {
57
- // Skip malformed entries
58
- }
59
- }
60
- return undefined;
61
- }
62
- async getPodQuota(podId) {
63
- try {
64
- const tableId = drizzle_orm_1.sql.identifier([this.podUsageTable]);
65
- const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `SELECT storage_limit_bytes FROM ${tableId} WHERE pod_id = ${podId} LIMIT 1`);
66
- if (result.rows.length === 0) {
67
- return undefined;
68
- }
69
- const limit = result.rows[0].storage_limit_bytes;
70
- return limit != null ? limit : undefined;
71
- }
72
- catch {
73
- // Table might not exist
74
- return undefined;
75
- }
76
- }
77
- async getQuotaContext(accountId, podId) {
78
- const podInfo = await this.getPodInfo(podId);
79
- if (!podInfo) {
80
- return undefined;
81
- }
82
- if (podInfo.accountId !== accountId) {
14
+ const pod = await this.podLookupRepo.findById(podId)
15
+ ?? await this.podLookupRepo.findByResourceIdentifier(podId);
16
+ if (!pod) {
83
17
  return undefined;
84
18
  }
85
- const accountQuota = await this.getAccountQuota(accountId);
86
19
  return {
87
- accountId,
88
- podId,
89
- baseUrl: podInfo.baseUrl,
90
- podQuota: podInfo.podQuota,
91
- accountQuota,
20
+ accountId: pod.accountId,
21
+ baseUrl: pod.baseUrl,
92
22
  };
93
23
  }
94
- async setAccountQuota(accountId, quota) {
95
- const tableId = drizzle_orm_1.sql.identifier([this.accountUsageTable]);
96
- if ((0, db_1.isDatabaseSqlite)(this.db)) {
97
- await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
98
- INSERT INTO ${tableId} (account_id, storage_limit_bytes)
99
- VALUES (${accountId}, ${quota ?? null})
100
- ON CONFLICT (account_id) DO UPDATE SET storage_limit_bytes = ${quota ?? null}
101
- `);
102
- }
103
- else {
104
- await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
105
- INSERT INTO ${tableId} (account_id, storage_limit_bytes)
106
- VALUES (${accountId}, ${quota ?? null})
107
- ON CONFLICT (account_id) DO UPDATE SET storage_limit_bytes = EXCLUDED.storage_limit_bytes
108
- `);
109
- }
110
- }
111
- async setPodQuota(podId, quota) {
112
- const tableId = drizzle_orm_1.sql.identifier([this.podUsageTable]);
113
- if ((0, db_1.isDatabaseSqlite)(this.db)) {
114
- await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
115
- INSERT INTO ${tableId} (pod_id, account_id, storage_limit_bytes)
116
- VALUES (${podId}, '', ${quota ?? null})
117
- ON CONFLICT (pod_id) DO UPDATE SET storage_limit_bytes = ${quota ?? null}
118
- `);
119
- }
120
- else {
121
- await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
122
- INSERT INTO ${tableId} (pod_id, account_id, storage_limit_bytes)
123
- VALUES (${podId}, '', ${quota ?? null})
124
- ON CONFLICT (pod_id) DO UPDATE SET storage_limit_bytes = EXCLUDED.storage_limit_bytes
125
- `);
126
- }
127
- }
128
24
  }
129
25
  exports.AccountRepository = AccountRepository;
130
26
  //# sourceMappingURL=AccountRepository.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AccountRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/AccountRepository.ts"],"names":[],"mappings":"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","sourcesContent":["import { sql } from 'drizzle-orm';\nimport { type IdentityDatabase, executeQuery, isDatabaseSqlite } from './db';\n\nexport interface PodQuotaContext {\n accountId: string;\n podId: string;\n baseUrl?: string;\n podQuota?: number;\n accountQuota?: number;\n}\n\ninterface InternalKvRow {\n key: string;\n value: string;\n}\n\n/**\n * Repository for account and pod quota operations.\n *\n * Reads account/pod data from CSS's internal_kv table.\n * Quota limits are stored in identity_account_usage / identity_pod_usage tables.\n */\nexport class AccountRepository {\n private readonly kvTableName: string;\n private readonly accountUsageTable: string;\n private readonly podUsageTable: string;\n\n public constructor(\n private readonly db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.kvTableName = kvTableName ?? 'internal_kv';\n this.accountUsageTable = 'identity_account_usage';\n this.podUsageTable = 'identity_pod_usage';\n }\n\n public async getAccountQuota(accountId: string): Promise<number | undefined> {\n try {\n const tableId = sql.identifier([this.accountUsageTable]);\n const result = await executeQuery<{ storage_limit_bytes: number | null }>(\n this.db,\n sql`SELECT storage_limit_bytes FROM ${tableId} WHERE account_id = ${accountId} LIMIT 1`,\n );\n if (result.rows.length === 0) {\n return undefined;\n }\n const limit = result.rows[0].storage_limit_bytes;\n return limit != null ? limit : undefined;\n } catch {\n // Table might not exist\n return undefined;\n }\n }\n\n public async getPodInfo(podId: string): Promise<{ accountId: string; podQuota?: number; baseUrl?: string } | undefined> {\n // Get pod info from CSS's internal_kv\n const kvTableId = sql.identifier([this.kvTableName]);\n const result = await executeQuery<InternalKvRow>(this.db, sql`\n SELECT key, value FROM ${kvTableId}\n WHERE key LIKE 'accounts/data/%'\n `);\n\n for (const row of result.rows) {\n try {\n const accountId = row.key.replace('accounts/data/', '');\n const data = typeof row.value === 'string' ? JSON.parse(row.value) : row.value;\n const podMap = data['**pod**'] || data.pod || {};\n\n if (podMap[podId]) {\n const pod = podMap[podId] as Record<string, unknown>;\n const podQuota = await this.getPodQuota(podId);\n return {\n accountId,\n baseUrl: typeof pod.baseUrl === 'string' ? pod.baseUrl : undefined,\n podQuota,\n };\n }\n } catch {\n // Skip malformed entries\n }\n }\n\n return undefined;\n }\n\n private async getPodQuota(podId: string): Promise<number | undefined> {\n try {\n const tableId = sql.identifier([this.podUsageTable]);\n const result = await executeQuery<{ storage_limit_bytes: number | null }>(\n this.db,\n sql`SELECT storage_limit_bytes FROM ${tableId} WHERE pod_id = ${podId} LIMIT 1`,\n );\n if (result.rows.length === 0) {\n return undefined;\n }\n const limit = result.rows[0].storage_limit_bytes;\n return limit != null ? limit : undefined;\n } catch {\n // Table might not exist\n return undefined;\n }\n }\n\n public async getQuotaContext(accountId: string, podId: string): Promise<PodQuotaContext | undefined> {\n const podInfo = await this.getPodInfo(podId);\n if (!podInfo) {\n return undefined;\n }\n if (podInfo.accountId !== accountId) {\n return undefined;\n }\n const accountQuota = await this.getAccountQuota(accountId);\n return {\n accountId,\n podId,\n baseUrl: podInfo.baseUrl,\n podQuota: podInfo.podQuota,\n accountQuota,\n };\n }\n\n public async setAccountQuota(accountId: string, quota?: number): Promise<void> {\n const tableId = sql.identifier([this.accountUsageTable]);\n if (isDatabaseSqlite(this.db)) {\n await executeQuery(this.db, sql`\n INSERT INTO ${tableId} (account_id, storage_limit_bytes)\n VALUES (${accountId}, ${quota ?? null})\n ON CONFLICT (account_id) DO UPDATE SET storage_limit_bytes = ${quota ?? null}\n `);\n } else {\n await executeQuery(this.db, sql`\n INSERT INTO ${tableId} (account_id, storage_limit_bytes)\n VALUES (${accountId}, ${quota ?? null})\n ON CONFLICT (account_id) DO UPDATE SET storage_limit_bytes = EXCLUDED.storage_limit_bytes\n `);\n }\n }\n\n public async setPodQuota(podId: string, quota?: number): Promise<void> {\n const tableId = sql.identifier([this.podUsageTable]);\n if (isDatabaseSqlite(this.db)) {\n await executeQuery(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, storage_limit_bytes)\n VALUES (${podId}, '', ${quota ?? null})\n ON CONFLICT (pod_id) DO UPDATE SET storage_limit_bytes = ${quota ?? null}\n `);\n } else {\n await executeQuery(this.db, sql`\n INSERT INTO ${tableId} (pod_id, account_id, storage_limit_bytes)\n VALUES (${podId}, '', ${quota ?? null})\n ON CONFLICT (pod_id) DO UPDATE SET storage_limit_bytes = EXCLUDED.storage_limit_bytes\n `);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"AccountRepository.js","sourceRoot":"","sources":["../../../src/identity/drizzle/AccountRepository.ts"],"names":[],"mappings":";;;AACA,+DAA4D;AAE5D;;;GAGG;AACH,MAAa,iBAAiB;IAG5B,YACE,EAAoB,EACpB,WAAoB;QAEpB,IAAI,CAAC,aAAa,GAAG,IAAI,yCAAmB,CAAC,EAAE,EAAE,WAAW,CAAC,CAAC;IAChE,CAAC;IAEM,KAAK,CAAC,UAAU,CAAC,KAAa;QACnC,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC;eAC/C,MAAM,IAAI,CAAC,aAAa,CAAC,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAC9D,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO;YACL,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,OAAO,EAAE,GAAG,CAAC,OAAO;SACrB,CAAC;IACJ,CAAC;CACF;AArBD,8CAqBC","sourcesContent":["import type { IdentityDatabase } from './db';\nimport { PodLookupRepository } from './PodLookupRepository';\n\n/**\n * Reads account/pod ownership data from the CSS identity facts.\n * Quota values live in identity_usage and are managed by UsageRepository.\n */\nexport class AccountRepository {\n private readonly podLookupRepo: PodLookupRepository;\n\n public constructor(\n db: IdentityDatabase,\n kvTableName?: string,\n ) {\n this.podLookupRepo = new PodLookupRepository(db, kvTableName);\n }\n\n public async getPodInfo(podId: string): Promise<{ accountId: string; baseUrl?: string } | undefined> {\n const pod = await this.podLookupRepo.findById(podId)\n ?? await this.podLookupRepo.findByResourceIdentifier(podId);\n if (!pod) {\n return undefined;\n }\n return {\n accountId: pod.accountId,\n baseUrl: pod.baseUrl,\n };\n }\n}\n"]}
@@ -6,18 +6,18 @@ export interface AccountRoleContext {
6
6
  }
7
7
  export declare class AccountRoleRepository {
8
8
  private readonly db;
9
- private readonly ready;
10
9
  private readonly logger;
11
10
  constructor(db: IdentityDatabase);
12
11
  findByAccountId(accountId: string): Promise<AccountRoleContext | undefined>;
13
12
  findByWebId(webId: string): Promise<AccountRoleContext | undefined>;
14
13
  findByWebIdLoose(webId: string): Promise<AccountRoleContext | undefined>;
15
14
  addRoles(accountId: string, roles: string[]): Promise<void>;
16
- private ensureSchema;
17
- private fetchRoles;
18
- private getAccountPayloadById;
15
+ private getAccountById;
19
16
  private loadAllAccounts;
17
+ private loadIdentityStoreAccounts;
18
+ private loadInternalKvAccounts;
20
19
  private loadFileAccountMap;
20
+ private updateAccountRecord;
21
+ private toJsonSql;
21
22
  private isTableMissing;
22
- private isDuplicateDefinitionError;
23
23
  }
@@ -10,6 +10,8 @@ const drizzle_orm_1 = require("drizzle-orm");
10
10
  const global_logger_factory_1 = require("global-logger-factory");
11
11
  const db_1 = require("./db");
12
12
  const ACCOUNT_DATA_DIR = node_path_1.default.resolve('.internal', 'accounts', 'data');
13
+ const IDENTITY_STORE_TABLE = 'identity_store';
14
+ const INTERNAL_KV_TABLE = 'internal_kv';
13
15
  function resolveWebIds(payload) {
14
16
  const candidates = new Set();
15
17
  const possibleKeys = ['webId', 'webid', 'primaryWebId', 'primary_webid'];
@@ -38,39 +40,87 @@ function resolveWebIds(payload) {
38
40
  }
39
41
  }
40
42
  }
41
- return [...candidates];
43
+ const webIdLink = payload['**webIdLink**'] ?? payload.webIdLink;
44
+ if (webIdLink && typeof webIdLink === 'object') {
45
+ for (const entry of Object.values(webIdLink)) {
46
+ if (!entry || typeof entry !== 'object') {
47
+ continue;
48
+ }
49
+ const webId = entry.webId;
50
+ if (typeof webId === 'string' && webId.trim().length > 0) {
51
+ candidates.add(webId.trim());
52
+ }
53
+ }
54
+ }
55
+ const podMap = payload['**pod**'] ?? payload.pod;
56
+ if (podMap && typeof podMap === 'object') {
57
+ for (const pod of Object.values(podMap)) {
58
+ if (!pod || typeof pod !== 'object') {
59
+ continue;
60
+ }
61
+ const owner = pod['**owner**'] ?? pod.owner;
62
+ if (!owner || typeof owner !== 'object') {
63
+ continue;
64
+ }
65
+ for (const entry of Object.values(owner)) {
66
+ if (!entry || typeof entry !== 'object') {
67
+ continue;
68
+ }
69
+ const webId = entry.webId;
70
+ if (typeof webId === 'string' && webId.trim().length > 0) {
71
+ candidates.add(webId.trim());
72
+ }
73
+ }
74
+ }
75
+ }
76
+ return Array.from(candidates);
77
+ }
78
+ function resolveRoles(payload) {
79
+ const roles = payload.roles;
80
+ if (!Array.isArray(roles)) {
81
+ return [];
82
+ }
83
+ return Array.from(new Set(roles
84
+ .map((role) => typeof role === 'string' ? role.trim() : '')
85
+ .filter((role) => role.length > 0)));
86
+ }
87
+ function parsePayload(value) {
88
+ if (!value) {
89
+ return undefined;
90
+ }
91
+ if (typeof value === 'string') {
92
+ try {
93
+ const parsed = JSON.parse(value);
94
+ return parsed && typeof parsed === 'object' ? parsed : undefined;
95
+ }
96
+ catch {
97
+ return undefined;
98
+ }
99
+ }
100
+ return typeof value === 'object' ? value : undefined;
42
101
  }
43
102
  class AccountRoleRepository {
44
103
  constructor(db) {
45
104
  this.db = db;
46
105
  this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
47
- this.ready = this.ensureSchema();
48
106
  }
49
107
  async findByAccountId(accountId) {
50
- await this.ready;
51
- const payload = await this.getAccountPayloadById(accountId);
52
- if (!payload) {
53
- const rolesFallback = await this.fetchRoles(accountId);
54
- if (rolesFallback.length === 0) {
55
- return undefined;
56
- }
57
- return { accountId, roles: rolesFallback };
108
+ const record = await this.getAccountById(accountId);
109
+ if (!record) {
110
+ return undefined;
58
111
  }
59
- const [webId] = resolveWebIds(payload);
60
- const roles = await this.fetchRoles(accountId);
61
- return { accountId, webId, roles };
112
+ const [webId] = resolveWebIds(record.payload);
113
+ return { accountId, webId, roles: resolveRoles(record.payload) };
62
114
  }
63
115
  async findByWebId(webId) {
64
- await this.ready;
65
116
  const accounts = await this.loadAllAccounts();
66
- for (const { id, payload } of accounts) {
117
+ for (const { id, payload } of accounts.values()) {
67
118
  const knownWebIds = resolveWebIds(payload);
68
119
  if (knownWebIds.includes(webId)) {
69
- const roles = await this.fetchRoles(id);
70
120
  return {
71
121
  accountId: id,
72
122
  webId,
73
- roles,
123
+ roles: resolveRoles(payload),
74
124
  };
75
125
  }
76
126
  }
@@ -80,100 +130,131 @@ class AccountRoleRepository {
80
130
  return this.findByWebId(webId);
81
131
  }
82
132
  async addRoles(accountId, roles) {
83
- await this.ready;
84
133
  const unique = Array.from(new Set(roles.map((role) => role.trim()).filter((role) => role.length > 0)));
85
134
  if (unique.length === 0) {
86
135
  return;
87
136
  }
88
- for (const role of unique) {
89
- await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
90
- INSERT INTO identity_account_role (account_id, role)
91
- VALUES (${accountId}, ${role})
92
- ON CONFLICT (account_id, role) DO NOTHING
93
- `);
137
+ const record = await this.getAccountById(accountId);
138
+ if (!record) {
139
+ this.logger.warn(`Cannot add roles for unknown account ${accountId}`);
140
+ return;
94
141
  }
142
+ const nextRoles = Array.from(new Set([...resolveRoles(record.payload), ...unique]));
143
+ await this.updateAccountRecord(record, { ...record.payload, roles: nextRoles });
95
144
  }
96
- async ensureSchema() {
97
- try {
98
- if ((0, db_1.isDatabaseSqlite)(this.db)) {
99
- // SQLite syntax
100
- await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
101
- CREATE TABLE IF NOT EXISTS identity_account_role (
102
- account_id TEXT NOT NULL,
103
- role TEXT NOT NULL,
104
- created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
105
- PRIMARY KEY (account_id, role)
106
- )
107
- `);
108
- }
109
- else {
110
- // PostgreSQL syntax
111
- await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
112
- CREATE TABLE IF NOT EXISTS identity_account_role (
113
- account_id TEXT NOT NULL,
114
- role TEXT NOT NULL,
115
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
116
- PRIMARY KEY (account_id, role)
117
- )
118
- `);
119
- }
120
- }
121
- catch (error) {
122
- if (!this.isDuplicateDefinitionError(error)) {
123
- throw error;
145
+ async getAccountById(accountId) {
146
+ const accounts = await this.loadAllAccounts();
147
+ return accounts.get(accountId);
148
+ }
149
+ async loadAllAccounts() {
150
+ const accounts = new Map();
151
+ await this.loadIdentityStoreAccounts(accounts);
152
+ await this.loadInternalKvAccounts(accounts);
153
+ for (const [id, payload] of await this.loadFileAccountMap()) {
154
+ if (!accounts.has(id)) {
155
+ accounts.set(id, { id, payload, source: 'file' });
124
156
  }
125
- this.logger.debug('identity_account_role schema already present, skipping creation.');
126
157
  }
158
+ return accounts;
127
159
  }
128
- async fetchRoles(accountId) {
129
- const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
130
- SELECT role
131
- FROM identity_account_role
132
- WHERE account_id = ${accountId}
133
- `);
134
- return result.rows.map((row) => {
135
- const value = typeof row.role === 'string' ? row.role.trim() : '';
136
- return value;
137
- }).filter((value) => value.length > 0);
138
- }
139
- async getAccountPayloadById(accountId) {
160
+ async loadIdentityStoreAccounts(accounts) {
161
+ const tableId = drizzle_orm_1.sql.identifier([IDENTITY_STORE_TABLE]);
162
+ let rows = [];
140
163
  try {
141
- const accountResult = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
142
- SELECT payload
143
- FROM identity_account
144
- WHERE id = ${accountId}
145
- LIMIT 1
164
+ const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
165
+ SELECT container, id, payload
166
+ FROM ${tableId}
167
+ WHERE container IN ('account', 'pod', 'owner', 'webIdLink')
146
168
  `);
147
- if (accountResult.rows.length > 0) {
148
- const payload = accountResult.rows[0]?.payload;
149
- if (payload && typeof payload === 'object') {
150
- return payload;
151
- }
152
- }
169
+ rows = result.rows;
153
170
  }
154
171
  catch (error) {
155
172
  if (!this.isTableMissing(error)) {
156
173
  throw error;
157
174
  }
175
+ return;
176
+ }
177
+ const podAccountIds = new Map();
178
+ const webIdsByAccount = new Map();
179
+ for (const row of rows) {
180
+ if (!row.id || !row.container) {
181
+ continue;
182
+ }
183
+ const payload = parsePayload(row.payload);
184
+ if (!payload) {
185
+ continue;
186
+ }
187
+ if (row.container === 'account') {
188
+ accounts.set(row.id, { id: row.id, payload, source: 'identity-store' });
189
+ }
190
+ else if (row.container === 'pod') {
191
+ const accountId = typeof payload.accountId === 'string' ? payload.accountId : undefined;
192
+ if (accountId) {
193
+ podAccountIds.set(row.id, accountId);
194
+ }
195
+ }
196
+ }
197
+ for (const row of rows) {
198
+ const payload = parsePayload(row.payload);
199
+ if (!row.container || !payload) {
200
+ continue;
201
+ }
202
+ if (row.container === 'webIdLink') {
203
+ const accountId = typeof payload.accountId === 'string' ? payload.accountId : undefined;
204
+ const webId = typeof payload.webId === 'string' ? payload.webId : undefined;
205
+ if (accountId && webId) {
206
+ appendWebId(webIdsByAccount, accountId, webId);
207
+ }
208
+ }
209
+ else if (row.container === 'owner') {
210
+ const podId = typeof payload.podId === 'string' ? payload.podId : undefined;
211
+ const webId = typeof payload.webId === 'string' ? payload.webId : undefined;
212
+ const accountId = podId ? podAccountIds.get(podId) : undefined;
213
+ if (accountId && webId) {
214
+ appendWebId(webIdsByAccount, accountId, webId);
215
+ }
216
+ }
217
+ }
218
+ for (const [accountId, webIds] of webIdsByAccount) {
219
+ const record = accounts.get(accountId);
220
+ if (!record) {
221
+ continue;
222
+ }
223
+ record.payload = {
224
+ ...record.payload,
225
+ webIdLink: Object.fromEntries(Array.from(webIds).map((webId, index) => [
226
+ `webid-${index}`,
227
+ { accountId, webId },
228
+ ])),
229
+ };
158
230
  }
159
- const files = await this.loadFileAccountMap();
160
- return files.get(accountId);
161
231
  }
162
- async loadAllAccounts() {
232
+ async loadInternalKvAccounts(accounts) {
233
+ const tableId = drizzle_orm_1.sql.identifier([INTERNAL_KV_TABLE]);
163
234
  try {
164
- const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `SELECT id, payload FROM identity_account`);
165
- return result.rows.map((row) => ({
166
- id: row.id,
167
- payload: (row.payload ?? {}),
168
- }));
235
+ const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
236
+ SELECT key, value
237
+ FROM ${tableId}
238
+ WHERE key LIKE 'accounts/data/%'
239
+ OR key LIKE '/.internal/accounts/data/%'
240
+ `);
241
+ for (const row of result.rows) {
242
+ if (!row.key) {
243
+ continue;
244
+ }
245
+ const accountId = extractAccountIdFromKey(row.key);
246
+ const payload = parsePayload(row.value);
247
+ if (!accountId || !payload || accounts.has(accountId)) {
248
+ continue;
249
+ }
250
+ accounts.set(accountId, { id: accountId, payload, source: 'internal-kv', key: row.key });
251
+ }
169
252
  }
170
253
  catch (error) {
171
254
  if (!this.isTableMissing(error)) {
172
255
  throw error;
173
256
  }
174
257
  }
175
- const files = await this.loadFileAccountMap();
176
- return Array.from(files.entries()).map(([id, payload]) => ({ id, payload }));
177
258
  }
178
259
  async loadFileAccountMap() {
179
260
  const map = new Map();
@@ -206,28 +287,54 @@ class AccountRoleRepository {
206
287
  }
207
288
  return map;
208
289
  }
209
- isTableMissing(error) {
210
- if (!error || typeof error !== 'object') {
211
- return false;
290
+ async updateAccountRecord(record, payload) {
291
+ if (record.source === 'identity-store') {
292
+ const tableId = drizzle_orm_1.sql.identifier([IDENTITY_STORE_TABLE]);
293
+ await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
294
+ UPDATE ${tableId}
295
+ SET payload = ${this.toJsonSql(payload)}
296
+ WHERE container = 'account' AND id = ${record.id}
297
+ `);
298
+ return;
212
299
  }
213
- const code = error.code;
214
- if (code === '42P01') {
215
- return true;
300
+ if (record.source === 'internal-kv' && record.key) {
301
+ const tableId = drizzle_orm_1.sql.identifier([INTERNAL_KV_TABLE]);
302
+ await (0, db_1.executeStatement)(this.db, (0, drizzle_orm_1.sql) `
303
+ UPDATE ${tableId}
304
+ SET value = ${JSON.stringify(payload)}
305
+ WHERE key = ${record.key}
306
+ `);
216
307
  }
217
- const message = error.message ?? '';
218
- return /does not exist/u.test(message);
219
308
  }
220
- isDuplicateDefinitionError(error) {
309
+ toJsonSql(payload) {
310
+ const serialized = JSON.stringify(payload);
311
+ return (0, db_1.isDatabaseSqlite)(this.db) ? (0, drizzle_orm_1.sql) `${serialized}` : (0, drizzle_orm_1.sql) `${serialized}::jsonb`;
312
+ }
313
+ isTableMissing(error) {
221
314
  if (!error || typeof error !== 'object') {
222
315
  return false;
223
316
  }
224
317
  const code = error.code;
225
- if (code && ['23505', '42P07', '42710'].includes(code)) {
318
+ if (code === '42P01') {
226
319
  return true;
227
320
  }
228
321
  const message = error.message ?? '';
229
- return /already exists/u.test(message);
322
+ return /does not exist|no such table/u.test(message);
230
323
  }
231
324
  }
232
325
  exports.AccountRoleRepository = AccountRoleRepository;
326
+ function appendWebId(target, accountId, webId) {
327
+ const values = target.get(accountId) ?? new Set();
328
+ values.add(webId);
329
+ target.set(accountId, values);
330
+ }
331
+ function extractAccountIdFromKey(key) {
332
+ const marker = 'accounts/data/';
333
+ const index = key.indexOf(marker);
334
+ if (index < 0) {
335
+ return undefined;
336
+ }
337
+ const accountId = key.slice(index + marker.length).replace(/\.json$/u, '');
338
+ return accountId || undefined;
339
+ }
233
340
  //# sourceMappingURL=AccountRoleRepository.js.map