refactor(cloud): add validation to prevent execute in cloud version

This commit is contained in:
Mauricio Siu
2024-10-04 01:12:14 -06:00
parent ab4677ac0e
commit 3747db08d4
5 changed files with 268 additions and 25 deletions

View File

@@ -7,24 +7,49 @@ import {
findAllDeploymentsByApplicationId, findAllDeploymentsByApplicationId,
findAllDeploymentsByComposeId, findAllDeploymentsByComposeId,
findAllDeploymentsByServerId, findAllDeploymentsByServerId,
findApplicationById,
findComposeById,
findServerById,
} from "@dokploy/builders"; } from "@dokploy/builders";
import { createTRPCRouter, protectedProcedure } from "../trpc"; import { createTRPCRouter, protectedProcedure } from "../trpc";
import { TRPCError } from "@trpc/server";
export const deploymentRouter = createTRPCRouter({ export const deploymentRouter = createTRPCRouter({
all: protectedProcedure all: protectedProcedure
.input(apiFindAllByApplication) .input(apiFindAllByApplication)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await findAllDeploymentsByApplicationId(input.applicationId); return await findAllDeploymentsByApplicationId(input.applicationId);
}), }),
allByCompose: protectedProcedure allByCompose: protectedProcedure
.input(apiFindAllByCompose) .input(apiFindAllByCompose)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
});
}
return await findAllDeploymentsByComposeId(input.composeId); return await findAllDeploymentsByComposeId(input.composeId);
}), }),
allByServer: protectedProcedure allByServer: protectedProcedure
.input(apiFindAllByServer) .input(apiFindAllByServer)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this server",
});
}
return await findAllDeploymentsByServerId(input.serverId); return await findAllDeploymentsByServerId(input.serverId);
}), }),
}); });

View File

@@ -19,13 +19,31 @@ import {
generateTraefikMeDomain, generateTraefikMeDomain,
removeDomainById, removeDomainById,
updateDomainById, updateDomainById,
findComposeById,
} from "@dokploy/builders"; } from "@dokploy/builders";
export const domainRouter = createTRPCRouter({ export const domainRouter = createTRPCRouter({
create: protectedProcedure create: protectedProcedure
.input(apiCreateDomain) .input(apiCreateDomain)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
try { try {
if (input.domainType === "compose" && input.composeId) {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
});
}
} else if (input.domainType === "application" && input.applicationId) {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
}
return await createDomain(input); return await createDomain(input);
} catch (error) { } catch (error) {
throw new TRPCError({ throw new TRPCError({
@@ -37,12 +55,26 @@ export const domainRouter = createTRPCRouter({
}), }),
byApplicationId: protectedProcedure byApplicationId: protectedProcedure
.input(apiFindOneApplication) .input(apiFindOneApplication)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await findDomainsByApplicationId(input.applicationId); return await findDomainsByApplicationId(input.applicationId);
}), }),
byComposeId: protectedProcedure byComposeId: protectedProcedure
.input(apiFindCompose) .input(apiFindCompose)
.query(async ({ input }) => { .query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
});
}
return await findDomainsByComposeId(input.composeId); return await findDomainsByComposeId(input.composeId);
}), }),
generateDomain: protectedProcedure generateDomain: protectedProcedure
@@ -57,7 +89,26 @@ export const domainRouter = createTRPCRouter({
update: protectedProcedure update: protectedProcedure
.input(apiUpdateDomain) .input(apiUpdateDomain)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const currentDomain = await findDomainById(input.domainId);
if (currentDomain.applicationId) {
const newApp = await findApplicationById(currentDomain.applicationId);
if (newApp.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
} else if (currentDomain.composeId) {
const newCompose = await findComposeById(currentDomain.composeId);
if (newCompose.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
});
}
}
const result = await updateDomainById(input.domainId, input); const result = await updateDomainById(input.domainId, input);
const domain = await findDomainById(input.domainId); const domain = await findDomainById(input.domainId);
if (domain.applicationId) { if (domain.applicationId) {
@@ -66,13 +117,48 @@ export const domainRouter = createTRPCRouter({
} }
return result; return result;
}), }),
one: protectedProcedure.input(apiFindDomain).query(async ({ input }) => { one: protectedProcedure.input(apiFindDomain).query(async ({ input, ctx }) => {
const domain = await findDomainById(input.domainId);
if (domain.applicationId) {
const application = await findApplicationById(domain.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
} else if (domain.composeId) {
const compose = await findComposeById(domain.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
});
}
}
return await findDomainById(input.domainId); return await findDomainById(input.domainId);
}), }),
delete: protectedProcedure delete: protectedProcedure
.input(apiFindDomain) .input(apiFindDomain)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const domain = await findDomainById(input.domainId); const domain = await findDomainById(input.domainId);
if (domain.applicationId) {
const application = await findApplicationById(domain.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
} else if (domain.composeId) {
const compose = await findComposeById(domain.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
});
}
}
const result = await removeDomainById(input.domainId); const result = await removeDomainById(input.domainId);
if (domain.applicationId) { if (domain.applicationId) {

View File

@@ -5,29 +5,64 @@ import {
} from "@/server/db/schema"; } from "@/server/db/schema";
import { import {
createRedirect, createRedirect,
findApplicationById,
findRedirectById, findRedirectById,
removeRedirectById, removeRedirectById,
updateRedirectById, updateRedirectById,
} from "@dokploy/builders"; } from "@dokploy/builders";
import { createTRPCRouter, protectedProcedure } from "../trpc"; import { createTRPCRouter, protectedProcedure } from "../trpc";
import { TRPCError } from "@trpc/server";
export const redirectsRouter = createTRPCRouter({ export const redirectsRouter = createTRPCRouter({
create: protectedProcedure create: protectedProcedure
.input(apiCreateRedirect) .input(apiCreateRedirect)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await createRedirect(input); return await createRedirect(input);
}), }),
one: protectedProcedure.input(apiFindOneRedirect).query(async ({ input }) => { one: protectedProcedure
return findRedirectById(input.redirectId); .input(apiFindOneRedirect)
}), .query(async ({ input, ctx }) => {
const redirect = await findRedirectById(input.redirectId);
const application = await findApplicationById(redirect.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return findRedirectById(input.redirectId);
}),
delete: protectedProcedure delete: protectedProcedure
.input(apiFindOneRedirect) .input(apiFindOneRedirect)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const redirect = await findRedirectById(input.redirectId);
const application = await findApplicationById(redirect.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return removeRedirectById(input.redirectId); return removeRedirectById(input.redirectId);
}), }),
update: protectedProcedure update: protectedProcedure
.input(apiUpdateRedirect) .input(apiUpdateRedirect)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const redirect = await findRedirectById(input.redirectId);
const application = await findApplicationById(redirect.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return updateRedirectById(input.redirectId, input); return updateRedirectById(input.redirectId, input);
}), }),
}); });

