feat: per-connection publish governance — auto, 1-admin, majority, consensus
Per the owner's spec: each social connection gets publishGovernance in its fields — unset = manual publish (today), auto = dispatch on approval, admin1/admin_majority/admin_consensus = a publish-phase chatgov poll (subject draft_publish, its own open-poll slot, no collision with review gates). Dispatch holds until the publish poll passes (E3 pre-dispatch check); auto dispatches on approval. Includes migration 033, the PATCH /connections/:id/publish-governance route (chatgov.manage), and 19+31 new tests. Implementation by the branding-panel process (claude/opus), lint and integration fixes here.
This commit is contained in:
parent
f2eb14956e
commit
b89dea5c93
26 changed files with 1637 additions and 31 deletions
|
|
@ -1,9 +1,11 @@
|
|||
import { Body, Controller, Delete, Get, Inject, Param, Post, Query, Req, Res, UseGuards } from "@nestjs/common";
|
||||
import { BadRequestException, Body, Controller, Delete, Get, Inject, Param, Patch, Post, Query, Req, Res, UseGuards } from "@nestjs/common";
|
||||
import type { Request, Response } from "express";
|
||||
import { RbacGuard, RequirePermission, type AuthenticatedRequest } from "../rbac/rbac.guard.js";
|
||||
import { optionalString, parseId, requireObject, requireSession, requireString } from "../http/validation.js";
|
||||
import { ConnectionsService, type ConnectionSummary } from "./connections.service.js";
|
||||
import { AUTH_TTL_MS, stateCookieName, validState } from "./pending.js";
|
||||
import { CHATGOV_PERMISSIONS } from "../chatgov/policy.js";
|
||||
import { PUBLISH_GOVERNANCE_MODES, type PublishGovernance } from "../publishing/publish-governance.js";
|
||||
|
||||
@Controller("connections")
|
||||
export class ConnectionsController {
|
||||
|
|
@ -74,6 +76,19 @@ export class ConnectionsController {
|
|||
return this.redirectResponse(response, await this.connections.reconnect(requireSession(request), parseId(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish governance for one connection: auto | admin1 | admin_majority |
|
||||
* admin_consensus, or null for manual publishing. Deciding who must sign
|
||||
* off a publish is a governance rule, so it needs chatgov.manage.
|
||||
*/
|
||||
@Patch(":connectionId/publish-governance")
|
||||
@UseGuards(RbacGuard)
|
||||
@RequirePermission(CHATGOV_PERMISSIONS.manage)
|
||||
async publishGovernance(@Req() request: Request & AuthenticatedRequest, @Param("connectionId") id: string, @Body() body: unknown) {
|
||||
const input = requireObject(body, "publish governance");
|
||||
return this.connections.setPublishGovernance(requireSession(request), parseId(id), parsePublishGovernance(input.publishGovernance));
|
||||
}
|
||||
|
||||
@Delete(":connectionId")
|
||||
@UseGuards(RbacGuard)
|
||||
@RequirePermission("connection.manage")
|
||||
|
|
@ -82,3 +97,10 @@ export class ConnectionsController {
|
|||
return { removed: true, token };
|
||||
}
|
||||
}
|
||||
|
||||
function parsePublishGovernance(value: unknown): PublishGovernance | null {
|
||||
if (value === null) return null;
|
||||
const mode = PUBLISH_GOVERNANCE_MODES.find((candidate) => candidate === value);
|
||||
if (mode === undefined) throw new BadRequestException(`publishGovernance must be null or one of: ${PUBLISH_GOVERNANCE_MODES.join(", ")}`);
|
||||
return mode;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue