diff --git a/packages/api/src/router/environment.ts b/packages/api/src/router/environment.ts index d2395d4c..4cd741cb 100644 --- a/packages/api/src/router/environment.ts +++ b/packages/api/src/router/environment.ts @@ -38,6 +38,7 @@ import { isPassingAllPolicies, isPassingReleaseSequencingCancelPolicy, } from "@ctrlplane/job-dispatch"; +import { Permission } from "@ctrlplane/validators/auth"; import { createTRPCRouter, protectedProcedure } from "../trpc"; @@ -300,6 +301,12 @@ export const createEnv = async ( const tragetRouter = createTRPCRouter({ byEnvironmentId: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "environment", id: input }, + [Permission.TargetList], + ], + }) .input(z.string()) .query(async ({ ctx, input }) => ctx.db @@ -375,25 +382,39 @@ export const environmentRouter = createTRPCRouter({ }; }), - byId: protectedProcedure.input(z.string()).query(({ ctx, input }) => - ctx.db - .select() - .from(environment) - .leftJoin( - environmentPolicy, - eq(environment.policyId, environmentPolicy.id), - ) - .where(and(eq(environment.id, input), isNull(environment.deletedAt))) - .then(takeFirstOrNull) - .then((env) => - env == null - ? null - : { ...env.environment, policy: env.environment_policy }, - ), - ), + byId: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "environment", id: input }, + [Permission.SystemGet], + ], + }) + .input(z.string().uuid()) + .query(({ ctx, input }) => + ctx.db + .select() + .from(environment) + .leftJoin( + environmentPolicy, + eq(environment.policyId, environmentPolicy.id), + ) + .where(and(eq(environment.id, input), isNull(environment.deletedAt))) + .then(takeFirstOrNull) + .then((env) => + env == null + ? null + : { ...env.environment, policy: env.environment_policy }, + ), + ), bySystemId: protectedProcedure - .input(z.string()) + .meta({ + operation: ({ input }) => [ + { type: "system", id: input }, + [Permission.SystemGet], + ], + }) + .input(z.string().uuid()) .query(async ({ ctx, input }) => { const envs = await ctx.db .select() @@ -420,12 +441,24 @@ export const environmentRouter = createTRPCRouter({ }), create: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "system", id: input.systemId }, + [Permission.SystemCreate], + ], + }) .input(createEnvironment) .mutation(({ ctx, input }) => ctx.db.transaction((db) => createEnv(db, input)), ), update: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "environment", id: input.id }, + [Permission.SystemUpdate], + ], + }) .input(z.object({ id: z.string().uuid(), data: updateEnvironment })) .mutation(({ ctx, input }) => ctx.db @@ -437,6 +470,12 @@ export const environmentRouter = createTRPCRouter({ ), delete: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "environment", id: input }, + [Permission.SystemDelete], + ], + }) .input(z.string().uuid()) .mutation(({ ctx, input }) => ctx.db.transaction((db) => diff --git a/packages/api/src/router/job.ts b/packages/api/src/router/job.ts index 38379f2b..a75a5169 100644 --- a/packages/api/src/router/job.ts +++ b/packages/api/src/router/job.ts @@ -34,6 +34,7 @@ import { getRolloutDateForJobConfig, isDateInTimeWindow, } from "@ctrlplane/job-dispatch"; +import { Permission } from "@ctrlplane/validators/auth"; import { createTRPCRouter, protectedProcedure, publicProcedure } from "../trpc"; @@ -56,26 +57,45 @@ const jobConfigQuery = (tx: Tx) => ); const jobConfigRouter = createTRPCRouter({ - byWorkspaceId: protectedProcedure.input(z.string()).query(({ ctx, input }) => - jobConfigQuery(ctx.db) - .leftJoin(system, eq(system.id, deployment.systemId)) - .where(and(eq(system.workspaceId, input), isNull(environment.deletedAt))) - .orderBy(asc(jobConfig.createdAt)) - .limit(1_000) - .then((data) => - data.map((t) => ({ - ...t.job_config, - execution: t.job_execution, - agent: t.job_agent, - target: t.target, - release: { ...t.release, deployment: t.deployment }, - runbook: t.runbook, - environment: t.environment, - })), - ), - ), + byWorkspaceId: protectedProcedure + .input(z.string()) + .meta({ + operation: ({ input }) => [ + { type: "workspace", id: input }, + [Permission.SystemList], + ], + }) + .query(({ ctx, input }) => + jobConfigQuery(ctx.db) + .leftJoin(system, eq(system.id, deployment.systemId)) + .where( + and(eq(system.workspaceId, input), isNull(environment.deletedAt)), + ) + .orderBy(asc(jobConfig.createdAt)) + .limit(1_000) + .then((data) => + data.map((t) => ({ + ...t.job_config, + execution: t.job_execution, + agent: t.job_agent, + target: t.target, + release: { ...t.release, deployment: t.deployment }, + runbook: t.runbook, + environment: t.environment, + })), + ), + ), byDeploymentAndEnvironment: protectedProcedure + .meta({ + operation: ({ input }) => [ + [ + { type: "deployment", id: input.deploymentId }, + { type: "environment", id: input.environmentId }, + ], + [Permission.DeploymentGet], + ], + }) .input( z.object({ deploymentId: z.string().uuid(), @@ -104,64 +124,80 @@ const jobConfigRouter = createTRPCRouter({ ), ), - byDeploymentId: protectedProcedure.input(z.string()).query(({ ctx, input }) => - jobConfigQuery(ctx.db) - .where(and(eq(deployment.id, input), isNull(environment.deletedAt))) - .then((data) => - data.map((t) => ({ - ...t.job_config, - jobExecution: t.job_execution, - jobAgent: t.job_agent, - target: t.target, - release: { ...t.release, deployment: t.deployment }, - runbook: t.runbook, - environment: t.environment, - })), - ), - ), + byDeploymentId: protectedProcedure + .input(z.string().uuid()) + .meta({ + operation: ({ input }) => [ + { type: "deployment", id: input }, + [Permission.DeploymentGet], + ], + }) + .query(({ ctx, input }) => + jobConfigQuery(ctx.db) + .where(and(eq(deployment.id, input), isNull(environment.deletedAt))) + .then((data) => + data.map((t) => ({ + ...t.job_config, + jobExecution: t.job_execution, + jobAgent: t.job_agent, + target: t.target, + release: { ...t.release, deployment: t.deployment }, + runbook: t.runbook, + environment: t.environment, + })), + ), + ), - byReleaseId: protectedProcedure.input(z.string()).query(({ ctx, input }) => - jobConfigQuery(ctx.db) - .leftJoin( - environmentPolicy, - eq(environment.policyId, environmentPolicy.id), - ) - .leftJoin( - environmentPolicyReleaseWindow, - eq(environmentPolicyReleaseWindow.policyId, environmentPolicy.id), - ) - .where(and(eq(release.id, input), isNull(environment.deletedAt))) - .then((data) => - _.chain(data) - .groupBy("job_config.id") - .map((v) => ({ - ...v[0]!.job_config, - jobExecution: v[0]!.job_execution, - jobAgent: v[0]!.job_agent, - target: v[0]!.target, - release: { ...v[0]!.release, deployment: v[0]!.deployment }, - runbook: v[0]!.runbook, - environment: v[0]!.environment, - rolloutDate: - v[0]!.job_config.targetId == null || - v[0]!.environment_policy == null || - v[0]!.release == null || - v[0]!.environment == null - ? null - : rolloutDateFromJobConfig( - v[0]!.job_config.targetId, - v[0]!.release.id, - v[0]!.environment.id, - v[0]!.release.createdAt, - v[0]!.environment_policy.duration, - v - .map((r) => r.environment_policy_release_window) - .filter(isPresent), - ), - })) - .value(), - ), - ), + byReleaseId: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "release", id: input }, + [Permission.DeploymentGet], + ], + }) + .input(z.string().uuid()) + .query(({ ctx, input }) => + jobConfigQuery(ctx.db) + .leftJoin( + environmentPolicy, + eq(environment.policyId, environmentPolicy.id), + ) + .leftJoin( + environmentPolicyReleaseWindow, + eq(environmentPolicyReleaseWindow.policyId, environmentPolicy.id), + ) + .where(and(eq(release.id, input), isNull(environment.deletedAt))) + .then((data) => + _.chain(data) + .groupBy("job_config.id") + .map((v) => ({ + ...v[0]!.job_config, + jobExecution: v[0]!.job_execution, + jobAgent: v[0]!.job_agent, + target: v[0]!.target, + release: { ...v[0]!.release, deployment: v[0]!.deployment }, + runbook: v[0]!.runbook, + environment: v[0]!.environment, + rolloutDate: + v[0]!.job_config.targetId == null || + v[0]!.environment_policy == null || + v[0]!.release == null || + v[0]!.environment == null + ? null + : rolloutDateFromJobConfig( + v[0]!.job_config.targetId, + v[0]!.release.id, + v[0]!.environment.id, + v[0]!.release.createdAt, + v[0]!.environment_policy.duration, + v + .map((r) => r.environment_policy_release_window) + .filter(isPresent), + ), + })) + .value(), + ), + ), }); const rolloutDateFromJobConfig = ( @@ -281,6 +317,12 @@ const jobExecutionRouter = createTRPCRouter({ create: createTRPCRouter({ byEnvId: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "environment", id: input }, + [Permission.DeploymentUpdate], + ], + }) .input(z.string().uuid()) .mutation(({ ctx, input }) => ctx.db diff --git a/packages/api/src/router/system.ts b/packages/api/src/router/system.ts index 43d3408a..765027ba 100644 --- a/packages/api/src/router/system.ts +++ b/packages/api/src/router/system.ts @@ -1,7 +1,12 @@ import { z } from "zod"; import { and, count, eq, like, or, takeFirst } from "@ctrlplane/db"; -import { createSystem, system, updateSystem } from "@ctrlplane/db/schema"; +import { + createSystem, + system, + updateSystem, + workspace, +} from "@ctrlplane/db/schema"; import { Permission } from "@ctrlplane/validators/auth"; import { createTRPCRouter, protectedProcedure } from "../trpc"; @@ -9,6 +14,12 @@ import { createEnv } from "./environment"; export const systemRouter = createTRPCRouter({ list: protectedProcedure + .meta({ + operation: ({ input }) => [ + { type: "workspace", id: input.workspaceId }, + [Permission.SystemList], + ], + }) .input( z.object({ workspaceId: z.string().uuid(), @@ -57,15 +68,35 @@ export const systemRouter = createTRPCRouter({ }), bySlug: protectedProcedure - .input(z.string()) + .meta({ + operation: async ({ input, ctx }) => { + const sys = await ctx.db + .select() + .from(system) + .where( + and( + eq(system.slug, input.systemSlug), + eq(workspace.slug, input.workspaceSlug), + ), + ) + .then(takeFirst); + return [{ type: "system", id: sys.id }, [Permission.SystemGet]]; + }, + }) + .input(z.object({ workspaceSlug: z.string(), systemSlug: z.string() })) .query(({ ctx: { db }, input }) => - db.query.system.findFirst({ where: eq(system.slug, input) }), + db + .select() + .from(system) + .where( + and( + eq(system.slug, input.systemSlug), + eq(workspace.slug, input.workspaceSlug), + ), + ) + .then(takeFirst), ), - byId: protectedProcedure.input(z.string()).query(({ ctx: { db }, input }) => { - db.query.system.findMany({ where: eq(system.id, input) }); - }), - create: protectedProcedure .meta({ operation: ({ input }) => [