feat: complete TASK-WP2-04 credit ledger

This commit is contained in:
suyx
2026-08-02 23:27:10 +08:00
parent 9b4860cfc2
commit 4551d73e76
24 changed files with 3002 additions and 6 deletions
+25
View File
@@ -990,6 +990,11 @@ export class RegistrationService {
return { userId: session.user_id };
}
authorizeAdminMutation(input: { csrfToken: string; sessionToken: string }) {
const session = this.authenticatedAdminMutationSession(input.sessionToken, input.csrfToken, this.options.clock());
return { userId: session.user_id };
}
logoutUser(input: { csrfToken: string; sessionToken: string }) {
const now = this.options.clock();
this.runImmediate("session_revoke", () => {
@@ -1531,6 +1536,26 @@ export class RegistrationService {
return session;
}
private authenticatedAdminMutationSession(sessionToken: string, csrfToken: string, now: number) {
const session = this.database.prepare(`
SELECT s.session_id, s.user_id, s.csrf_token_digest
FROM sessions s
JOIN users u ON u.user_id = s.user_id
JOIN admin_access a ON a.user_id = u.user_id
WHERE s.token_digest = ? AND s.audience = 'admin' AND s.revoked_at IS NULL
AND s.expires_at > ? AND u.role = 'super_admin' AND u.status = 'active' AND a.allowed = 1
`).get(digest(sessionToken), now) as {
csrf_token_digest: string | null;
session_id: string;
user_id: string;
} | undefined;
if (!session) throw new RegistrationError("AUTH_SESSION_INVALID", "session_invalid");
if (!session.csrf_token_digest || !constantTimeTextEqual(session.csrf_token_digest, digest(csrfToken))) {
throw new RegistrationError("AUTH_CSRF_INVALID", "csrf_invalid");
}
return session;
}
private queueOwnedManagedFiles(userId: string, now: number) {
const table = this.database.prepare(`
SELECT 1 AS present FROM sqlite_master WHERE type = 'table' AND name = 'managed_files'