feat: appove and go

This commit is contained in:
thanhnv
2026-07-19 09:37:16 +07:00
parent 13fae3e6c3
commit 709b6cccd6
24 changed files with 1245 additions and 70 deletions
@@ -83,7 +83,11 @@ export class ApprovalsService {
list(actor: SettingsActor, status = 'pending') {
this.requireRbac(actor, 'monitoring', 'read');
const res = runFile('python3', [INBOX_CLI, 'list', '--status', status], this.tenantEnv(actor));
return { ...parseJson<Record<string, any>>(res.stdout, { count: 0, proposals: [], oversight: [] }), audit_verify: this.verifyAudit(actor) };
const inbox = parseJson<{ proposals?: Array<Record<string, any>>; oversight?: Array<Record<string, any>> }>(res.stdout, { proposals: [], oversight: [] });
const proposals = (inbox.proposals ?? []).filter((proposal) => this.canAccessProject(actor, String(proposal.project ?? actor.project)));
const visibleIds = new Set(proposals.map((proposal) => String(proposal.id ?? '')));
const oversight = (inbox.oversight ?? []).filter((record) => visibleIds.has(String(record.proposal_id ?? '')));
return { ...inbox, count: proposals.length, proposals, oversight, audit_verify: this.verifyAudit(actor) };
}
submit(input: ApprovalSubmit, actor: SettingsActor) {
@@ -122,9 +126,9 @@ export class ApprovalsService {
if (!input.id || !input.decision || !input.reason) {
throw new ForbiddenException('APPROVAL_DECIDE_DENY id/decision/reason required');
}
this.requireRbac(actor, 'approval', 'grant');
try {
const pending = this.findProposal(input.id, actor);
this.requireRbac(actor, 'approval', 'grant', String(pending.project ?? actor.project));
await this.verifyApprovalIdentity(input, actor, pending);
const res = runFile('python3', [
INBOX_CLI,
@@ -281,7 +285,16 @@ export class ApprovalsService {
}
}
private requireRbac(actor: SettingsActor, resource: string, action: string) {
private canAccessProject(actor: SettingsActor, targetProject: string): boolean {
try {
this.requireRbac(actor, 'monitoring', 'read', targetProject);
return true;
} catch {
return false;
}
}
private requireRbac(actor: SettingsActor, resource: string, action: string, targetProject = actor.project) {
try {
runFile('python3', [
RBAC_CLI,
@@ -295,7 +308,7 @@ export class ApprovalsService {
'--role-project',
actor.project,
'--target-project',
actor.project,
targetProject,
'--role-tenant',
actor.tenant,
'--target-tenant',