mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-06-15 20:25:23 +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.
206 lines
5.9 KiB
TypeScript
206 lines
5.9 KiB
TypeScript
import {
|
|
createDomain,
|
|
findApplicationById,
|
|
findDomainById,
|
|
findDomainsByApplicationId,
|
|
findDomainsByComposeId,
|
|
findPreviewDeploymentById,
|
|
findServerById,
|
|
generateTraefikMeDomain,
|
|
getWebServerSettings,
|
|
manageDomain,
|
|
removeDomain,
|
|
removeDomainById,
|
|
updateDomainById,
|
|
validateDomain,
|
|
} from "@dokploy/server";
|
|
import { checkServicePermissionAndAccess } from "@dokploy/server/services/permission";
|
|
import { TRPCError } from "@trpc/server";
|
|
import { z } from "zod";
|
|
import {
|
|
createTRPCRouter,
|
|
protectedProcedure,
|
|
withPermission,
|
|
} from "@/server/api/trpc";
|
|
import { audit } from "@/server/api/utils/audit";
|
|
import {
|
|
apiCreateDomain,
|
|
apiFindCompose,
|
|
apiFindDomain,
|
|
apiFindOneApplication,
|
|
apiUpdateDomain,
|
|
} from "@/server/db/schema";
|
|
|
|
export const domainRouter = createTRPCRouter({
|
|
create: protectedProcedure
|
|
.input(apiCreateDomain)
|
|
.mutation(async ({ input, ctx }) => {
|
|
try {
|
|
if (input.domainType === "compose" && input.composeId) {
|
|
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
|
domain: ["create"],
|
|
});
|
|
} else if (input.domainType === "application" && input.applicationId) {
|
|
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
|
domain: ["create"],
|
|
});
|
|
}
|
|
const domain = await createDomain(input);
|
|
await audit(ctx, {
|
|
action: "create",
|
|
resourceType: "domain",
|
|
resourceId: domain.domainId,
|
|
resourceName: domain.host,
|
|
});
|
|
return domain;
|
|
} catch (error) {
|
|
throw new TRPCError({
|
|
code: "BAD_REQUEST",
|
|
message:
|
|
error instanceof Error
|
|
? error.message
|
|
: "Error creating the domain",
|
|
cause: error,
|
|
});
|
|
}
|
|
}),
|
|
byApplicationId: protectedProcedure
|
|
.input(apiFindOneApplication)
|
|
.query(async ({ input, ctx }) => {
|
|
await checkServicePermissionAndAccess(ctx, input.applicationId, {
|
|
domain: ["read"],
|
|
});
|
|
return await findDomainsByApplicationId(input.applicationId);
|
|
}),
|
|
byComposeId: protectedProcedure
|
|
.input(apiFindCompose)
|
|
.query(async ({ input, ctx }) => {
|
|
await checkServicePermissionAndAccess(ctx, input.composeId, {
|
|
domain: ["read"],
|
|
});
|
|
return await findDomainsByComposeId(input.composeId);
|
|
}),
|
|
generateDomain: withPermission("domain", "create")
|
|
.input(z.object({ appName: z.string(), serverId: z.string().optional() }))
|
|
.mutation(async ({ input, ctx }) => {
|
|
return generateTraefikMeDomain(
|
|
input.appName,
|
|
ctx.user.ownerId,
|
|
input.serverId,
|
|
);
|
|
}),
|
|
canGenerateTraefikMeDomains: withPermission("domain", "read")
|
|
.input(z.object({ serverId: z.string() }))
|
|
.query(async ({ input }) => {
|
|
if (input.serverId) {
|
|
const server = await findServerById(input.serverId);
|
|
return server.ipAddress;
|
|
}
|
|
const settings = await getWebServerSettings();
|
|
return settings?.serverIp || "";
|
|
}),
|
|
|
|
update: protectedProcedure
|
|
.input(apiUpdateDomain)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const currentDomain = await findDomainById(input.domainId);
|
|
const serviceId = currentDomain.applicationId || currentDomain.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
domain: ["create"],
|
|
});
|
|
} else if (currentDomain.previewDeploymentId) {
|
|
const preview = await findPreviewDeploymentById(
|
|
currentDomain.previewDeploymentId,
|
|
);
|
|
await checkServicePermissionAndAccess(ctx, preview.applicationId, {
|
|
domain: ["create"],
|
|
});
|
|
}
|
|
|
|
const result = await updateDomainById(input.domainId, input);
|
|
const domain = await findDomainById(input.domainId);
|
|
await audit(ctx, {
|
|
action: "update",
|
|
resourceType: "domain",
|
|
resourceId: domain.domainId,
|
|
resourceName: domain.host,
|
|
});
|
|
if (domain.applicationId) {
|
|
const application = await findApplicationById(domain.applicationId);
|
|
await manageDomain(application, domain);
|
|
} else if (domain.previewDeploymentId) {
|
|
const previewDeployment = await findPreviewDeploymentById(
|
|
domain.previewDeploymentId,
|
|
);
|
|
const application = await findApplicationById(
|
|
previewDeployment.applicationId,
|
|
);
|
|
application.appName = previewDeployment.appName;
|
|
await manageDomain(application, domain);
|
|
}
|
|
return result;
|
|
}),
|
|
one: protectedProcedure.input(apiFindDomain).query(async ({ input, ctx }) => {
|
|
const domain = await findDomainById(input.domainId);
|
|
const serviceId = domain.applicationId || domain.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
domain: ["read"],
|
|
});
|
|
} else if (domain.previewDeploymentId) {
|
|
const preview = await findPreviewDeploymentById(
|
|
domain.previewDeploymentId,
|
|
);
|
|
await checkServicePermissionAndAccess(ctx, preview.applicationId, {
|
|
domain: ["read"],
|
|
});
|
|
}
|
|
return domain;
|
|
}),
|
|
delete: protectedProcedure
|
|
.input(apiFindDomain)
|
|
.mutation(async ({ input, ctx }) => {
|
|
const domain = await findDomainById(input.domainId);
|
|
const serviceId = domain.applicationId || domain.composeId;
|
|
if (serviceId) {
|
|
await checkServicePermissionAndAccess(ctx, serviceId, {
|
|
domain: ["delete"],
|
|
});
|
|
} else if (domain.previewDeploymentId) {
|
|
const preview = await findPreviewDeploymentById(
|
|
domain.previewDeploymentId,
|
|
);
|
|
await checkServicePermissionAndAccess(ctx, preview.applicationId, {
|
|
domain: ["delete"],
|
|
});
|
|
}
|
|
|
|
const result = await removeDomainById(input.domainId);
|
|
await audit(ctx, {
|
|
action: "delete",
|
|
resourceType: "domain",
|
|
resourceId: domain.domainId,
|
|
resourceName: domain.host,
|
|
});
|
|
|
|
if (domain.applicationId) {
|
|
const application = await findApplicationById(domain.applicationId);
|
|
await removeDomain(application, domain.uniqueConfigKey);
|
|
}
|
|
|
|
return result;
|
|
}),
|
|
|
|
validateDomain: withPermission("domain", "read")
|
|
.input(
|
|
z.object({
|
|
domain: z.string(),
|
|
serverIp: z.string().optional(),
|
|
}),
|
|
)
|
|
.mutation(async ({ input }) => {
|
|
return validateDomain(input.domain, input.serverIp);
|
|
}),
|
|
});
|