feat(sources): site center backend (credentials, refresh login, user data, health check, delete)
PUT /sites/{id}/credentials by kind (moved from auth-settings, legacy body
accepted), POST refresh-login (async task or wait), POST health-check,
GET user-data history, DELETE /sites/{id} (soft delete, releases kept),
DELETE /site-accounts/{id}, GET /sites?include=accounts; refreshSiteAccount
task with login-failure decisions and auto re-login on auth_expired;
scheduled jobs sites.user_data (6h) and sites.health_check (1h);
settings.sites defaults for rate limit, snapshot interval, UA and proxy.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d6fca25679
commit
05a7706dde
15 files changed
+1602
-179
No files matched your search
@@ -238,43 +238,6 @@ describe.skipIf(!hasPg)("W2-A 管理 API(真实 PG)", () => {
|
||||
expect(v.verifyDetail.code).toBe("ENOENT");
|
||||
expect((await admin("DELETE", `/path-mappings/${m.id}`)).status).toBe(200);
|
||||
});
|
||||
|
||||
it("PUT /sites/{id}/credentials:新建/替换账号凭证,不回显,旧 secret 删除;未知站点 404", async () => {
|
||||
const site = await sql<{ id: string }>`insert into site (key, name, adapter, base_url)
|
||||
values ('audiences', '观众', 'nexusphp', 'https://audiences.me') returning id`.execute(h.db);
|
||||
const siteId = site.rows[0]!.id;
|
||||
const r1 = await json(
|
||||
await admin("PUT", `/sites/${siteId}/credentials`, {
|
||||
authKind: "cookie",
|
||||
credential: "c_secure_uid=1; pass=abc",
|
||||
}),
|
||||
);
|
||||
expect(r1.data).toMatchObject({ created: true, label: "default", authKind: "cookie" });
|
||||
expect(JSON.stringify(r1)).not.toContain("pass=abc");
|
||||
expect(await h.ctx.secrets.resolve(r1.data.secretRef)).toBe("c_secure_uid=1; pass=abc");
|
||||
const r2 = await json(
|
||||
await admin("PUT", `/sites/${siteId}/credentials`, {
|
||||
authKind: "password",
|
||||
credential: "pw-123456",
|
||||
username: "me",
|
||||
}),
|
||||
);
|
||||
expect(r2.data).toMatchObject({ created: false, siteAccountId: r1.data.siteAccountId });
|
||||
await expect(h.ctx.secrets.resolve(r1.data.secretRef)).rejects.toThrow(/不存在/);
|
||||
const acc = await sql<{ auth_kind: string; user_info: { username?: string } }>`
|
||||
select auth_kind, user_info from site_account where id = ${r1.data.siteAccountId}`.execute(h.db);
|
||||
expect(acc.rows[0]).toMatchObject({ auth_kind: "password", user_info: { username: "me" } });
|
||||
expect(
|
||||
(
|
||||
await admin("PUT", "/sites/00000000-0000-4000-8000-000000000000/credentials", {
|
||||
authKind: "cookie",
|
||||
credential: "x",
|
||||
})
|
||||
).status,
|
||||
).toBe(404);
|
||||
noLeak("pass=abc");
|
||||
noLeak("pw-123456");
|
||||
});
|
||||
});
|
||||
|
||||
describe("审计查询", () => {
|
||||
@@ -289,11 +252,9 @@ describe.skipIf(!hasPg)("W2-A 管理 API(真实 PG)", () => {
|
||||
const ids = [...p1.data.items, ...p2.data.items].map((e: { id: string }) => e.id);
|
||||
expect(new Set(ids).size).toBe(6);
|
||||
expect(p1.data.items[0].at >= p2.data.items[0].at).toBe(true);
|
||||
const t = await json(await admin("GET", "/audit-events?targetType=site_account"));
|
||||
expect(t.data.items.map((e: { action: string }) => e.action)).toEqual([
|
||||
"site_account.credentials_set",
|
||||
"site_account.credentials_set",
|
||||
]);
|
||||
const t = await json(await admin("GET", "/audit-events?targetType=api_key&limit=200"));
|
||||
expect(t.data.items.length).toBeGreaterThan(0);
|
||||
expect(t.data.items.every((e: { action: string }) => e.action.startsWith("api_key."))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user