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); }), });