Merge public-gh/master into paperclip-company-import-export

This commit is contained in:
dotta 2026-03-20 06:25:24 -05:00
commit d07d86f778
41 changed files with 11912 additions and 392 deletions

View file

@ -7,6 +7,7 @@ import {
createCompanySchema,
updateCompanyBrandingSchema,
updateCompanySchema,
updateCompanyBrandingSchema,
} from "@paperclipai/shared";
import { forbidden } from "../errors.js";
import { validate } from "../middleware/validate.js";
@ -90,9 +91,12 @@ export function companyRoutes(db: Db, storage?: StorageService) {
});
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" });
@ -238,23 +242,44 @@ export function companyRoutes(db: Db, storage?: StorageService) {
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);
});