@undefineds.co/xpod 0.4.9 → 0.4.11

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 (130) hide show
  1. package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
  2. package/dist/api/auth/ClientCredentialsAuthenticator.js +0 -3
  3. package/dist/api/auth/index.d.ts +1 -0
  4. package/dist/api/auth/index.js +1 -1
  5. package/dist/identity/drizzle/AccountRoleRepository.d.ts +30 -2
  6. package/dist/identity/drizzle/AccountRoleRepository.js +163 -15
  7. package/dist/identity/drizzle/PodLookupRepository.d.ts +55 -12
  8. package/dist/identity/drizzle/PodLookupRepository.js +225 -243
  9. package/dist/identity/drizzle/db.d.ts +19 -0
  10. package/dist/identity/drizzle/db.js +39 -4
  11. package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +1 -0
  12. package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +1 -0
  13. package/node_modules/@undefineds.co/ai-connections/dist/controller.js +94 -16
  14. package/node_modules/@undefineds.co/shared-ui/dist/index.d.ts +0 -1
  15. package/node_modules/@undefineds.co/shared-ui/dist/index.js +0 -1
  16. package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +112 -11
  17. package/node_modules/@undefineds.co/solid-sdk/dist/session.d.ts +2 -0
  18. package/node_modules/@undefineds.co/solid-sdk/dist/session.js +135 -11
  19. package/package.json +2 -2
  20. package/static/app/assets/main.css +1 -1
  21. package/static/app/assets/main.js +12 -13
  22. package/static/auth-callback/assets/{AiConfigContext-DJKvxduw.js → AiConfigContext-DqWRSm7n.js} +1 -1
  23. package/static/auth-callback/assets/{AiConfigPage-DgGmHb18.js → AiConfigPage-C5YYyvzl.js} +1 -1
  24. package/static/auth-callback/assets/{DocumentProcessingPanel-CJmxu8x1.js → DocumentProcessingPanel-DZCiacBz.js} +1 -1
  25. package/static/auth-callback/assets/{IndexLifecyclePanel-byCa7h7L.js → IndexLifecyclePanel-CCLeOljB.js} +1 -1
  26. package/static/auth-callback/assets/{IndexSubjectPanel-CDwT4Yx1.js → IndexSubjectPanel-BbUqop4b.js} +1 -1
  27. package/static/auth-callback/assets/{ModelAssignmentsPanel-CwM3k8w7.js → ModelAssignmentsPanel-jUUTDKuh.js} +1 -1
  28. package/static/auth-callback/assets/ModelsPage-B08zdtff.js +10 -0
  29. package/static/auth-callback/assets/{NetworkPage-DkVgxtNj.js → NetworkPage-Vp2PrMxs.js} +1 -1
  30. package/static/auth-callback/assets/{PaneListHeader-DRI0hjmt.js → PaneListHeader-DY7baSFl.js} +1 -1
  31. package/static/auth-callback/assets/{SearchIndexingPanel-mbppDZFE.js → SearchIndexingPanel-D4YRg1-8.js} +1 -1
  32. package/static/auth-callback/assets/{StatusSubjectPanel-DNsW76aC.js → StatusSubjectPanel-ArIS-TxT.js} +1 -1
  33. package/static/auth-callback/assets/{StatusWorkspace-DGcTbHT5.js → StatusWorkspace-Cp5LQ9Yx.js} +1 -1
  34. package/static/auth-callback/assets/SystemSettingsPage-BDvT1NUz.js +1 -0
  35. package/static/auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js +33 -0
  36. package/static/auth-callback/assets/{activity-Ck-Mfxxo.js → activity-D1Dsfa_0.js} +1 -1
  37. package/static/auth-callback/assets/admin-C6qCRFiY.js +1 -0
  38. package/static/auth-callback/assets/{auth-callback-DG4Ge40c.js → auth-callback-Cmf4hXVZ.js} +105 -109
  39. package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +1 -0
  40. package/static/auth-callback/assets/{badge-GSSnw1VV.js → badge-CZsHq3Ih.js} +1 -1
  41. package/static/auth-callback/assets/{download-BYyRX-K2.js → download-DU9ntlHw.js} +1 -1
  42. package/static/auth-callback/assets/{external-link-WEC0Ki-0.js → external-link-BfzaaZDx.js} +1 -1
  43. package/static/auth-callback/assets/{index-kju2k4Dp.js → index-DSNnLUbU.js} +4 -4
  44. package/static/auth-callback/assets/{index-DlsMCgo8.js → index-FHhHoKgp.js} +1 -1
  45. package/static/auth-callback/assets/{index-browser-D8YW6coF.js → index-browser-BNFqqC74.js} +1 -1
  46. package/static/auth-callback/assets/{key-round-DJUT5Nh0.js → key-round-BwP3DBWo.js} +1 -1
  47. package/static/auth-callback/assets/{rotate-ccw-CXwfZUKU.js → rotate-ccw-VMwnmyab.js} +1 -1
  48. package/static/auth-callback/assets/settings-projection-CUkyprDQ.js +1 -0
  49. package/static/auth-callback/assets/{skeleton-DWPtNhUB.js → skeleton-CoD55rBT.js} +1 -1
  50. package/static/auth-callback/assets/{waypoints-l1oDnRJ0.js → waypoints-DRo_1Ca7.js} +1 -1
  51. package/static/auth-callback/assets/{workspace-layout-B7iKpK-Q.js → workspace-layout--mcXl3My.js} +1 -1
  52. package/static/auth-callback/auth-callback.html +2 -2
  53. package/static/dashboard/assets/{AiConfigContext-U0cCSY28.js → AiConfigContext-BpPDKSzI.js} +1 -1
  54. package/static/dashboard/assets/{AiConfigPage-CMi0uJdo.js → AiConfigPage-wsqEZDIX.js} +1 -1
  55. package/static/dashboard/assets/{DocumentProcessingPanel-DEZiEpft.js → DocumentProcessingPanel-CDSy3juc.js} +1 -1
  56. package/static/dashboard/assets/{IndexLifecyclePanel-CGZfLUog.js → IndexLifecyclePanel-CVd6vQCX.js} +1 -1
  57. package/static/dashboard/assets/{IndexSubjectPanel-DiDZauHk.js → IndexSubjectPanel-B2cXwl8X.js} +1 -1
  58. package/static/dashboard/assets/{ModelAssignmentsPanel-DaGGZKuf.js → ModelAssignmentsPanel-BeBZhnQd.js} +1 -1
  59. package/static/dashboard/assets/ModelsPage-B6McSCJw.js +25 -0
  60. package/static/dashboard/assets/{NetworkPage-DI5-O7GT.js → NetworkPage-DV2V0qWO.js} +1 -1
  61. package/static/dashboard/assets/{PaneListHeader-4oa7uf59.js → PaneListHeader-D0c1yCFH.js} +1 -1
  62. package/static/dashboard/assets/{SearchIndexingPanel-DoqEwocD.js → SearchIndexingPanel-CGs0OZ12.js} +1 -1
  63. package/static/dashboard/assets/{StatusSubjectPanel-CLzBC5fu.js → StatusSubjectPanel-oPSECj7i.js} +1 -1
  64. package/static/dashboard/assets/{StatusWorkspace-UyKADZoM.js → StatusWorkspace-PdovQPKa.js} +1 -1
  65. package/static/dashboard/assets/SystemSettingsPage-CtNDklkx.js +1 -0
  66. package/static/dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js +33 -0
  67. package/static/dashboard/assets/{activity-CjIoNU4S.js → activity-B9ylSoyr.js} +1 -1
  68. package/static/dashboard/assets/admin-C6qCRFiY.js +1 -0
  69. package/static/dashboard/assets/{badge-dcDZpKh3.js → badge-BNbFbpIb.js} +1 -1
  70. package/static/dashboard/assets/{dashboard-BY8GdnEI.js → dashboard-CD-FFVRD.js} +129 -133
  71. package/static/dashboard/assets/dashboard-DWMMiFjh.css +1 -0
  72. package/static/dashboard/assets/{download-BeYCrc-_.js → download-nOP38LtW.js} +1 -1
  73. package/static/dashboard/assets/{external-link-626-5KVx.js → external-link-Bh2ewN9E.js} +1 -1
  74. package/static/dashboard/assets/{index-CBbk6hx-.js → index-PgS-Bt7X.js} +1 -1
  75. package/static/dashboard/assets/{index-CsxH6Hmz.js → index-QN2QgEwr.js} +4 -4
  76. package/static/dashboard/assets/{key-round-B9IL4Wua.js → key-round-bGmguBLv.js} +1 -1
  77. package/static/dashboard/assets/{rotate-ccw-DM4vCfiI.js → rotate-ccw-D2x__ODW.js} +1 -1
  78. package/static/dashboard/assets/settings-projection-CUkyprDQ.js +1 -0
  79. package/static/dashboard/assets/{skeleton-ChlFAPvP.js → skeleton-VOqft8WS.js} +1 -1
  80. package/static/dashboard/assets/{waypoints-CvpZzf_I.js → waypoints-fgRqm4oH.js} +1 -1
  81. package/static/dashboard/assets/{workspace-layout-Bxv7V-tt.js → workspace-layout-DV4dgeHX.js} +1 -1
  82. package/static/dashboard/dashboard.html +2 -2
  83. package/static/settings/assets/{AiConfigContext-CDVIHLBb.js → AiConfigContext-zKQYdfai.js} +1 -1
  84. package/static/settings/assets/{AiConfigPage-C7qfiiaJ.js → AiConfigPage-Bs1EE3Ei.js} +1 -1
  85. package/static/settings/assets/{DocumentProcessingPanel-B59QTe-V.js → DocumentProcessingPanel-CSFfdOXS.js} +1 -1
  86. package/static/settings/assets/{IndexLifecyclePanel-C-e-3eVi.js → IndexLifecyclePanel-BR9Su49B.js} +1 -1
  87. package/static/settings/assets/{IndexSubjectPanel-B-scRZ1u.js → IndexSubjectPanel-BgbI9c-a.js} +1 -1
  88. package/static/settings/assets/{ModelAssignmentsPanel-Wy8jzVg4.js → ModelAssignmentsPanel-C0BE7nbk.js} +1 -1
  89. package/static/settings/assets/ModelsPage-CU-YQ8_7.js +10 -0
  90. package/static/settings/assets/{NetworkPage-tBB-4Ox8.js → NetworkPage-C8OA9Mtp.js} +1 -1
  91. package/static/settings/assets/{PaneListHeader-DuD6glzn.js → PaneListHeader-COvlPJWj.js} +1 -1
  92. package/static/settings/assets/{SearchIndexingPanel-CiDHc0nx.js → SearchIndexingPanel-Bdd46mIV.js} +1 -1
  93. package/static/settings/assets/{StatusSubjectPanel-OF5cgWwt.js → StatusSubjectPanel-BuLml7qn.js} +1 -1
  94. package/static/settings/assets/{StatusWorkspace-CZ8Oa0gV.js → StatusWorkspace-CoMFRXoV.js} +1 -1
  95. package/static/settings/assets/SystemSettingsPage-DlzmaWV-.js +1 -0
  96. package/static/settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js +33 -0
  97. package/static/settings/assets/{activity-2M1f5eBS.js → activity-TLerXgMs.js} +1 -1
  98. package/static/settings/assets/admin-C6qCRFiY.js +1 -0
  99. package/static/settings/assets/{badge-CRKan0qX.js → badge-Bcs8iWrH.js} +1 -1
  100. package/static/settings/assets/{download-PCHldCdu.js → download-Ct3QmHPt.js} +1 -1
  101. package/static/settings/assets/{external-link-tmKe7eYO.js → external-link-C8KiHbss.js} +1 -1
  102. package/static/settings/assets/{index-Cek02M79.js → index-1Q2cRjZd.js} +1 -1
  103. package/static/settings/assets/{index-BDCNTwWz.js → index-DBO_Vq0P.js} +4 -4
  104. package/static/settings/assets/{index-browser-EVDVm9T5.js → index-browser-CazX1pc2.js} +1 -1
  105. package/static/settings/assets/{key-round-CUGvQCkt.js → key-round-B19YD1GA.js} +1 -1
  106. package/static/settings/assets/{rotate-ccw-BRA3lIHM.js → rotate-ccw-AQm2kMgU.js} +1 -1
  107. package/static/settings/assets/{settings-BdlKrzJ3.js → settings-C-VBtt9r.js} +129 -133
  108. package/static/settings/assets/settings-DWMMiFjh.css +1 -0
  109. package/static/settings/assets/settings-projection-CUkyprDQ.js +1 -0
  110. package/static/settings/assets/{skeleton-CuDeifWb.js → skeleton-DyZ1OBYf.js} +1 -1
  111. package/static/settings/assets/{waypoints-w6LxsFY1.js → waypoints-CC3wq-SU.js} +1 -1
  112. package/static/settings/assets/{workspace-layout-TN-BWmT5.js → workspace-layout-BxXNKgf9.js} +1 -1
  113. package/static/settings/settings.html +2 -2
  114. package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.d.ts +0 -54
  115. package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.js +0 -21
  116. package/static/auth-callback/assets/ModelsPage-DapY2Dwf.js +0 -10
  117. package/static/auth-callback/assets/SystemSettingsPage-C6qklJ9k.js +0 -1
  118. package/static/auth-callback/assets/SystemSettingsSubjectPanel-B4VV9xkG.js +0 -31
  119. package/static/auth-callback/assets/admin-BU4m6Ko2.js +0 -1
  120. package/static/auth-callback/assets/auth-callback-C_7MUXJb.css +0 -1
  121. package/static/dashboard/assets/ModelsPage-DZgAOdxO.js +0 -25
  122. package/static/dashboard/assets/SystemSettingsPage-qP0hLHNV.js +0 -1
  123. package/static/dashboard/assets/SystemSettingsSubjectPanel-EaWax9Ot.js +0 -31
  124. package/static/dashboard/assets/admin-BU4m6Ko2.js +0 -1
  125. package/static/dashboard/assets/dashboard-C_7MUXJb.css +0 -1
  126. package/static/settings/assets/ModelsPage-DHNiaodk.js +0 -10
  127. package/static/settings/assets/SystemSettingsPage-DT2tC0F_.js +0 -1
  128. package/static/settings/assets/SystemSettingsSubjectPanel-Bkta1oDw.js +0 -31
  129. package/static/settings/assets/admin-BU4m6Ko2.js +0 -1
  130. package/static/settings/assets/settings-C_7MUXJb.css +0 -1
