mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-30 03:25:22 +02:00
- Introduced new test files for permission checks, including `check-permission.test.ts`, `enterprise-only-resources.test.ts`, `resolve-permissions.test.ts`, and `service-access.test.ts`. - Implemented permission checks in various components to ensure actions are gated by user permissions, including `ShowTraefikConfig`, `UpdateTraefikConfig`, `ShowVolumes`, `ShowDomains`, and others. - Enhanced the logic for displaying UI elements based on user permissions, ensuring that only authorized users can access or modify resources.
211 lines
5.7 KiB
TypeScript
211 lines
5.7 KiB
TypeScript
import { IS_CLOUD, removeScheduleJob, scheduleJob } from "@dokploy/server";
|
|
import { db } from "@dokploy/server/db";
|
|
import { deployments } from "@dokploy/server/db/schema/deployment";
|
|
import {
|
|
createScheduleSchema,
|
|
schedules,
|
|
updateScheduleSchema,
|
|
} from "@dokploy/server/db/schema/schedule";
|
|
import { runCommand } from "@dokploy/server/index";
|
|
import {
|
|
createSchedule,
|
|
deleteSchedule,
|
|
findScheduleById,
|
|
updateSchedule,
|
|
} from "@dokploy/server/services/schedule";
|
|
import { TRPCError } from "@trpc/server";
|
|
import { desc, eq } from "drizzle-orm";
|
|
import { z } from "zod";
|
|
import { audit } from "@/server/api/utils/audit";
|
|
import { removeJob, schedule } from "@/server/utils/backup";
|
|
import { checkServicePermissionAndAccess } from "@dokploy/server/services/permission";
|
|
import { createTRPCRouter, protectedProcedure } from "../trpc";
|
|
export const scheduleRouter = createTRPCRouter({
|
|
create: protectedProcedure
|
|
.input(createScheduleSchema)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const serviceId = input.applicationId || input.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
schedule: ["create"],
|
|
});
|
|
}
|
|
const newSchedule = await createSchedule(input);
|
|
|
|
if (newSchedule?.enabled) {
|
|
if (IS_CLOUD) {
|
|
schedule({
|
|
scheduleId: newSchedule.scheduleId,
|
|
type: "schedule",
|
|
cronSchedule: newSchedule.cronExpression,
|
|
timezone: newSchedule.timezone,
|
|
});
|
|
} else {
|
|
scheduleJob(newSchedule);
|
|
}
|
|
}
|
|
await audit(ctx, {
|
|
action: "create",
|
|
resourceType: "schedule",
|
|
resourceId: newSchedule?.scheduleId,
|
|
resourceName: newSchedule?.name,
|
|
});
|
|
return newSchedule;
|
|
}),
|
|
|
|
update: protectedProcedure
|
|
.input(updateScheduleSchema)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const existingSchedule = await findScheduleById(input.scheduleId);
|
|
const serviceId =
|
|
existingSchedule.applicationId || existingSchedule.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
schedule: ["update"],
|
|
});
|
|
}
|
|
const updatedSchedule = await updateSchedule(input);
|
|
|
|
if (IS_CLOUD) {
|
|
if (updatedSchedule?.enabled) {
|
|
schedule({
|
|
scheduleId: updatedSchedule.scheduleId,
|
|
type: "schedule",
|
|
cronSchedule: updatedSchedule.cronExpression,
|
|
timezone: updatedSchedule.timezone,
|
|
});
|
|
} else {
|
|
await removeJob({
|
|
cronSchedule: updatedSchedule.cronExpression,
|
|
scheduleId: updatedSchedule.scheduleId,
|
|
type: "schedule",
|
|
});
|
|
}
|
|
} else {
|
|
if (updatedSchedule?.enabled) {
|
|
removeScheduleJob(updatedSchedule.scheduleId);
|
|
scheduleJob(updatedSchedule);
|
|
} else {
|
|
removeScheduleJob(updatedSchedule.scheduleId);
|
|
}
|
|
}
|
|
await audit(ctx, {
|
|
action: "update",
|
|
resourceType: "schedule",
|
|
resourceId: updatedSchedule.scheduleId,
|
|
resourceName: updatedSchedule.name,
|
|
});
|
|
return updatedSchedule;
|
|
}),
|
|
|
|
delete: protectedProcedure
|
|
.input(z.object({ scheduleId: z.string() }))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const scheduleItem = await findScheduleById(input.scheduleId);
|
|
const serviceId = scheduleItem.applicationId || scheduleItem.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
schedule: ["delete"],
|
|
});
|
|
}
|
|
await deleteSchedule(input.scheduleId);
|
|
|
|
if (IS_CLOUD) {
|
|
await removeJob({
|
|
cronSchedule: scheduleItem.cronExpression,
|
|
scheduleId: scheduleItem.scheduleId,
|
|
type: "schedule",
|
|
});
|
|
} else {
|
|
removeScheduleJob(scheduleItem.scheduleId);
|
|
}
|
|
await audit(ctx, {
|
|
action: "delete",
|
|
resourceType: "schedule",
|
|
resourceId: scheduleItem.scheduleId,
|
|
resourceName: scheduleItem.name,
|
|
});
|
|
return true;
|
|
}),
|
|
|
|
list: protectedProcedure
|
|
.input(
|
|
z.object({
|
|
id: z.string(),
|
|
scheduleType: z.enum([
|
|
"application",
|
|
"compose",
|
|
"server",
|
|
"dokploy-server",
|
|
]),
|
|
}),
|
|
)
|
|
.query(async ({ input, ctx }) => {
|
|
if (
|
|
input.scheduleType === "application" ||
|
|
input.scheduleType === "compose"
|
|
) {
|
|
await checkServicePermissionAndAccess(ctx, input.id, {
|
|
schedule: ["read"],
|
|
});
|
|
}
|
|
const where = {
|
|
application: eq(schedules.applicationId, input.id),
|
|
compose: eq(schedules.composeId, input.id),
|
|
server: eq(schedules.serverId, input.id),
|
|
"dokploy-server": eq(schedules.userId, input.id),
|
|
};
|
|
return db.query.schedules.findMany({
|
|
where: where[input.scheduleType],
|
|
with: {
|
|
application: true,
|
|
server: true,
|
|
compose: true,
|
|
deployments: {
|
|
orderBy: [desc(deployments.createdAt)],
|
|
},
|
|
},
|
|
});
|
|
}),
|
|
|
|
one: protectedProcedure
|
|
.input(z.object({ scheduleId: z.string() }))
|
|
.query(async ({ input, ctx }) => {
|
|
const schedule = await findScheduleById(input.scheduleId);
|
|
const serviceId = schedule.applicationId || schedule.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
schedule: ["read"],
|
|
});
|
|
}
|
|
return schedule;
|
|
}),
|
|
|
|
runManually: protectedProcedure
|
|
.input(z.object({ scheduleId: z.string().min(1) }))
|
|
.mutation(async ({ input, ctx }) => {
|
|
const scheduleItem = await findScheduleById(input.scheduleId);
|
|
const serviceId = scheduleItem.applicationId || scheduleItem.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
schedule: ["create"],
|
|
});
|
|
}
|
|
try {
|
|
await runCommand(input.scheduleId);
|
|
await audit(ctx, {
|
|
action: "run",
|
|
resourceType: "schedule",
|
|
resourceId: input.scheduleId,
|
|
});
|
|
return true;
|
|
} catch (error) {
|
|
throw new TRPCError({
|
|
code: "INTERNAL_SERVER_ERROR",
|
|
message:
|
|
error instanceof Error ? error.message : "Error running schedule",
|
|
});
|
|
}
|
|
}),
|
|
});
|