View File

@@ -6,28 +6,63 @@ import {
import { import {
createSecurity, createSecurity,
deleteSecurityById, deleteSecurityById,
findApplicationById,
findSecurityById, findSecurityById,
updateSecurityById, updateSecurityById,
} from "@dokploy/builders"; } from "@dokploy/builders";
import { createTRPCRouter, protectedProcedure } from "../trpc"; import { createTRPCRouter, protectedProcedure } from "../trpc";
import { TRPCError } from "@trpc/server";
export const securityRouter = createTRPCRouter({ export const securityRouter = createTRPCRouter({
create: protectedProcedure create: protectedProcedure
.input(apiCreateSecurity) .input(apiCreateSecurity)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await createSecurity(input); return await createSecurity(input);
}), }),
one: protectedProcedure.input(apiFindOneSecurity).query(async ({ input }) => { one: protectedProcedure
return await findSecurityById(input.securityId); .input(apiFindOneSecurity)
}), .query(async ({ input, ctx }) => {
const security = await findSecurityById(input.securityId);
const application = await findApplicationById(security.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await findSecurityById(input.securityId);
}),
delete: protectedProcedure delete: protectedProcedure
.input(apiFindOneSecurity) .input(apiFindOneSecurity)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const security = await findSecurityById(input.securityId);
const application = await findApplicationById(security.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await deleteSecurityById(input.securityId); return await deleteSecurityById(input.securityId);
}), }),
update: protectedProcedure update: protectedProcedure
.input(apiUpdateSecurity) .input(apiUpdateSecurity)
.mutation(async ({ input }) => { .mutation(async ({ input, ctx }) => {
const security = await findSecurityById(input.securityId);
const application = await findApplicationById(security.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
});
}
return await updateSecurityById(input.securityId, input); return await updateSecurityById(input.securityId, input);
}), }),
}); });

View File

