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:
rootandClaude Opus 5.5 committed 2026-09-28 15:18:40 +08:00
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);
});
});