Allow CEO agents to update company branding (name, description, logo, color)
- Add updateCompanyBrandingSchema restricting agent-updatable fields to name, description, brandColor, and logoAssetId - Update PATCH /api/companies/:companyId to allow CEO agents with branding-only fields while keeping admin fields (status, budget, etc.) board-only - Allow agents to GET /api/companies/:companyId for reading company info - issuePrefix (company slug) remains protected — not in any update schema - Document branding APIs in SKILL.md quick reference and api-reference.md Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
@@ -6,11 +6,13 @@ import {
|
||||
companyPortabilityPreviewSchema,
|
||||
createCompanySchema,
|
||||
updateCompanySchema,
|
||||
updateCompanyBrandingSchema,
|
||||
} from "@paperclipai/shared";
|
||||
import { forbidden } from "../errors.js";
|
||||
import { validate } from "../middleware/validate.js";
|
||||
import {
|
||||
accessService,
|
||||
agentService,
|
||||
budgetService,
|
||||
companyPortabilityService,
|
||||
companyService,
|
||||
@@ -58,9 +60,12 @@ export function companyRoutes(db: Db) {
|
||||
});
|
||||
|
||||
router.get("/:companyId", async (req, res) => {
|
||||
assertBoard(req);
|
||||
const companyId = req.params.companyId as string;
|
||||
assertCompanyAccess(req, companyId);
|
||||
// Allow agents (CEO) to read their own company; board always allowed
|
||||
if (req.actor.type !== "agent") {
|
||||
assertBoard(req);
|
||||
}
|
||||
const company = await svc.getById(companyId);
|
||||
if (!company) {
|
||||
res.status(404).json({ error: "Company not found" });
|
||||
@@ -144,23 +149,44 @@ export function companyRoutes(db: Db) {
|
||||
res.status(201).json(company);
|
||||
});
|
||||
|
||||
router.patch("/:companyId", validate(updateCompanySchema), async (req, res) => {
|
||||
assertBoard(req);
|
||||
router.patch("/:companyId", async (req, res) => {
|
||||
const companyId = req.params.companyId as string;
|
||||
assertCompanyAccess(req, companyId);
|
||||
const company = await svc.update(companyId, req.body);
|
||||
|
||||
const actor = getActorInfo(req);
|
||||
let body: Record<string, unknown>;
|
||||
|
||||
if (req.actor.type === "agent") {
|
||||
// Only CEO agents may update company branding fields
|
||||
const agentSvc = agentService(db);
|
||||
const actorAgent = req.actor.agentId ? await agentSvc.getById(req.actor.agentId) : null;
|
||||
if (!actorAgent || actorAgent.role !== "ceo") {
|
||||
throw forbidden("Only CEO agents or board users may update company settings");
|
||||
}
|
||||
if (actorAgent.companyId !== companyId) {
|
||||
throw forbidden("Agent key cannot access another company");
|
||||
}
|
||||
body = updateCompanyBrandingSchema.parse(req.body);
|
||||
} else {
|
||||
assertBoard(req);
|
||||
body = updateCompanySchema.parse(req.body);
|
||||
}
|
||||
|
||||
const company = await svc.update(companyId, body);
|
||||
if (!company) {
|
||||
res.status(404).json({ error: "Company not found" });
|
||||
return;
|
||||
}
|
||||
await logActivity(db, {
|
||||
companyId,
|
||||
actorType: "user",
|
||||
actorId: req.actor.userId ?? "board",
|
||||
actorType: actor.actorType,
|
||||
actorId: actor.actorId,
|
||||
agentId: actor.agentId,
|
||||
runId: actor.runId,
|
||||
action: "company.updated",
|
||||
entityType: "company",
|
||||
entityId: companyId,
|
||||
details: req.body,
|
||||
details: body,
|
||||
});
|
||||
res.json(company);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user