Manifest-first: connectWorkspace declared mutates:true with the workspace-connected audit event, requiredCores gains audit. Workspace entity gains gitUrl + persisted status enum (created/connecting/ready/ error). Input takes name + git URL + PAT; the output schema is the credential-free workspace entity, so the PAT can never round-trip. Audit emission asserted with RecordingAuditLog; binders wire the use case through wireUseCase with the __audited brand, and web-next bindAll now binds core-audit (payload+stdout sinks in production, stdout in dev-seed) so boot conformance passes in both modes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
63 lines
2.7 KiB
TypeScript
63 lines
2.7 KiB
TypeScript
import { z } from "zod";
|
|
import type { AuditLogProtocol } from "@repo/core-shared/di/bind-protocols";
|
|
|
|
import { gitUrlSchema, workspaceSchema } from "../../entities/models/workspace";
|
|
import type { IWorkspaceRepository } from "../repositories/workspace.repository.interface";
|
|
|
|
// ── Input ────────────────────────────────────────────────────────────────
|
|
export const connectWorkspaceInputSchema = z
|
|
.object({
|
|
name: z.string().min(1).max(128),
|
|
gitUrl: gitUrlSchema,
|
|
/** Repository personal access token. Write-only: encrypted at rest and never returned by any output. */
|
|
pat: z.string().min(1).max(4096),
|
|
})
|
|
.strict();
|
|
export type ConnectWorkspaceInput = z.infer<typeof connectWorkspaceInputSchema>;
|
|
|
|
// ── Output ───────────────────────────────────────────────────────────────
|
|
// The credential is deliberately absent: `workspaceSchema` has no credential
|
|
// field, and `.parse` strips any unknown keys a repository might leak.
|
|
export const connectWorkspaceOutputSchema = workspaceSchema;
|
|
export type ConnectWorkspaceOutput = z.infer<
|
|
typeof connectWorkspaceOutputSchema
|
|
>;
|
|
|
|
// ── Use case ─────────────────────────────────────────────────────────────
|
|
export type IConnectWorkspaceUseCase = ReturnType<
|
|
typeof connectWorkspaceUseCase
|
|
>;
|
|
|
|
export const connectWorkspaceUseCase =
|
|
(workspaceRepository: IWorkspaceRepository, auditLog?: AuditLogProtocol) =>
|
|
async (input: ConnectWorkspaceInput): Promise<ConnectWorkspaceOutput> => {
|
|
const workspace = await workspaceRepository.createWorkspace({
|
|
name: input.name,
|
|
gitUrl: input.gitUrl,
|
|
credential: input.pat,
|
|
});
|
|
|
|
// Audit event "workspace-connected" — declared in feature.manifest.ts
|
|
// (useCases.connectWorkspace.audits). No session context exists at this
|
|
// layer yet (auth wiring is a later story), so the system sentinels apply.
|
|
await auditLog?.record({
|
|
actorId: "system",
|
|
actorType: "system",
|
|
actorRoles: [],
|
|
action: "CREATE",
|
|
resource: { type: "workspaces", id: workspace.id },
|
|
at: new Date(),
|
|
scope: {
|
|
feature: "workspaces",
|
|
environment: process.env.NODE_ENV ?? "development",
|
|
tenant: "default",
|
|
},
|
|
from: { ipTruncated: "system", userAgent: "control-plane" },
|
|
containsPii: false,
|
|
outcome: "success",
|
|
reason: "workspace-connected",
|
|
});
|
|
|
|
return connectWorkspaceOutputSchema.parse(workspace);
|
|
};
|