@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.
- package/dist/api/auth/ClientCredentialsAuthenticator.d.ts +0 -1
- package/dist/api/auth/ClientCredentialsAuthenticator.js +0 -3
- package/dist/api/auth/index.d.ts +1 -0
- package/dist/api/auth/index.js +1 -1
- package/dist/identity/drizzle/AccountRoleRepository.d.ts +30 -2
- package/dist/identity/drizzle/AccountRoleRepository.js +163 -15
- package/dist/identity/drizzle/PodLookupRepository.d.ts +55 -12
- package/dist/identity/drizzle/PodLookupRepository.js +225 -243
- package/dist/identity/drizzle/db.d.ts +19 -0
- package/dist/identity/drizzle/db.js +39 -4
- package/node_modules/@undefineds.co/ai-connections/dist/AiConnectionsMain.js +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/controller.d.ts +1 -0
- package/node_modules/@undefineds.co/ai-connections/dist/controller.js +94 -16
- package/node_modules/@undefineds.co/shared-ui/dist/index.d.ts +0 -1
- package/node_modules/@undefineds.co/shared-ui/dist/index.js +0 -1
- package/node_modules/@undefineds.co/solid-sdk/dist/index.cjs +112 -11
- package/node_modules/@undefineds.co/solid-sdk/dist/session.d.ts +2 -0
- package/node_modules/@undefineds.co/solid-sdk/dist/session.js +135 -11
- package/package.json +2 -2
- package/static/app/assets/main.css +1 -1
- package/static/app/assets/main.js +12 -13
- package/static/auth-callback/assets/{AiConfigContext-DJKvxduw.js → AiConfigContext-DqWRSm7n.js} +1 -1
- package/static/auth-callback/assets/{AiConfigPage-DgGmHb18.js → AiConfigPage-C5YYyvzl.js} +1 -1
- package/static/auth-callback/assets/{DocumentProcessingPanel-CJmxu8x1.js → DocumentProcessingPanel-DZCiacBz.js} +1 -1
- package/static/auth-callback/assets/{IndexLifecyclePanel-byCa7h7L.js → IndexLifecyclePanel-CCLeOljB.js} +1 -1
- package/static/auth-callback/assets/{IndexSubjectPanel-CDwT4Yx1.js → IndexSubjectPanel-BbUqop4b.js} +1 -1
- package/static/auth-callback/assets/{ModelAssignmentsPanel-CwM3k8w7.js → ModelAssignmentsPanel-jUUTDKuh.js} +1 -1
- package/static/auth-callback/assets/ModelsPage-B08zdtff.js +10 -0
- package/static/auth-callback/assets/{NetworkPage-DkVgxtNj.js → NetworkPage-Vp2PrMxs.js} +1 -1
- package/static/auth-callback/assets/{PaneListHeader-DRI0hjmt.js → PaneListHeader-DY7baSFl.js} +1 -1
- package/static/auth-callback/assets/{SearchIndexingPanel-mbppDZFE.js → SearchIndexingPanel-D4YRg1-8.js} +1 -1
- package/static/auth-callback/assets/{StatusSubjectPanel-DNsW76aC.js → StatusSubjectPanel-ArIS-TxT.js} +1 -1
- package/static/auth-callback/assets/{StatusWorkspace-DGcTbHT5.js → StatusWorkspace-Cp5LQ9Yx.js} +1 -1
- package/static/auth-callback/assets/SystemSettingsPage-BDvT1NUz.js +1 -0
- package/static/auth-callback/assets/SystemSettingsSubjectPanel-CDqRuC4t.js +33 -0
- package/static/auth-callback/assets/{activity-Ck-Mfxxo.js → activity-D1Dsfa_0.js} +1 -1
- package/static/auth-callback/assets/admin-C6qCRFiY.js +1 -0
- package/static/auth-callback/assets/{auth-callback-DG4Ge40c.js → auth-callback-Cmf4hXVZ.js} +105 -109
- package/static/auth-callback/assets/auth-callback-DWMMiFjh.css +1 -0
- package/static/auth-callback/assets/{badge-GSSnw1VV.js → badge-CZsHq3Ih.js} +1 -1
- package/static/auth-callback/assets/{download-BYyRX-K2.js → download-DU9ntlHw.js} +1 -1
- package/static/auth-callback/assets/{external-link-WEC0Ki-0.js → external-link-BfzaaZDx.js} +1 -1
- package/static/auth-callback/assets/{index-kju2k4Dp.js → index-DSNnLUbU.js} +4 -4
- package/static/auth-callback/assets/{index-DlsMCgo8.js → index-FHhHoKgp.js} +1 -1
- package/static/auth-callback/assets/{index-browser-D8YW6coF.js → index-browser-BNFqqC74.js} +1 -1
- package/static/auth-callback/assets/{key-round-DJUT5Nh0.js → key-round-BwP3DBWo.js} +1 -1
- package/static/auth-callback/assets/{rotate-ccw-CXwfZUKU.js → rotate-ccw-VMwnmyab.js} +1 -1
- package/static/auth-callback/assets/settings-projection-CUkyprDQ.js +1 -0
- package/static/auth-callback/assets/{skeleton-DWPtNhUB.js → skeleton-CoD55rBT.js} +1 -1
- package/static/auth-callback/assets/{waypoints-l1oDnRJ0.js → waypoints-DRo_1Ca7.js} +1 -1
- package/static/auth-callback/assets/{workspace-layout-B7iKpK-Q.js → workspace-layout--mcXl3My.js} +1 -1
- package/static/auth-callback/auth-callback.html +2 -2
- package/static/dashboard/assets/{AiConfigContext-U0cCSY28.js → AiConfigContext-BpPDKSzI.js} +1 -1
- package/static/dashboard/assets/{AiConfigPage-CMi0uJdo.js → AiConfigPage-wsqEZDIX.js} +1 -1
- package/static/dashboard/assets/{DocumentProcessingPanel-DEZiEpft.js → DocumentProcessingPanel-CDSy3juc.js} +1 -1
- package/static/dashboard/assets/{IndexLifecyclePanel-CGZfLUog.js → IndexLifecyclePanel-CVd6vQCX.js} +1 -1
- package/static/dashboard/assets/{IndexSubjectPanel-DiDZauHk.js → IndexSubjectPanel-B2cXwl8X.js} +1 -1
- package/static/dashboard/assets/{ModelAssignmentsPanel-DaGGZKuf.js → ModelAssignmentsPanel-BeBZhnQd.js} +1 -1
- package/static/dashboard/assets/ModelsPage-B6McSCJw.js +25 -0
- package/static/dashboard/assets/{NetworkPage-DI5-O7GT.js → NetworkPage-DV2V0qWO.js} +1 -1
- package/static/dashboard/assets/{PaneListHeader-4oa7uf59.js → PaneListHeader-D0c1yCFH.js} +1 -1
- package/static/dashboard/assets/{SearchIndexingPanel-DoqEwocD.js → SearchIndexingPanel-CGs0OZ12.js} +1 -1
- package/static/dashboard/assets/{StatusSubjectPanel-CLzBC5fu.js → StatusSubjectPanel-oPSECj7i.js} +1 -1
- package/static/dashboard/assets/{StatusWorkspace-UyKADZoM.js → StatusWorkspace-PdovQPKa.js} +1 -1
- package/static/dashboard/assets/SystemSettingsPage-CtNDklkx.js +1 -0
- package/static/dashboard/assets/SystemSettingsSubjectPanel-WaE8mTQi.js +33 -0
- package/static/dashboard/assets/{activity-CjIoNU4S.js → activity-B9ylSoyr.js} +1 -1
- package/static/dashboard/assets/admin-C6qCRFiY.js +1 -0
- package/static/dashboard/assets/{badge-dcDZpKh3.js → badge-BNbFbpIb.js} +1 -1
- package/static/dashboard/assets/{dashboard-BY8GdnEI.js → dashboard-CD-FFVRD.js} +129 -133
- package/static/dashboard/assets/dashboard-DWMMiFjh.css +1 -0
- package/static/dashboard/assets/{download-BeYCrc-_.js → download-nOP38LtW.js} +1 -1
- package/static/dashboard/assets/{external-link-626-5KVx.js → external-link-Bh2ewN9E.js} +1 -1
- package/static/dashboard/assets/{index-CBbk6hx-.js → index-PgS-Bt7X.js} +1 -1
- package/static/dashboard/assets/{index-CsxH6Hmz.js → index-QN2QgEwr.js} +4 -4
- package/static/dashboard/assets/{key-round-B9IL4Wua.js → key-round-bGmguBLv.js} +1 -1
- package/static/dashboard/assets/{rotate-ccw-DM4vCfiI.js → rotate-ccw-D2x__ODW.js} +1 -1
- package/static/dashboard/assets/settings-projection-CUkyprDQ.js +1 -0
- package/static/dashboard/assets/{skeleton-ChlFAPvP.js → skeleton-VOqft8WS.js} +1 -1
- package/static/dashboard/assets/{waypoints-CvpZzf_I.js → waypoints-fgRqm4oH.js} +1 -1
- package/static/dashboard/assets/{workspace-layout-Bxv7V-tt.js → workspace-layout-DV4dgeHX.js} +1 -1
- package/static/dashboard/dashboard.html +2 -2
- package/static/settings/assets/{AiConfigContext-CDVIHLBb.js → AiConfigContext-zKQYdfai.js} +1 -1
- package/static/settings/assets/{AiConfigPage-C7qfiiaJ.js → AiConfigPage-Bs1EE3Ei.js} +1 -1
- package/static/settings/assets/{DocumentProcessingPanel-B59QTe-V.js → DocumentProcessingPanel-CSFfdOXS.js} +1 -1
- package/static/settings/assets/{IndexLifecyclePanel-C-e-3eVi.js → IndexLifecyclePanel-BR9Su49B.js} +1 -1
- package/static/settings/assets/{IndexSubjectPanel-B-scRZ1u.js → IndexSubjectPanel-BgbI9c-a.js} +1 -1
- package/static/settings/assets/{ModelAssignmentsPanel-Wy8jzVg4.js → ModelAssignmentsPanel-C0BE7nbk.js} +1 -1
- package/static/settings/assets/ModelsPage-CU-YQ8_7.js +10 -0
- package/static/settings/assets/{NetworkPage-tBB-4Ox8.js → NetworkPage-C8OA9Mtp.js} +1 -1
- package/static/settings/assets/{PaneListHeader-DuD6glzn.js → PaneListHeader-COvlPJWj.js} +1 -1
- package/static/settings/assets/{SearchIndexingPanel-CiDHc0nx.js → SearchIndexingPanel-Bdd46mIV.js} +1 -1
- package/static/settings/assets/{StatusSubjectPanel-OF5cgWwt.js → StatusSubjectPanel-BuLml7qn.js} +1 -1
- package/static/settings/assets/{StatusWorkspace-CZ8Oa0gV.js → StatusWorkspace-CoMFRXoV.js} +1 -1
- package/static/settings/assets/SystemSettingsPage-DlzmaWV-.js +1 -0
- package/static/settings/assets/SystemSettingsSubjectPanel-DExjDo_v.js +33 -0
- package/static/settings/assets/{activity-2M1f5eBS.js → activity-TLerXgMs.js} +1 -1
- package/static/settings/assets/admin-C6qCRFiY.js +1 -0
- package/static/settings/assets/{badge-CRKan0qX.js → badge-Bcs8iWrH.js} +1 -1
- package/static/settings/assets/{download-PCHldCdu.js → download-Ct3QmHPt.js} +1 -1
- package/static/settings/assets/{external-link-tmKe7eYO.js → external-link-C8KiHbss.js} +1 -1
- package/static/settings/assets/{index-Cek02M79.js → index-1Q2cRjZd.js} +1 -1
- package/static/settings/assets/{index-BDCNTwWz.js → index-DBO_Vq0P.js} +4 -4
- package/static/settings/assets/{index-browser-EVDVm9T5.js → index-browser-CazX1pc2.js} +1 -1
- package/static/settings/assets/{key-round-CUGvQCkt.js → key-round-B19YD1GA.js} +1 -1
- package/static/settings/assets/{rotate-ccw-BRA3lIHM.js → rotate-ccw-AQm2kMgU.js} +1 -1
- package/static/settings/assets/{settings-BdlKrzJ3.js → settings-C-VBtt9r.js} +129 -133
- package/static/settings/assets/settings-DWMMiFjh.css +1 -0
- package/static/settings/assets/settings-projection-CUkyprDQ.js +1 -0
- package/static/settings/assets/{skeleton-CuDeifWb.js → skeleton-DyZ1OBYf.js} +1 -1
- package/static/settings/assets/{waypoints-w6LxsFY1.js → waypoints-CC3wq-SU.js} +1 -1
- package/static/settings/assets/{workspace-layout-TN-BWmT5.js → workspace-layout-BxXNKgf9.js} +1 -1
- package/static/settings/settings.html +2 -2
- package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.d.ts +0 -54
- package/node_modules/@undefineds.co/shared-ui/dist/oidc-consent.js +0 -21
- package/static/auth-callback/assets/ModelsPage-DapY2Dwf.js +0 -10
- package/static/auth-callback/assets/SystemSettingsPage-C6qklJ9k.js +0 -1
- package/static/auth-callback/assets/SystemSettingsSubjectPanel-B4VV9xkG.js +0 -31
- package/static/auth-callback/assets/admin-BU4m6Ko2.js +0 -1
- package/static/auth-callback/assets/auth-callback-C_7MUXJb.css +0 -1
- package/static/dashboard/assets/ModelsPage-DZgAOdxO.js +0 -25
- package/static/dashboard/assets/SystemSettingsPage-qP0hLHNV.js +0 -1
- package/static/dashboard/assets/SystemSettingsSubjectPanel-EaWax9Ot.js +0 -31
- package/static/dashboard/assets/admin-BU4m6Ko2.js +0 -1
- package/static/dashboard/assets/dashboard-C_7MUXJb.css +0 -1
- package/static/settings/assets/ModelsPage-DHNiaodk.js +0 -10
- package/static/settings/assets/SystemSettingsPage-DT2tC0F_.js +0 -1
- package/static/settings/assets/SystemSettingsSubjectPanel-Bkta1oDw.js +0 -31
- package/static/settings/assets/admin-BU4m6Ko2.js +0 -1
- package/static/settings/assets/settings-C_7MUXJb.css +0 -1
|
@@ -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) {
|
package/dist/api/auth/index.d.ts
CHANGED
package/dist/api/auth/index.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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 (${
|
|
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
|
|
43
|
+
* Find Pod by an explicit owner or pod.webId URL.
|
|
33
44
|
*
|
|
34
|
-
*
|
|
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
|
|
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
|
|
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
|
-
*
|
|
67
|
-
*
|
|
68
|
-
* profile without scanning all account records.
|
|
84
|
+
* 与全扫描一致的 WebID 匹配过滤:按 Pod 解析后的 WebID 列表按原字符串比较,
|
|
85
|
+
* 命中时附加该 Pod 自身存储的 WebID。
|
|
69
86
|
*/
|
|
70
|
-
private
|
|
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
|
}
|