@@ -42,5 +42,4 @@ export declare class ClientCredentialsAuthenticator implements Authenticator {
42
42
  canAuthenticate(request: IncomingMessage): boolean;
43
43
  authenticate(request: IncomingMessage): Promise<AuthResult>;
44
44
  private exchangeForToken;
45
- private isJwt;
46
45
  }
@@ -173,9 +173,6 @@ class ClientCredentialsAuthenticator {
173
173
  return { success: false, error: 'Token exchange failed' };
174
174
  }
175
175
  }
176
- isJwt(token) {
177
- return /^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+$/.test(token);
178
- }
179
176
  }
180
177
  exports.ClientCredentialsAuthenticator = ClientCredentialsAuthenticator;
181
178
  function tokenEndpointRoutingHeaders(tokenEndpoint, publicBaseUrl) {
@@ -3,4 +3,5 @@ export * from './AuthContext';
3
3
  export * from './SolidTokenAuthenticator';
4
4
  export * from './ClientCredentialsAuthenticator';
5
5
  export * from './ServiceTokenAuthenticator';
6
+ export * from './NodeTokenAuthenticator';
6
7
  export * from './MultiAuthenticator';
@@ -19,6 +19,6 @@ __exportStar(require("./AuthContext"), exports);
19
19
  __exportStar(require("./SolidTokenAuthenticator"), exports);
20
20
  __exportStar(require("./ClientCredentialsAuthenticator"), exports);
21
21
  __exportStar(require("./ServiceTokenAuthenticator"), exports);
22
- // export * from './NodeTokenAuthenticator'; // TODO: Fix later
22
+ __exportStar(require("./NodeTokenAuthenticator"), exports);
23
23
  __exportStar(require("./MultiAuthenticator"), exports);
24
24
  //# sourceMappingURL=index.js.map
@@ -4,16 +4,44 @@ export interface AccountRoleContext {
4
4
  webId?: string;
5
5
  roles: string[];
6
6
  }
7
+ export interface AccountRoleRepositoryOptions {
8
+ /**
9
+ * CSS 文件存储时代的账户目录(`.internal/accounts/data`)。
10
+ * 仅在迁移期兜底读取;默认按进程 CWD 惰性解析,桌面/集群部署应显式注入。
11
+ */
12
+ legacyAccountDataDir?: string;
13
+ }
7
14
  export declare class AccountRoleRepository {
8
15
  private readonly db;
9
16
  private readonly logger;
10
- constructor(db: IdentityDatabase);
17
+ private readonly legacyAccountDataDir?;
18
+ constructor(db: IdentityDatabase, options?: AccountRoleRepositoryOptions);
11
19
  findByAccountId(accountId: string): Promise<AccountRoleContext | undefined>;
12
20
  findByWebId(webId: string): Promise<AccountRoleContext | undefined>;
13
21
  findByWebIdLoose(webId: string): Promise<AccountRoleContext | undefined>;
14
22
  listAccounts(): Promise<AccountRoleContext[]>;
15
23
  addRoles(accountId: string, roles: string[]): Promise<void>;
16
- private getAccountById;
24
+ /**
25
+ * 按主键直查 identity_store 中的 account 行;表不存在时返回 undefined(由调用方走兜底)。
26
+ */
27
+ private getIdentityStoreAccount;
28
+ /**
29
+ * 按 CSS 行契约(webIdLink.accountId、pod.accountId、owner.podId)下推查询账户的 WebID。
30
+ */
31
+ private findWebIdsForAccount;
32
+ /**
33
+ * WebID → accountId 的下推解析:webIdLink 优先,其次 account 行内嵌 webId 字段,
34
+ * 最后 owner→pod 链路。全部 miss 返回 undefined(调用方再走全量兜底)。
35
+ */
36
+ private findAccountIdByWebId;
37
+ /**
38
+ * owner→pod 链路里把 owner 行的 podId 与 pod 行的 id 对齐。
39
+ */
40
+ private ownerPodJoin;
41
+ /**
42
+ * legacy 来源:internal_kv 与文件账户(迁移期兜底),不含 identity_store。
43
+ */
44
+ private loadLegacyAccounts;
17
45
  private loadAllAccounts;
18
46
  private loadIdentityStoreAccounts;
19
47
  private loadInternalKvAccounts;
@@ -9,7 +9,6 @@ const node_path_1 = __importDefault(require("node:path"));
9
9
  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
- const ACCOUNT_DATA_DIR = node_path_1.default.resolve('.internal', 'accounts', 'data');
13
12
  const IDENTITY_STORE_TABLE = 'identity_store';
14
13
  const INTERNAL_KV_TABLE = 'internal_kv';
15
14
  function isWebIdString(value) {
@@ -103,22 +102,39 @@ function parsePayload(value) {
103
102
  return typeof value === 'object' ? value : undefined;
104
103
  }
105
104
  class AccountRoleRepository {
106
- constructor(db) {
105
+ constructor(db, options = {}) {
107
106
  this.db = db;
108
107
  this.logger = (0, global_logger_factory_1.getLoggerFor)(this);
108
+ this.legacyAccountDataDir = options.legacyAccountDataDir;
109
109
  }
110
110
  async findByAccountId(accountId) {
111
- const record = await this.getAccountById(accountId);
112
- if (!record) {
111
+ // 快路径:按主键直查 account 行,再按 accountId 反查链接表,避免全表扫描。
112
+ const record = await this.getIdentityStoreAccount(accountId);
113
+ if (record) {
114
+ const linked = await this.findWebIdsForAccount(accountId);
115
+ const webId = linked[0] ?? resolveWebIds(record.payload)[0];
116
+ return { accountId, webId, roles: resolveRoles(record.payload) };
117
+ }
118
+ const legacy = await this.loadLegacyAccounts();
119
+ const fallback = legacy.get(accountId);
120
+ if (!fallback) {
113
121
  return undefined;
114
122
  }
115
- const [webId] = resolveWebIds(record.payload);
116
- return { accountId, webId, roles: resolveRoles(record.payload) };
123
+ return { accountId, webId: resolveWebIds(fallback.payload)[0], roles: resolveRoles(fallback.payload) };
117
124
  }
118
125
  async findByWebId(webId) {
119
126
  if (!isWebIdString(webId)) {
120
127
  return undefined;
121
128
  }
129
+ // 快路径:webIdLink / owner→pod 链路的字段下推查询。
130
+ const accountId = await this.findAccountIdByWebId(webId);
131
+ if (accountId) {
132
+ const context = await this.findByAccountId(accountId);
133
+ if (context) {
134
+ return { ...context, webId };
135
+ }
136
+ }
137
+ // 兜底:legacy kv/文件来源及历史 payload 形状,保持原有全扫描行为。
122
138
  const accounts = await this.loadAllAccounts();
123
139
  for (const { id, payload } of accounts.values()) {
124
140
  const knownWebIds = resolveWebIds(payload);
@@ -148,7 +164,10 @@ class AccountRoleRepository {
148
164
  if (unique.length === 0) {
149
165
  return;
150
166
  }
151
- const record = await this.getAccountById(accountId);
167
+ let record = await this.getIdentityStoreAccount(accountId);
168
+ if (!record) {
169
+ record = (await this.loadLegacyAccounts()).get(accountId);
170
+ }
152
171
  if (!record) {
153
172
  this.logger.warn(`Cannot add roles for unknown account ${accountId}`);
154
173
  return;
@@ -156,13 +175,130 @@ class AccountRoleRepository {
156
175
  const nextRoles = Array.from(new Set([...resolveRoles(record.payload), ...unique]));
157
176
  await this.updateAccountRecord(record, { ...record.payload, roles: nextRoles });
158
177
  }
159
- async getAccountById(accountId) {
160
- const accounts = await this.loadAllAccounts();
161
- return accounts.get(accountId);
178
+ /**
179
+ * 按主键直查 identity_store 中的 account 行;表不存在时返回 undefined(由调用方走兜底)。
180
+ */
181
+ async getIdentityStoreAccount(accountId) {
182
+ const tableId = drizzle_orm_1.sql.identifier(IDENTITY_STORE_TABLE);
183
+ try {
184
+ const result = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
185
+ SELECT payload
186
+ FROM ${tableId}
187
+ WHERE container = 'account' AND id = ${accountId}
188
+ LIMIT 1
189
+ `);
190
+ const payload = parsePayload(result.rows[0]?.payload);
191
+ return payload ? { id: accountId, payload, source: 'identity-store' } : undefined;
192
+ }
193
+ catch (error) {
194
+ if (!this.isTableMissing(error)) {
195
+ throw error;
196
+ }
197
+ return undefined;
198
+ }
162
199
  }
163
- async loadAllAccounts() {
200
+ /**
201
+ * 按 CSS 行契约(webIdLink.accountId、pod.accountId、owner.podId)下推查询账户的 WebID。
202
+ */
203
+ async findWebIdsForAccount(accountId) {
204
+ const tableId = drizzle_orm_1.sql.identifier(IDENTITY_STORE_TABLE);
205
+ const webIds = new Set();
206
+ try {
207
+ const links = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
208
+ SELECT payload FROM ${tableId}
209
+ WHERE container = 'webIdLink' AND ${(0, db_1.jsonFieldEquals)(this.db, 'accountId', accountId)}
210
+ `);
211
+ for (const row of links.rows) {
212
+ const webId = parsePayload(row.payload)?.webId;
213
+ if (isWebIdString(webId)) {
214
+ webIds.add(webId);
215
+ }
216
+ }
217
+ const owners = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
218
+ SELECT o.payload FROM ${tableId} o
219
+ WHERE o.container = 'owner' AND EXISTS (
220
+ SELECT 1 FROM ${tableId} p
221
+ WHERE p.container = 'pod'
222
+ AND ${(0, db_1.jsonFieldEquals)(this.db, 'accountId', accountId, 'p')}
223
+ AND ${this.ownerPodJoin()}
224
+ )
225
+ `);
226
+ for (const row of owners.rows) {
227
+ const webId = parsePayload(row.payload)?.webId;
228
+ if (isWebIdString(webId)) {
229
+ webIds.add(webId);
230
+ }
231
+ }
232
+ }
233
+ catch (error) {
234
+ if (!this.isTableMissing(error)) {
235
+ throw error;
236
+ }
237
+ }
238
+ return Array.from(webIds);
239
+ }
240
+ /**
241
+ * WebID → accountId 的下推解析:webIdLink 优先,其次 account 行内嵌 webId 字段,
242
+ * 最后 owner→pod 链路。全部 miss 返回 undefined(调用方再走全量兜底)。
243
+ */
244
+ async findAccountIdByWebId(webId) {
245
+ const tableId = drizzle_orm_1.sql.identifier(IDENTITY_STORE_TABLE);
246
+ try {
247
+ const links = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
248
+ SELECT payload FROM ${tableId}
249
+ WHERE container = 'webIdLink' AND ${(0, db_1.jsonFieldEquals)(this.db, 'webId', webId)}
250
+ LIMIT 1
251
+ `);
252
+ const linkedAccount = parsePayload(links.rows[0]?.payload)?.accountId;
253
+ if (typeof linkedAccount === 'string' && linkedAccount.length > 0) {
254
+ return linkedAccount;
255
+ }
256
+ for (const field of ['webId', 'webid', 'primaryWebId', 'primary_webid']) {
257
+ const accounts = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
258
+ SELECT id FROM ${tableId}
259
+ WHERE container = 'account' AND ${(0, db_1.jsonFieldEquals)(this.db, field, webId)}
260
+ LIMIT 1
261
+ `);
262
+ if (accounts.rows[0]?.id) {
263
+ return accounts.rows[0].id;
264
+ }
265
+ }
266
+ const owners = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
267
+ SELECT payload FROM ${tableId}
268
+ WHERE container = 'owner' AND ${(0, db_1.jsonFieldEquals)(this.db, 'webId', webId)}
269
+ LIMIT 1
270
+ `);
271
+ const podId = parsePayload(owners.rows[0]?.payload)?.podId;
272
+ if (typeof podId === 'string' && podId.length > 0) {
273
+ const pods = await (0, db_1.executeQuery)(this.db, (0, drizzle_orm_1.sql) `
274
+ SELECT payload FROM ${tableId}
275
+ WHERE container = 'pod' AND id = ${podId}
276
+ LIMIT 1
277
+ `);
278
+ const ownerAccount = parsePayload(pods.rows[0]?.payload)?.accountId;
279
+ if (typeof ownerAccount === 'string' && ownerAccount.length > 0) {
280
+ return ownerAccount;
281
+ }
282
+ }
283
+ }
284
+ catch (error) {
285
+ if (!this.isTableMissing(error)) {
286
+ throw error;
287
+ }
288
+ }
289
+ return undefined;
290
+ }
291
+ /**
292
+ * owner→pod 链路里把 owner 行的 podId 与 pod 行的 id 对齐。
293
+ */
294
+ ownerPodJoin() {
295
+ return (0, drizzle_orm_1.sql) `${(0, db_1.jsonFieldExtract)(this.db, 'podId', 'o')} = p.id`;
296
+ }
297
+ /**
298
+ * legacy 来源:internal_kv 与文件账户(迁移期兜底),不含 identity_store。
299
+ */
300
+ async loadLegacyAccounts() {
164
301
  const accounts = new Map();
165
- await this.loadIdentityStoreAccounts(accounts);
166
302
  await this.loadInternalKvAccounts(accounts);
167
303
  for (const [id, payload] of await this.loadFileAccountMap()) {
168
304
  if (!accounts.has(id)) {
@@ -171,6 +307,16 @@ class AccountRoleRepository {
171
307
  }
172
308
  return accounts;
173
309
  }
310
+ async loadAllAccounts() {
311
+ const accounts = new Map();
312
+ await this.loadIdentityStoreAccounts(accounts);
313
+ for (const [id, record] of await this.loadLegacyAccounts()) {
314
+ if (!accounts.has(id)) {
315
+ accounts.set(id, record);
316
+ }
317
+ }
318
+ return accounts;
319
+ }
174
320
  async loadIdentityStoreAccounts(accounts) {
175
321
  const tableId = drizzle_orm_1.sql.identifier(IDENTITY_STORE_TABLE);
176
322
  let rows = [];
@@ -271,14 +417,16 @@ class AccountRoleRepository {
271
417
  }
272
418
  }
273
419
  async loadFileAccountMap() {
420
+ // 目录在使用时才解析(而不是模块加载时):桌面/测试进程的 CWD 与模块加载时机无关。
421
+ const accountDataDir = this.legacyAccountDataDir ?? node_path_1.default.resolve('.internal', 'accounts', 'data');
274
422
  const map = new Map();
275
423
  try {
276
- const files = await node_fs_1.promises.readdir(ACCOUNT_DATA_DIR);
424
+ const files = await node_fs_1.promises.readdir(accountDataDir);
277
425
  for (const file of files) {
278
426
  if (!file.endsWith('.json')) {
279
427
  continue;
280
428
  }
281
- const fullPath = node_path_1.default.join(ACCOUNT_DATA_DIR, file);
429
+ const fullPath = node_path_1.default.join(accountDataDir, file);
282
430
  try {
283
431
  const raw = await node_fs_1.promises.readFile(fullPath, 'utf8');
284
432
  const parsed = JSON.parse(raw);
@@ -297,7 +445,7 @@ class AccountRoleRepository {
297
445
  }
298
446
  }
299
447
  catch (error) {
300
- this.logger.debug(`Account data directory unavailable (${ACCOUNT_DATA_DIR}): ${error.message}`);
448
+ this.logger.debug(`Account data directory unavailable (${accountDataDir}): ${error.message}`);
301
449
  }
302
450
  return map;
303
451
  }
@@ -14,6 +14,12 @@ export interface PodLookupResult {
14
14
  *
15
15
  * Reads Pod facts from the canonical identity_store table and from CSS
16
16
  * WrappedIndexedStorage rows when that storage backend is active.
17
+ *
18
+ * 点查(findById / findAllByWebId / findByWebIds / listByAccountId)走字段下推
19
+ * 快路径,避免每请求全扫 identity_store;KV 历史账户记录独立读取,
20
+ * 同 ID 的 canonical 记录整条优先,包括无法解析的记录,不能恢复旧身份。
21
+ * findByResourceIdentifier / listAllPods 本质上需要
22
+ * 候选全集,仍保留全量读取(identity_store 侧用反向前缀 LIKE 下推预筛)。
17
23
  */
18
24
  export declare class PodLookupRepository {
19
25
  private readonly db;
@@ -22,22 +28,27 @@ export declare class PodLookupRepository {
22
28
  constructor(db: IdentityDatabase, kvTableName?: string);
23
29
  /**
24
30
  * Find Pod by resource path (matches longest canonical storage/base URL prefix).
31
+ *
32
+ * kv 账户行无法按 URL 下推,保持全量解析(cloud 部署 kv 为空,local 规模有限)。
25
33
  */
26
34
  findByResourceIdentifier(resourcePath: string): Promise<PodLookupResult | undefined>;
27
35
  /**
28
36
  * Get Pod by ID.
37
+ *
38
+ * 快路径按主键直查 identity_store 的 pod 行并补齐 owner/node 关联;
39
+ * 独立读取 KV 历史记录,并按原始 ID 检查 canonical 遮蔽。
29
40
  */
30
41
  findById(podId: string): Promise<PodLookupResult | undefined>;
31
42
  /**
32
- * Find Pod by a linked WebID URL.
43
+ * Find Pod by an explicit owner or pod.webId URL.
33
44
  *
34
- * CSS account data stores WebID links separately from Pod base URLs. This is
45
+ * Pod ownership stores WebIDs separately from Pod base URLs. This is
35
46
  * the precise lookup for IdP/SP split deployments where the WebID path does
36
47
  * not have to match the storage base URL.
37
48
  */
38
49
  findByWebId(webId: string): Promise<PodLookupResult | undefined>;
39
50
  /**
40
- * Find all Pods linked to a WebID.
51
+ * Find all Pods explicitly owned by a WebID.
41
52
  *
42
53
  * A Cloud WebID can legitimately back both a Cloud Pod and a Local SP Pod.
43
54
  * Callers that are scoped to a storage provider must inspect all candidates
@@ -45,11 +56,14 @@ export declare class PodLookupRepository {
45
56
  */
46
57
  findAllByWebId(webId: string): Promise<PodLookupResult[]>;
47
58
  /**
48
- * Find Pods by linked WebID URLs in one scan.
59
+ * Find Pods by explicit WebID bindings with batched canonical queries.
49
60
  */
50
61
  findByWebIds(webIds: string[]): Promise<PodLookupResult[]>;
51
62
  /**
52
63
  * List Pods for a specific account.
64
+ *
65
+ * 定向查询与全扫描等价:kv 按账户键直取,identity_store 按 accountId 下推,
66
+ * canonical 整条优先与 getAllPods 一致,KV 扫描补齐 .json 等历史键。
53
67
  */
54
68
  listByAccountId(accountId: string): Promise<PodLookupResult[]>;
55
69
  /**
@@ -60,21 +74,50 @@ export declare class PodLookupRepository {
60
74
  * Extract all pods from the configured CSS identity storage.
61
75
  */
62
76
  private getAllPods;
77
+ private getLegacyPods;
78
+ private extractKvPods;
63
79
  private getAccountRowsFromKv;
64
80
  private getNodeAssignments;
81
+ private mergeCanonicalPods;
82
+ private extractPodsFromAccountData;
65
83
  /**
66
- * Fast path for CSS WrappedIndexedStorage. WebID indexes point to the root
67
- * account id, so a single indexed key plus account data row can resolve the
68
- * profile without scanning all account records.
84
+ * 与全扫描一致的 WebID 匹配过滤:按 Pod 解析后的 WebID 列表按原字符串比较,
85
+ * 命中时附加该 Pod 自身存储的 WebID。
69
86
  */
70
- private findByWebIdIndex;
71
- private readAccountData;
72
- private readStringArrayFromKv;
73
- private readKvValue;
74
- private extractPodsFromAccountData;
87
+ private filterPodsByWebIds;
75
88
  /**
76
89
  * DrizzleIndexedStorage stores CSS identity facts as typed rows in
77
90
  * identity_store; this is the canonical clustered identity source.
78
91
  */
79
92
  private getPodsFromIndexedStore;
93
+ /**
94
+ * 按主键直查 identity_store pod 行并补齐关联。
95
+ */
96
+ private getIndexedStorePodsByIds;
97
+ /**
98
+ * 按 payload.accountId 下推查询 identity_store pod 行并补齐关联。
99
+ */
100
+ private getIndexedStorePodsByAccountId;
101
+ /**
102
+ * 反向前缀 LIKE 预筛候选 pod 行(baseUrl/storageUrl/storage 任一前缀),
103
+ * JS 侧 startsWith 复核由调用方(findByResourceIdentifier)执行。
104
+ */
105
+ private findIndexedStorePodsByResource;
106
+ /**
107
+ * WebID → Pod 的下推解析:owner 按 webId 命中拿 podId,pod.webId
108
+ * 直查;Account webIdLink 不建立 Pod ownership。
109
+ * webIdForms 只携带完整原字符串;URL 等价不代表相同身份。
110
+ */
111
+ private findIndexedStorePodsByWebIds;
112
+ /**
113
+ * 为候选 pod 行补齐 owner 关联与 nodeAssignments,组装最终结果。
114
+ * 与全扫描共用 buildIndexedStorePods,保证返回形状一致。
115
+ */
116
+ private enrichIndexedStorePods;
117
+ private queryIndexedStoreRows;
118
+ /**
119
+ * 组装 identity_store Pod 视图:pod 行为主体,owner 行补充 WebID,
120
+ * nodeAssignments 兜底 nodeId。全扫描与下推快路径共用,保证返回形状一致。
121
+ */
122
+ private buildIndexedStorePods;
80
123
  }