@@ -60,6 +60,9 @@ import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
export const settingsRouter = createTRPCRouter({ export const settingsRouter = createTRPCRouter({
reloadServer: adminProcedure.mutation(async () => { reloadServer: adminProcedure.mutation(async () => {
if (IS_CLOUD) {
return true;
}
const { stdout } = await execAsync( const { stdout } = await execAsync(
"docker service inspect dokploy --format '{{.ID}}'", "docker service inspect dokploy --format '{{.ID}}'",
); );
@@ -73,7 +76,7 @@ export const settingsRouter = createTRPCRouter({
if (input?.serverId) { if (input?.serverId) {
await stopServiceRemote(input.serverId, "dokploy-traefik"); await stopServiceRemote(input.serverId, "dokploy-traefik");
await startServiceRemote(input.serverId, "dokploy-traefik"); await startServiceRemote(input.serverId, "dokploy-traefik");
} else { } else if (!IS_CLOUD) {
await stopService("dokploy-traefik"); await stopService("dokploy-traefik");
await startService("dokploy-traefik"); await startService("dokploy-traefik");
} }
@@ -135,6 +138,9 @@ export const settingsRouter = createTRPCRouter({
return true; return true;
}), }),
cleanMonitoring: adminProcedure.mutation(async () => { cleanMonitoring: adminProcedure.mutation(async () => {
if (IS_CLOUD) {
return true;
}
const { MONITORING_PATH } = paths(); const { MONITORING_PATH } = paths();
await recreateDirectory(MONITORING_PATH); await recreateDirectory(MONITORING_PATH);
return true; return true;
@@ -142,6 +148,9 @@ export const settingsRouter = createTRPCRouter({
saveSSHPrivateKey: adminProcedure saveSSHPrivateKey: adminProcedure
.input(apiSaveSSHKey) .input(apiSaveSSHKey)
.mutation(async ({ input, ctx }) => { .mutation(async ({ input, ctx }) => {
if (IS_CLOUD) {
return true;
}
await updateAdmin(ctx.user.authId, { await updateAdmin(ctx.user.authId, {
sshPrivateKey: input.sshPrivateKey, sshPrivateKey: input.sshPrivateKey,
}); });
@@ -151,6 +160,9 @@ export const settingsRouter = createTRPCRouter({
assignDomainServer: adminProcedure assignDomainServer: adminProcedure
.input(apiAssignDomain) .input(apiAssignDomain)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
if (IS_CLOUD) {
return true;
}
const admin = await updateAdmin(ctx.user.authId, { const admin = await updateAdmin(ctx.user.authId, {
host: input.host, host: input.host,
letsEncryptEmail: input.letsEncryptEmail, letsEncryptEmail: input.letsEncryptEmail,
@@ -169,6 +181,9 @@ export const settingsRouter = createTRPCRouter({
return admin; return admin;
}), }),
cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => {
if (IS_CLOUD) {
return true;
}
await updateAdmin(ctx.user.authId, { await updateAdmin(ctx.user.authId, {
sshPrivateKey: null, sshPrivateKey: null,
}); });
@@ -194,7 +209,7 @@ export const settingsRouter = createTRPCRouter({
await sendDockerCleanupNotifications(); await sendDockerCleanupNotifications();
}); });
} }
} else { } else if (!IS_CLOUD) {
await updateAdmin(ctx.user.authId, { await updateAdmin(ctx.user.authId, {
enableDockerCleanup: input.enableDockerCleanup, enableDockerCleanup: input.enableDockerCleanup,
}); });
@@ -220,6 +235,9 @@ export const settingsRouter = createTRPCRouter({
}), }),
readTraefikConfig: adminProcedure.query(() => { readTraefikConfig: adminProcedure.query(() => {
if (IS_CLOUD) {
return true;
}
const traefikConfig = readMainConfig(); const traefikConfig = readMainConfig();
return traefikConfig; return traefikConfig;
}), }),
@@ -227,22 +245,34 @@ export const settingsRouter = createTRPCRouter({
updateTraefikConfig: adminProcedure updateTraefikConfig: adminProcedure
.input(apiTraefikConfig) .input(apiTraefikConfig)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
if (IS_CLOUD) {
return true;
}
writeMainConfig(input.traefikConfig); writeMainConfig(input.traefikConfig);
return true; return true;
}), }),
readWebServerTraefikConfig: adminProcedure.query(() => { readWebServerTraefikConfig: adminProcedure.query(() => {
if (IS_CLOUD) {
return true;
}
const traefikConfig = readConfig("dokploy"); const traefikConfig = readConfig("dokploy");
return traefikConfig; return traefikConfig;
}), }),
updateWebServerTraefikConfig: adminProcedure updateWebServerTraefikConfig: adminProcedure
.input(apiTraefikConfig) .input(apiTraefikConfig)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
if (IS_CLOUD) {
return true;
}
writeConfig("dokploy", input.traefikConfig); writeConfig("dokploy", input.traefikConfig);
return true; return true;
}), }),
readMiddlewareTraefikConfig: adminProcedure.query(() => { readMiddlewareTraefikConfig: adminProcedure.query(() => {
if (IS_CLOUD) {
return true;
}
const traefikConfig = readConfig("middlewares"); const traefikConfig = readConfig("middlewares");
return traefikConfig; return traefikConfig;
}), }),
@@ -250,14 +280,23 @@ export const settingsRouter = createTRPCRouter({
updateMiddlewareTraefikConfig: adminProcedure updateMiddlewareTraefikConfig: adminProcedure
.input(apiTraefikConfig) .input(apiTraefikConfig)
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
if (IS_CLOUD) {
return true;
}
writeConfig("middlewares", input.traefikConfig); writeConfig("middlewares", input.traefikConfig);
return true; return true;
}), }),
checkAndUpdateImage: adminProcedure.mutation(async () => { checkAndUpdateImage: adminProcedure.mutation(async () => {
if (IS_CLOUD) {
return true;
}
return await pullLatestRelease(); return await pullLatestRelease();
}), }),
updateServer: adminProcedure.mutation(async () => { updateServer: adminProcedure.mutation(async () => {
if (IS_CLOUD) {
return true;
}
await spawnAsync("docker", [ await spawnAsync("docker", [
"service", "service",
"update", "update",
@@ -322,6 +361,9 @@ export const settingsRouter = createTRPCRouter({
return readConfigInPath(input.path, input.serverId); return readConfigInPath(input.path, input.serverId);
}), }),
getIp: protectedProcedure.query(async () => { getIp: protectedProcedure.query(async () => {
if (IS_CLOUD) {
return true;
}
const admin = await findAdmin(); const admin = await findAdmin();
return admin.serverIp; return admin.serverIp;
}), }),
@@ -387,8 +429,10 @@ export const settingsRouter = createTRPCRouter({
const result = await execAsyncRemote(input.serverId, command); const result = await execAsyncRemote(input.serverId, command);
return result.stdout.trim(); return result.stdout.trim();
} }
const result = await execAsync(command); if (!IS_CLOUD) {
return result.stdout.trim(); const result = await execAsync(command);
return result.stdout.trim();
}
}), }),
writeTraefikEnv: adminProcedure writeTraefikEnv: adminProcedure
@@ -439,6 +483,9 @@ export const settingsRouter = createTRPCRouter({
}) })
.input(apiReadStatsLogs) .input(apiReadStatsLogs)
.query(({ input }) => { .query(({ input }) => {
if (IS_CLOUD) {
return true;
}
const rawConfig = readMonitoringConfig(); const rawConfig = readMonitoringConfig();
const parsedConfig = parseRawConfig( const parsedConfig = parseRawConfig(
rawConfig as string, rawConfig as string,
@@ -451,11 +498,17 @@ export const settingsRouter = createTRPCRouter({
return parsedConfig; return parsedConfig;
}), }),
readStats: adminProcedure.query(() => { readStats: adminProcedure.query(() => {
if (IS_CLOUD) {
return true;
}
const rawConfig = readMonitoringConfig(); const rawConfig = readMonitoringConfig();
const processedLogs = processLogs(rawConfig as string); const processedLogs = processLogs(rawConfig as string);
return processedLogs || []; return processedLogs || [];
}), }),
getLogRotateStatus: adminProcedure.query(async () => { getLogRotateStatus: adminProcedure.query(async () => {
if (IS_CLOUD) {
return true;
}
return await logRotationManager.getStatus(); return await logRotationManager.getStatus();
}), }),
toggleLogRotate: adminProcedure toggleLogRotate: adminProcedure
@@ -465,6 +518,9 @@ export const settingsRouter = createTRPCRouter({
}), }),
) )
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
if (IS_CLOUD) {
return true;
}
if (input.enable) { if (input.enable) {
await logRotationManager.activate(); await logRotationManager.activate();
} else { } else {
@@ -474,6 +530,9 @@ export const settingsRouter = createTRPCRouter({
return true; return true;
}), }),
haveActivateRequests: adminProcedure.query(async () => { haveActivateRequests: adminProcedure.query(async () => {
if (IS_CLOUD) {
return true;
}
const config = readMainConfig(); const config = readMainConfig();
if (!config) return false; if (!config) return false;
@@ -492,6 +551,9 @@ export const settingsRouter = createTRPCRouter({
}), }),
) )
.mutation(async ({ input }) => { .mutation(async ({ input }) => {
if (IS_CLOUD) {
return true;
}
const mainConfig = readMainConfig(); const mainConfig = readMainConfig();
if (!mainConfig) return false; if (!mainConfig) return false;