Reapply "Merge branch 'canary' into kucherenko/canary"

This reverts commit e6cb6454db.
This commit is contained in:
Mauricio Siu
2025-03-02 00:30:02 -06:00
parent e6cb6454db
commit 747c2137c9
639 changed files with 82888 additions and 17188 deletions

View File

@@ -20,6 +20,7 @@ import { mongoRouter } from "./routers/mongo";
import { mountRouter } from "./routers/mount";
import { mysqlRouter } from "./routers/mysql";
import { notificationRouter } from "./routers/notification";
import { organizationRouter } from "./routers/organization";
import { portRouter } from "./routers/port";
import { postgresRouter } from "./routers/postgres";
import { previewDeploymentRouter } from "./routers/preview-deployment";
@@ -34,7 +35,6 @@ import { sshRouter } from "./routers/ssh-key";
import { stripeRouter } from "./routers/stripe";
import { swarmRouter } from "./routers/swarm";
import { userRouter } from "./routers/user";
/**
* This is the primary router for your server.
*
@@ -77,6 +77,7 @@ export const appRouter = createTRPCRouter({
stripe: stripeRouter,
swarm: swarmRouter,
ai: aiRouter,
organization: organizationRouter,
});
// export type definition of API

View File

@@ -1,103 +1,59 @@
import { db } from "@/server/db";
import { apiUpdateWebServerMonitoring } from "@/server/db/schema";
import {
apiAssignPermissions,
apiCreateUserInvitation,
apiFindOneToken,
apiRemoveUser,
apiUpdateAdmin,
users,
} from "@/server/db/schema";
import {
createInvitation,
findAdminById,
findUserByAuthId,
IS_CLOUD,
findUserById,
getUserByToken,
removeUserByAuthId,
updateAdmin,
setupWebMonitoring,
updateUser,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { adminProcedure, createTRPCRouter, publicProcedure } from "../trpc";
import { adminProcedure, createTRPCRouter } from "../trpc";
export const adminRouter = createTRPCRouter({
one: adminProcedure.query(async ({ ctx }) => {
const { sshPrivateKey, ...rest } = await findAdminById(ctx.user.adminId);
return {
haveSSH: !!sshPrivateKey,
...rest,
};
}),
update: adminProcedure
.input(apiUpdateAdmin)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this admin",
});
}
const { authId } = await findAdminById(ctx.user.adminId);
return updateAdmin(authId, input);
}),
createUserInvitation: adminProcedure
.input(apiCreateUserInvitation)
setupMonitoring: adminProcedure
.input(apiUpdateWebServerMonitoring)
.mutation(async ({ input, ctx }) => {
try {
await createInvitation(input, ctx.user.adminId);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message:
"Error creating this user\ncheck if the email is not registered",
cause: error,
});
}
}),
removeUser: adminProcedure
.input(apiRemoveUser)
.mutation(async ({ input, ctx }) => {
try {
const user = await findUserByAuthId(input.authId);
if (user.adminId !== ctx.user.adminId) {
if (IS_CLOUD) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this user",
message: "Feature disabled on cloud",
});
}
return await removeUserByAuthId(input.authId);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error deleting this user",
cause: error,
});
}
}),
getUserByToken: publicProcedure
.input(apiFindOneToken)
.query(async ({ input }) => {
return await getUserByToken(input.token);
}),
assignPermissions: adminProcedure
.input(apiAssignPermissions)
.mutation(async ({ input, ctx }) => {
try {
const user = await findUserById(input.userId);
if (user.adminId !== ctx.user.adminId) {
const user = await findUserById(ctx.user.ownerId);
if (user.id !== ctx.user.ownerId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to assign permissions",
message: "You are not authorized to setup the monitoring",
});
}
await db
.update(users)
.set({
...input,
})
.where(eq(users.userId, input.userId));
await updateUser(user.id, {
metricsConfig: {
server: {
type: "Dokploy",
refreshRate: input.metricsConfig.server.refreshRate,
port: input.metricsConfig.server.port,
token: input.metricsConfig.server.token,
cronJob: input.metricsConfig.server.cronJob,
urlCallback: input.metricsConfig.server.urlCallback,
retentionDays: input.metricsConfig.server.retentionDays,
thresholds: {
cpu: input.metricsConfig.server.thresholds.cpu,
memory: input.metricsConfig.server.thresholds.memory,
},
},
containers: {
refreshRate: input.metricsConfig.containers.refreshRate,
services: {
include: input.metricsConfig.containers.services.include || [],
exclude: input.metricsConfig.containers.services.exclude || [],
},
},
},
});
const currentServer = await setupWebMonitoring(user.id);
return currentServer;
} catch (error) {
throw error;
}

View File

@@ -60,8 +60,13 @@ export const applicationRouter = createTRPCRouter({
.input(apiCreateApplication)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -72,7 +77,7 @@ export const applicationRouter = createTRPCRouter({
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
@@ -80,8 +85,12 @@ export const applicationRouter = createTRPCRouter({
}
const newApplication = await createApplication(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newApplication.applicationId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newApplication.applicationId,
project.organizationId,
);
}
return newApplication;
} catch (error: unknown) {
@@ -98,15 +107,18 @@ export const applicationRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOneApplication)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.authId,
ctx.user.id,
input.applicationId,
ctx.session.activeOrganizationId,
"access",
);
}
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -119,7 +131,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiReloadApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to reload this application",
@@ -144,16 +158,19 @@ export const applicationRouter = createTRPCRouter({
delete: protectedProcedure
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.authId,
ctx.user.id,
input.applicationId,
ctx.session.activeOrganizationId,
"delete",
);
}
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this application",
@@ -184,7 +201,7 @@ export const applicationRouter = createTRPCRouter({
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_) {}
}
return result[0];
@@ -194,7 +211,7 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const service = await findApplicationById(input.applicationId);
if (service.project.adminId !== ctx.user.adminId) {
if (service.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this application",
@@ -214,7 +231,7 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const service = await findApplicationById(input.applicationId);
if (service.project.adminId !== ctx.user.adminId) {
if (service.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to start this application",
@@ -235,7 +252,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to redeploy this application",
@@ -268,7 +287,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveEnvironmentVariables)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this environment",
@@ -284,7 +305,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveBuildType)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this build type",
@@ -305,7 +328,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveGithubProvider)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this github provider",
@@ -327,7 +352,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveGitlabProvider)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this gitlab provider",
@@ -351,7 +378,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveBitbucketProvider)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this bitbucket provider",
@@ -373,7 +402,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveDockerProvider)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this docker provider",
@@ -394,7 +425,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiSaveGitProvider)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this git provider",
@@ -415,7 +448,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to mark this application as running",
@@ -427,7 +462,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiUpdateApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this application",
@@ -451,7 +488,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to refresh this application",
@@ -466,7 +505,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this application",
@@ -500,7 +541,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to clean this application",
@@ -513,7 +556,9 @@ export const applicationRouter = createTRPCRouter({
.input(apiFindOneApplication)
.query(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to read this application",
@@ -548,7 +593,7 @@ export const applicationRouter = createTRPCRouter({
const app = await findApplicationById(input.applicationId as string);
if (app.project.adminId !== ctx.user.adminId) {
if (app.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this application",
@@ -590,7 +635,9 @@ export const applicationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this application",
@@ -610,7 +657,7 @@ export const applicationRouter = createTRPCRouter({
}),
readAppMonitoring: protectedProcedure
.input(apiFindMonitoringStats)
.query(async ({ input, ctx }) => {
.query(async ({ input }) => {
if (IS_CLOUD) {
throw new TRPCError({
code: "UNAUTHORIZED",

View File

@@ -1,523 +1,326 @@
import {
apiCreateAdmin,
apiCreateUser,
apiFindOneAuth,
apiLogin,
apiUpdateAuth,
apiVerify2FA,
apiVerifyLogin2FA,
auth,
} from "@/server/db/schema";
import { WEBSITE_URL } from "@/server/utils/stripe";
import {
type Auth,
IS_CLOUD,
createAdmin,
createUser,
findAuthByEmail,
findAuthById,
generate2FASecret,
getUserByToken,
lucia,
luciaToken,
removeAdminByAuthId,
removeUserByAuthId,
sendDiscordNotification,
sendEmailNotification,
updateAuthById,
validateRequest,
verify2FA,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
import { isBefore } from "date-fns";
import { eq } from "drizzle-orm";
import { nanoid } from "nanoid";
import { z } from "zod";
import { db } from "../../db";
import {
adminProcedure,
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "../trpc";
import { createTRPCRouter } from "../trpc";
export const authRouter = createTRPCRouter({
createAdmin: publicProcedure
.input(apiCreateAdmin)
.mutation(async ({ ctx, input }) => {
try {
if (!IS_CLOUD) {
const admin = await db.query.admins.findFirst({});
if (admin) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Admin already exists",
});
}
}
const newAdmin = await createAdmin(input);
if (IS_CLOUD) {
await sendDiscordNotificationWelcome(newAdmin);
await sendVerificationEmail(newAdmin.id);
return {
status: "success",
type: "cloud",
};
}
const session = await lucia.createSession(newAdmin.id || "", {});
ctx.res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return {
status: "success",
type: "selfhosted",
};
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
// @ts-ignore
message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`,
cause: error,
});
}
}),
createUser: publicProcedure
.input(apiCreateUser)
.mutation(async ({ ctx, input }) => {
try {
const token = await getUserByToken(input.token);
if (token.isExpired) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Invalid token",
});
}
const newUser = await createUser(input);
if (IS_CLOUD) {
await sendVerificationEmail(token.authId);
return true;
}
const session = await lucia.createSession(newUser?.authId || "", {});
ctx.res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return true;
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the user",
cause: error,
});
}
}),
login: publicProcedure.input(apiLogin).mutation(async ({ ctx, input }) => {
try {
const auth = await findAuthByEmail(input.email);
const correctPassword = bcrypt.compareSync(
input.password,
auth?.password || "",
);
if (!correctPassword) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Credentials do not match",
});
}
if (auth?.confirmationToken && IS_CLOUD) {
await sendVerificationEmail(auth.id);
throw new TRPCError({
code: "BAD_REQUEST",
message:
"Email not confirmed, we have sent you a confirmation email please check your inbox.",
});
}
if (auth?.is2FAEnabled) {
return {
is2FAEnabled: true,
authId: auth.id,
};
}
const session = await lucia.createSession(auth?.id || "", {});
ctx.res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return {
is2FAEnabled: false,
authId: auth?.id,
};
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: `Error: ${error instanceof Error ? error.message : "Login error"}`,
cause: error,
});
}
}),
get: protectedProcedure.query(async ({ ctx }) => {
const auth = await findAuthById(ctx.user.authId);
return auth;
}),
logout: protectedProcedure.mutation(async ({ ctx }) => {
const { req, res } = ctx;
const { session } = await validateRequest(req, res);
if (!session) return false;
await lucia.invalidateSession(session.id);
res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
return true;
}),
update: protectedProcedure
.input(apiUpdateAuth)
.mutation(async ({ ctx, input }) => {
const currentAuth = await findAuthByEmail(ctx.user.email);
if (input.currentPassword || input.password) {
const correctPassword = bcrypt.compareSync(
input.currentPassword || "",
currentAuth?.password || "",
);
if (!correctPassword) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Current password is incorrect",
});
}
}
const auth = await updateAuthById(ctx.user.authId, {
...(input.email && { email: input.email.toLowerCase() }),
...(input.password && {
password: bcrypt.hashSync(input.password, 10),
}),
...(input.image && { image: input.image }),
});
return auth;
}),
removeSelfAccount: protectedProcedure
.input(
z.object({
password: z.string().min(1),
}),
)
.mutation(async ({ ctx, input }) => {
if (!IS_CLOUD) {
throw new TRPCError({
code: "NOT_FOUND",
message: "This feature is only available in the cloud version",
});
}
const currentAuth = await findAuthByEmail(ctx.user.email);
const correctPassword = bcrypt.compareSync(
input.password,
currentAuth?.password || "",
);
if (!correctPassword) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Password is incorrect",
});
}
const { req, res } = ctx;
const { session } = await validateRequest(req, res);
if (!session) return false;
await lucia.invalidateSession(session.id);
res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
if (ctx.user.rol === "admin") {
await removeAdminByAuthId(ctx.user.authId);
} else {
await removeUserByAuthId(ctx.user.authId);
}
return true;
}),
generateToken: protectedProcedure.mutation(async ({ ctx, input }) => {
const auth = await findAuthById(ctx.user.authId);
if (auth.token) {
await luciaToken.invalidateSession(auth.token);
}
const session = await luciaToken.createSession(auth?.id || "", {
expiresIn: 60 * 60 * 24 * 30,
});
await updateAuthById(auth.id, {
token: session.id,
});
return auth;
}),
verifyToken: protectedProcedure.mutation(async () => {
return true;
}),
one: adminProcedure.input(apiFindOneAuth).query(async ({ input }) => {
const auth = await findAuthById(input.id);
return auth;
}),
generate2FASecret: protectedProcedure.query(async ({ ctx }) => {
return await generate2FASecret(ctx.user.authId);
}),
verify2FASetup: protectedProcedure
.input(apiVerify2FA)
.mutation(async ({ ctx, input }) => {
const auth = await findAuthById(ctx.user.authId);
await verify2FA(auth, input.secret, input.pin);
await updateAuthById(auth.id, {
is2FAEnabled: true,
secret: input.secret,
});
return auth;
}),
verifyLogin2FA: publicProcedure
.input(apiVerifyLogin2FA)
.mutation(async ({ ctx, input }) => {
const auth = await findAuthById(input.id);
await verify2FA(auth, auth.secret || "", input.pin);
const session = await lucia.createSession(auth.id, {});
ctx.res.appendHeader(
"Set-Cookie",
lucia.createSessionCookie(session.id).serialize(),
);
return true;
}),
disable2FA: protectedProcedure.mutation(async ({ ctx }) => {
const auth = await findAuthById(ctx.user.authId);
await updateAuthById(auth.id, {
is2FAEnabled: false,
secret: null,
});
return auth;
}),
sendResetPasswordEmail: publicProcedure
.input(
z.object({
email: z.string().min(1).email(),
}),
)
.mutation(async ({ ctx, input }) => {
if (!IS_CLOUD) {
throw new TRPCError({
code: "NOT_FOUND",
message: "This feature is only available in the cloud version",
});
}
const authR = await db.query.auth.findFirst({
where: eq(auth.email, input.email),
});
if (!authR) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
const token = nanoid();
await updateAuthById(authR.id, {
resetPasswordToken: token,
// Make resetPassword in 24 hours
resetPasswordExpiresAt: new Date(
new Date().getTime() + 24 * 60 * 60 * 1000,
).toISOString(),
});
await sendEmailNotification(
{
fromAddress: process.env.SMTP_FROM_ADDRESS!,
toAddresses: [authR.email],
smtpServer: process.env.SMTP_SERVER!,
smtpPort: Number(process.env.SMTP_PORT),
username: process.env.SMTP_USERNAME!,
password: process.env.SMTP_PASSWORD!,
},
"Reset Password",
`
Reset your password by clicking the link below:
The link will expire in 24 hours.
<a href="${WEBSITE_URL}/reset-password?token=${token}">
Reset Password
</a>
`,
);
}),
resetPassword: publicProcedure
.input(
z.object({
resetPasswordToken: z.string().min(1),
password: z.string().min(1),
}),
)
.mutation(async ({ ctx, input }) => {
if (!IS_CLOUD) {
throw new TRPCError({
code: "NOT_FOUND",
message: "This feature is only available in the cloud version",
});
}
const authR = await db.query.auth.findFirst({
where: eq(auth.resetPasswordToken, input.resetPasswordToken),
});
if (!authR || authR.resetPasswordExpiresAt === null) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Token not found",
});
}
const isExpired = isBefore(
new Date(authR.resetPasswordExpiresAt),
new Date(),
);
if (isExpired) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Token expired",
});
}
await updateAuthById(authR.id, {
resetPasswordExpiresAt: null,
resetPasswordToken: null,
password: bcrypt.hashSync(input.password, 10),
});
return true;
}),
confirmEmail: adminProcedure
.input(
z.object({
confirmationToken: z.string().min(1),
}),
)
.mutation(async ({ ctx, input }) => {
if (!IS_CLOUD) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Functionality not available in cloud version",
});
}
const authR = await db.query.auth.findFirst({
where: eq(auth.confirmationToken, input.confirmationToken),
});
if (!authR || authR.confirmationExpiresAt === null) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Token not found",
});
}
if (authR.confirmationToken !== input.confirmationToken) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Confirmation Token not found",
});
}
const isExpired = isBefore(
new Date(authR.confirmationExpiresAt),
new Date(),
);
if (isExpired) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Confirmation Token expired",
});
}
1;
await updateAuthById(authR.id, {
confirmationToken: null,
confirmationExpiresAt: null,
});
return true;
}),
// createAdmin: publicProcedure.mutation(async ({ input }) => {
// try {
// if (!IS_CLOUD) {
// const admin = await db.query.admins.findFirst({});
// if (admin) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Admin already exists",
// });
// }
// }
// const newAdmin = await createAdmin(input);
// if (IS_CLOUD) {
// await sendDiscordNotificationWelcome(newAdmin);
// await sendVerificationEmail(newAdmin.id);
// return {
// status: "success",
// type: "cloud",
// };
// }
// // const session = await lucia.createSession(newAdmin.id || "", {});
// // ctx.res.appendHeader(
// // "Set-Cookie",
// // lucia.createSessionCookie(session.id).serialize(),
// // );
// return {
// status: "success",
// type: "selfhosted",
// };
// } catch (error) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// // @ts-ignore
// message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`,
// cause: error,
// });
// }
// }),
// createUser: publicProcedure.mutation(async ({ input }) => {
// try {
// const _token = await getUserByToken(input.token);
// // if (token.isExpired) {
// // throw new TRPCError({
// // code: "BAD_REQUEST",
// // message: "Invalid token",
// // });
// // }
// // const newUser = await createUser(input);
// // if (IS_CLOUD) {
// // await sendVerificationEmail(token.authId);
// // return true;
// // }
// // const session = await lucia.createSession(newUser?.authId || "", {});
// // ctx.res.appendHeader(
// // "Set-Cookie",
// // lucia.createSessionCookie(session.id).serialize(),
// // );
// return true;
// } catch (error) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Error creating the user",
// cause: error,
// });
// }
// }),
// login: publicProcedure.mutation(async ({ input }) => {
// try {
// const auth = await findAuthByEmail(input.email);
// const correctPassword = bcrypt.compareSync(
// input.password,
// auth?.password || "",
// );
// if (!correctPassword) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Credentials do not match",
// });
// }
// if (auth?.confirmationToken && IS_CLOUD) {
// await sendVerificationEmail(auth.id);
// throw new TRPCError({
// code: "BAD_REQUEST",
// message:
// "Email not confirmed, we have sent you a confirmation email please check your inbox.",
// });
// }
// if (auth?.is2FAEnabled) {
// return {
// is2FAEnabled: true,
// authId: auth.id,
// };
// }
// // const session = await lucia.createSession(auth?.id || "", {});
// // ctx.res.appendHeader(
// // "Set-Cookie",
// // lucia.createSessionCookie(session.id).serialize(),
// // );
// return {
// is2FAEnabled: false,
// authId: auth?.id,
// };
// } catch (error) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: `Error: ${error instanceof Error ? error.message : "Login error"}`,
// cause: error,
// });
// }
// }),
// get: protectedProcedure.query(async ({ ctx }) => {
// const memberResult = await db.query.member.findFirst({
// where: and(
// eq(member.userId, ctx.user.id),
// eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
// ),
// with: {
// user: true,
// },
// });
// return memberResult;
// }),
// logout: protectedProcedure.mutation(async ({ ctx }) => {
// const { req } = ctx;
// const { session } = await validateRequest(req);
// if (!session) return false;
// // await lucia.invalidateSession(session.id);
// // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
// return true;
// }),
// update: protectedProcedure.mutation(async ({ ctx, input }) => {
// const currentAuth = await findAuthByEmail(ctx.user.email);
// if (input.currentPassword || input.password) {
// const correctPassword = bcrypt.compareSync(
// input.currentPassword || "",
// currentAuth?.password || "",
// );
// if (!correctPassword) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Current password is incorrect",
// });
// }
// }
// // const auth = await updateAuthById(ctx.user.authId, {
// // ...(input.email && { email: input.email.toLowerCase() }),
// // ...(input.password && {
// // password: bcrypt.hashSync(input.password, 10),
// // }),
// // ...(input.image && { image: input.image }),
// // });
// return auth;
// }),
// removeSelfAccount: protectedProcedure
// .input(
// z.object({
// password: z.string().min(1),
// }),
// )
// .mutation(async ({ ctx, input }) => {
// if (!IS_CLOUD) {
// throw new TRPCError({
// code: "NOT_FOUND",
// message: "This feature is only available in the cloud version",
// });
// }
// const currentAuth = await findAuthByEmail(ctx.user.email);
// const correctPassword = bcrypt.compareSync(
// input.password,
// currentAuth?.password || "",
// );
// if (!correctPassword) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "Password is incorrect",
// });
// }
// const { req } = ctx;
// const { session } = await validateRequest(req);
// if (!session) return false;
// // await lucia.invalidateSession(session.id);
// // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize());
// // if (ctx.user.rol === "owner") {
// // await removeAdminByAuthId(ctx.user.authId);
// // } else {
// // await removeUserByAuthId(ctx.user.authId);
// // }
// return true;
// }),
// generateToken: protectedProcedure.mutation(async ({ ctx }) => {
// const auth = await findUserById(ctx.user.id);
// console.log(auth);
// // if (auth.token) {
// // await luciaToken.invalidateSession(auth.token);
// // }
// // const session = await luciaToken.createSession(auth?.id || "", {
// // expiresIn: 60 * 60 * 24 * 30,
// // });
// // await updateUser(auth.id, {
// // token: session.id,
// // });
// return auth;
// }),
// verifyToken: protectedProcedure.mutation(async () => {
// return true;
// }),
// one: adminProcedure
// .input(z.object({ userId: z.string().min(1) }))
// .query(async ({ input }) => {
// // TODO: Check if the user is admin or member
// const user = await findUserById(input.userId);
// return user;
// }),
// sendResetPasswordEmail: publicProcedure
// .input(
// z.object({
// email: z.string().min(1).email(),
// }),
// )
// .mutation(async ({ input }) => {
// if (!IS_CLOUD) {
// throw new TRPCError({
// code: "NOT_FOUND",
// message: "This feature is only available in the cloud version",
// });
// }
// const authR = await db.query.auth.findFirst({
// where: eq(auth.email, input.email),
// });
// if (!authR) {
// throw new TRPCError({
// code: "NOT_FOUND",
// message: "User not found",
// });
// }
// const token = nanoid();
// await updateAuthById(authR.id, {
// resetPasswordToken: token,
// // Make resetPassword in 24 hours
// resetPasswordExpiresAt: new Date(
// new Date().getTime() + 24 * 60 * 60 * 1000,
// ).toISOString(),
// });
// await sendEmailNotification(
// {
// fromAddress: process.env.SMTP_FROM_ADDRESS!,
// toAddresses: [authR.email],
// smtpServer: process.env.SMTP_SERVER!,
// smtpPort: Number(process.env.SMTP_PORT),
// username: process.env.SMTP_USERNAME!,
// password: process.env.SMTP_PASSWORD!,
// },
// "Reset Password",
// `
// Reset your password by clicking the link below:
// The link will expire in 24 hours.
// <a href="${WEBSITE_URL}/reset-password?token=${token}">
// Reset Password
// </a>
// `,
// );
// }),
});
export const sendVerificationEmail = async (authId: string) => {
const token = nanoid();
const result = await updateAuthById(authId, {
confirmationToken: token,
confirmationExpiresAt: new Date(
new Date().getTime() + 24 * 60 * 60 * 1000,
).toISOString(),
});
// export const sendVerificationEmail = async (authId: string) => {
// const token = nanoid();
// const result = await updateAuthById(authId, {
// confirmationToken: token,
// confirmationExpiresAt: new Date(
// new Date().getTime() + 24 * 60 * 60 * 1000,
// ).toISOString(),
// });
if (!result) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "User not found",
});
}
await sendEmailNotification(
{
fromAddress: process.env.SMTP_FROM_ADDRESS || "",
toAddresses: [result?.email],
smtpServer: process.env.SMTP_SERVER || "",
smtpPort: Number(process.env.SMTP_PORT),
username: process.env.SMTP_USERNAME || "",
password: process.env.SMTP_PASSWORD || "",
},
"Confirm your email | Dokploy",
`
Welcome to Dokploy!
Please confirm your email by clicking the link below:
<a href="${WEBSITE_URL}/confirm-email?token=${result?.confirmationToken}">
Confirm Email
</a>
`,
);
// if (!result) {
// throw new TRPCError({
// code: "BAD_REQUEST",
// message: "User not found",
// });
// }
// await sendEmailNotification(
// {
// fromAddress: process.env.SMTP_FROM_ADDRESS || "",
// toAddresses: [result?.email],
// smtpServer: process.env.SMTP_SERVER || "",
// smtpPort: Number(process.env.SMTP_PORT),
// username: process.env.SMTP_USERNAME || "",
// password: process.env.SMTP_PASSWORD || "",
// },
// "Confirm your email | Dokploy",
// `
// Welcome to Dokploy!
// Please confirm your email by clicking the link below:
// <a href="${WEBSITE_URL}/confirm-email?token=${result?.confirmationToken}">
// Confirm Email
// </a>
// `,
// );
return true;
};
// return true;
// };
export const sendDiscordNotificationWelcome = async (newAdmin: Auth) => {
await sendDiscordNotification(
{
webhookUrl: process.env.DISCORD_WEBHOOK_URL || "",
},
{
title: "New User Registered",
color: 0x00ff00,
fields: [
{
name: "Email",
value: newAdmin.email,
inline: true,
},
],
timestamp: newAdmin.createdAt,
footer: {
text: "Dokploy User Registration Notification",
},
},
);
};
// export const sendDiscordNotificationWelcome = async (newAdmin: Auth) => {
// await sendDiscordNotification(
// {
// webhookUrl: process.env.DISCORD_WEBHOOK_URL || "",
// },
// {
// title: "New User Registered",
// color: 0x00ff00,
// fields: [
// {
// name: "Email",
// value: newAdmin.email,
// inline: true,
// },
// ],
// timestamp: newAdmin.createdAt,
// footer: {
// text: "Dokploy User Registration Notification",
// },
// },
// );
// };

View File

@@ -30,7 +30,7 @@ import { TRPCError } from "@trpc/server";
export const backupRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreateBackup)
.mutation(async ({ input, ctx }) => {
.mutation(async ({ input }) => {
try {
const newBackup = await createBackup(input);
@@ -74,16 +74,14 @@ export const backupRouter = createTRPCRouter({
});
}
}),
one: protectedProcedure
.input(apiFindOneBackup)
.query(async ({ input, ctx }) => {
const backup = await findBackupById(input.backupId);
one: protectedProcedure.input(apiFindOneBackup).query(async ({ input }) => {
const backup = await findBackupById(input.backupId);
return backup;
}),
return backup;
}),
update: protectedProcedure
.input(apiUpdateBackup)
.mutation(async ({ input, ctx }) => {
.mutation(async ({ input }) => {
try {
await updateBackupById(input.backupId, input);
const backup = await findBackupById(input.backupId);
@@ -111,15 +109,17 @@ export const backupRouter = createTRPCRouter({
}
}
} catch (error) {
const message =
error instanceof Error ? error.message : "Error updating this Backup";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error updating this Backup",
message,
});
}
}),
remove: protectedProcedure
.input(apiRemoveBackup)
.mutation(async ({ input, ctx }) => {
.mutation(async ({ input }) => {
try {
const value = await removeBackupById(input.backupId);
if (IS_CLOUD && value) {
@@ -133,10 +133,11 @@ export const backupRouter = createTRPCRouter({
}
return value;
} catch (error) {
const message =
error instanceof Error ? error.message : "Error deleting this Backup";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error deleting this Backup",
cause: error,
message,
});
}
}),
@@ -149,11 +150,13 @@ export const backupRouter = createTRPCRouter({
await runPostgresBackup(postgres, backup);
return true;
} catch (error) {
console.log(error);
const message =
error instanceof Error
? error.message
: "Error running manual Postgres backup ";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error running manual Postgres backup ",
cause: error,
message,
});
}
}),

View File

@@ -8,7 +8,6 @@ import {
apiUpdateBitbucket,
} from "@/server/db/schema";
import {
IS_CLOUD,
createBitbucket,
findBitbucketById,
getBitbucketBranches,
@@ -23,7 +22,7 @@ export const bitbucketRouter = createTRPCRouter({
.input(apiCreateBitbucket)
.mutation(async ({ input, ctx }) => {
try {
return await createBitbucket(input, ctx.user.adminId);
return await createBitbucket(input, ctx.session.activeOrganizationId);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -37,10 +36,9 @@ export const bitbucketRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if (
IS_CLOUD &&
bitbucketProvider.gitProvider.adminId !== ctx.user.adminId
bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider",
@@ -58,12 +56,11 @@ export const bitbucketRouter = createTRPCRouter({
},
});
if (IS_CLOUD) {
// TODO: mAyBe a rEfaCtoR 🤫
result = result.filter(
(provider) => provider.gitProvider.adminId === ctx.user.adminId,
);
}
result = result.filter(
(provider) =>
provider.gitProvider.organizationId ===
ctx.session.activeOrganizationId,
);
return result;
}),
@@ -72,10 +69,9 @@ export const bitbucketRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if (
IS_CLOUD &&
bitbucketProvider.gitProvider.adminId !== ctx.user.adminId
bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider",
@@ -90,10 +86,9 @@ export const bitbucketRouter = createTRPCRouter({
input.bitbucketId || "",
);
if (
IS_CLOUD &&
bitbucketProvider.gitProvider.adminId !== ctx.user.adminId
bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider",
@@ -107,10 +102,9 @@ export const bitbucketRouter = createTRPCRouter({
try {
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if (
IS_CLOUD &&
bitbucketProvider.gitProvider.adminId !== ctx.user.adminId
bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider",
@@ -131,10 +125,9 @@ export const bitbucketRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const bitbucketProvider = await findBitbucketById(input.bitbucketId);
if (
IS_CLOUD &&
bitbucketProvider.gitProvider.adminId !== ctx.user.adminId
bitbucketProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this bitbucket provider",
@@ -142,7 +135,7 @@ export const bitbucketRouter = createTRPCRouter({
}
return await updateBitbucket(input.bitbucketId, {
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
}),
});

View File

@@ -25,14 +25,14 @@ export const certificateRouter = createTRPCRouter({
message: "Please set a server to create a certificate",
});
}
return await createCertificate(input, ctx.user.adminId);
return await createCertificate(input, ctx.session.activeOrganizationId);
}),
one: adminProcedure
.input(apiFindCertificate)
.query(async ({ input, ctx }) => {
const certificates = await findCertificateById(input.certificateId);
if (IS_CLOUD && certificates.adminId !== ctx.user.adminId) {
if (certificates.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this certificate",
@@ -44,7 +44,7 @@ export const certificateRouter = createTRPCRouter({
.input(apiFindCertificate)
.mutation(async ({ input, ctx }) => {
const certificates = await findCertificateById(input.certificateId);
if (IS_CLOUD && certificates.adminId !== ctx.user.adminId) {
if (certificates.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this certificate",
@@ -55,8 +55,7 @@ export const certificateRouter = createTRPCRouter({
}),
all: adminProcedure.query(async ({ ctx }) => {
return await db.query.certificates.findMany({
// TODO: Remove this line when the cloud version is ready
...(IS_CLOUD && { where: eq(certificates.adminId, ctx.user.adminId) }),
where: eq(certificates.organizationId, ctx.session.activeOrganizationId),
});
}),
});

View File

@@ -40,7 +40,7 @@ export const clusterRouter = createTRPCRouter({
});
}
}),
addWorker: protectedProcedure.query(async ({ input }) => {
addWorker: protectedProcedure.query(async () => {
if (IS_CLOUD) {
return {
command: "",
@@ -57,7 +57,7 @@ export const clusterRouter = createTRPCRouter({
version: docker_version.Version,
};
}),
addManager: protectedProcedure.query(async ({ input }) => {
addManager: protectedProcedure.query(async () => {
if (IS_CLOUD) {
return {
command: "",

View File

@@ -39,13 +39,14 @@ import {
createComposeByTemplate,
createDomain,
createMount,
findAdminById,
findComposeById,
findDomainsByComposeId,
findProjectById,
findServerById,
findUserById,
loadServices,
randomizeComposeFile,
randomizeIsolatedDeploymentComposeFile,
removeCompose,
removeComposeDirectory,
removeDeploymentsByComposeId,
@@ -59,8 +60,13 @@ export const composeRouter = createTRPCRouter({
.input(apiCreateCompose)
.mutation(async ({ ctx, input }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -70,7 +76,7 @@ export const composeRouter = createTRPCRouter({
});
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
@@ -78,8 +84,12 @@ export const composeRouter = createTRPCRouter({
}
const newService = await createCompose(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newService.composeId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newService.composeId,
project.organizationId,
);
}
return newService;
@@ -91,12 +101,17 @@ export const composeRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindCompose)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.composeId, "access");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.composeId,
ctx.session.activeOrganizationId,
"access",
);
}
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
@@ -109,7 +124,7 @@ export const composeRouter = createTRPCRouter({
.input(apiUpdateCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this compose",
@@ -120,12 +135,20 @@ export const composeRouter = createTRPCRouter({
delete: protectedProcedure
.input(apiDeleteCompose)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.composeId, "delete");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.composeId,
ctx.session.activeOrganizationId,
"delete",
);
}
const composeResult = await findComposeById(input.composeId);
if (composeResult.project.adminId !== ctx.user.adminId) {
if (
composeResult.project.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this compose",
@@ -147,7 +170,7 @@ export const composeRouter = createTRPCRouter({
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_) {}
}
return result[0];
@@ -156,7 +179,7 @@ export const composeRouter = createTRPCRouter({
.input(apiFindCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to clean this compose",
@@ -169,7 +192,7 @@ export const composeRouter = createTRPCRouter({
.input(apiFetchServices)
.query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to load this compose",
@@ -183,7 +206,9 @@ export const composeRouter = createTRPCRouter({
try {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (
compose.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to fetch this compose",
@@ -208,7 +233,7 @@ export const composeRouter = createTRPCRouter({
.input(apiRandomizeCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to randomize this compose",
@@ -216,11 +241,26 @@ export const composeRouter = createTRPCRouter({
}
return await randomizeComposeFile(input.composeId, input.suffix);
}),
isolatedDeployment: protectedProcedure
.input(apiRandomizeCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to randomize this compose",
});
}
return await randomizeIsolatedDeploymentComposeFile(
input.composeId,
input.suffix,
);
}),
getConvertedCompose: protectedProcedure
.input(apiFindCompose)
.query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to get this compose",
@@ -238,7 +278,7 @@ export const composeRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this compose",
@@ -271,7 +311,7 @@ export const composeRouter = createTRPCRouter({
.input(apiFindCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to redeploy this compose",
@@ -303,7 +343,7 @@ export const composeRouter = createTRPCRouter({
.input(apiFindCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this compose",
@@ -317,7 +357,7 @@ export const composeRouter = createTRPCRouter({
.input(apiFindCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this compose",
@@ -332,7 +372,7 @@ export const composeRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to get this compose",
@@ -345,7 +385,7 @@ export const composeRouter = createTRPCRouter({
.input(apiFindCompose)
.mutation(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to refresh this compose",
@@ -359,8 +399,13 @@ export const composeRouter = createTRPCRouter({
deployTemplate: protectedProcedure
.input(apiCreateComposeByTemplate)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -374,7 +419,7 @@ export const composeRouter = createTRPCRouter({
const generate = await loadTemplateModule(input.id as TemplatesKeys);
const admin = await findAdminById(ctx.user.adminId);
const admin = await findUserById(ctx.user.ownerId);
let serverIp = admin.serverIp || "127.0.0.1";
const project = await findProjectById(input.projectId);
@@ -399,10 +444,15 @@ export const composeRouter = createTRPCRouter({
name: input.id,
sourceType: "raw",
appName: `${projectName}-${generatePassword(6)}`,
isolatedDeployment: true,
});
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, compose.composeId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
compose.composeId,
project.organizationId,
);
}
if (mounts && mounts?.length > 0) {
@@ -446,7 +496,7 @@ export const composeRouter = createTRPCRouter({
return templatesData;
}),
getTags: protectedProcedure.query(async ({ input }) => {
getTags: protectedProcedure.query(async () => {
const allTags = templates.flatMap((template) => template.tags);
const uniqueTags = _.uniq(allTags);
return uniqueTags;

View File

@@ -19,7 +19,9 @@ export const deploymentRouter = createTRPCRouter({
.input(apiFindAllByApplication)
.query(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -32,7 +34,7 @@ export const deploymentRouter = createTRPCRouter({
.input(apiFindAllByCompose)
.query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
@@ -44,7 +46,7 @@ export const deploymentRouter = createTRPCRouter({
.input(apiFindAllByServer)
.query(async ({ input, ctx }) => {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this server",

View File

@@ -28,7 +28,10 @@ export const destinationRouter = createTRPCRouter({
.input(apiCreateDestination)
.mutation(async ({ input, ctx }) => {
try {
return await createDestintation(input, ctx.user.adminId);
return await createDestintation(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -84,7 +87,7 @@ export const destinationRouter = createTRPCRouter({
.input(apiFindOneDestination)
.query(async ({ input, ctx }) => {
const destination = await findDestinationById(input.destinationId);
if (destination.adminId !== ctx.user.adminId) {
if (destination.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this destination",
@@ -94,7 +97,7 @@ export const destinationRouter = createTRPCRouter({
}),
all: protectedProcedure.query(async ({ ctx }) => {
return await db.query.destinations.findMany({
where: eq(destinations.adminId, ctx.user.adminId),
where: eq(destinations.organizationId, ctx.session.activeOrganizationId),
});
}),
remove: adminProcedure
@@ -103,7 +106,7 @@ export const destinationRouter = createTRPCRouter({
try {
const destination = await findDestinationById(input.destinationId);
if (destination.adminId !== ctx.user.adminId) {
if (destination.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this destination",
@@ -111,7 +114,7 @@ export const destinationRouter = createTRPCRouter({
}
return await removeDestinationById(
input.destinationId,
ctx.user.adminId,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw error;
@@ -122,7 +125,7 @@ export const destinationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const destination = await findDestinationById(input.destinationId);
if (destination.adminId !== ctx.user.adminId) {
if (destination.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this destination",
@@ -130,7 +133,7 @@ export const destinationRouter = createTRPCRouter({
}
return await updateDestinationById(input.destinationId, {
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw error;

View File

@@ -30,7 +30,9 @@ export const domainRouter = createTRPCRouter({
try {
if (input.domainType === "compose" && input.composeId) {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (
compose.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
@@ -38,7 +40,10 @@ export const domainRouter = createTRPCRouter({
}
} else if (input.domainType === "application" && input.applicationId) {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -58,7 +63,9 @@ export const domainRouter = createTRPCRouter({
.input(apiFindOneApplication)
.query(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -70,7 +77,7 @@ export const domainRouter = createTRPCRouter({
.input(apiFindCompose)
.query(async ({ input, ctx }) => {
const compose = await findComposeById(input.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
@@ -83,7 +90,7 @@ export const domainRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
return generateTraefikMeDomain(
input.appName,
ctx.user.adminId,
ctx.user.ownerId,
input.serverId,
);
}),
@@ -95,7 +102,9 @@ export const domainRouter = createTRPCRouter({
if (currentDomain.applicationId) {
const newApp = await findApplicationById(currentDomain.applicationId);
if (newApp.project.adminId !== ctx.user.adminId) {
if (
newApp.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -103,7 +112,9 @@ export const domainRouter = createTRPCRouter({
}
} else if (currentDomain.composeId) {
const newCompose = await findComposeById(currentDomain.composeId);
if (newCompose.project.adminId !== ctx.user.adminId) {
if (
newCompose.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
@@ -114,7 +125,8 @@ export const domainRouter = createTRPCRouter({
currentDomain.previewDeploymentId,
);
if (
newPreviewDeployment.application.project.adminId !== ctx.user.adminId
newPreviewDeployment.application.project.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
@@ -143,7 +155,9 @@ export const domainRouter = createTRPCRouter({
const domain = await findDomainById(input.domainId);
if (domain.applicationId) {
const application = await findApplicationById(domain.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -151,7 +165,7 @@ export const domainRouter = createTRPCRouter({
}
} else if (domain.composeId) {
const compose = await findComposeById(domain.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (compose.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",
@@ -166,7 +180,10 @@ export const domainRouter = createTRPCRouter({
const domain = await findDomainById(input.domainId);
if (domain.applicationId) {
const application = await findApplicationById(domain.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -174,7 +191,9 @@ export const domainRouter = createTRPCRouter({
}
} else if (domain.composeId) {
const compose = await findComposeById(domain.composeId);
if (compose.project.adminId !== ctx.user.adminId) {
if (
compose.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this compose",

View File

@@ -1,11 +1,7 @@
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import { db } from "@/server/db";
import { apiRemoveGitProvider, gitProvider } from "@/server/db/schema";
import {
IS_CLOUD,
findGitProviderById,
removeGitProvider,
} from "@dokploy/server";
import { findGitProviderById, removeGitProvider } from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
@@ -18,8 +14,7 @@ export const gitProviderRouter = createTRPCRouter({
github: true,
},
orderBy: desc(gitProvider.createdAt),
...(IS_CLOUD && { where: eq(gitProvider.adminId, ctx.user.adminId) }),
//TODO: Remove this line when the cloud version is ready
where: eq(gitProvider.organizationId, ctx.session.activeOrganizationId),
});
}),
remove: protectedProcedure
@@ -28,8 +23,7 @@ export const gitProviderRouter = createTRPCRouter({
try {
const gitProvider = await findGitProviderById(input.gitProviderId);
if (IS_CLOUD && gitProvider.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (gitProvider.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this Git provider",
@@ -37,9 +31,13 @@ export const gitProviderRouter = createTRPCRouter({
}
return await removeGitProvider(input.gitProviderId);
} catch (error) {
const message =
error instanceof Error
? error.message
: "Error deleting this Git provider";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error deleting this Git provider",
message,
});
}
}),

View File

@@ -6,7 +6,6 @@ import {
apiUpdateGithub,
} from "@/server/db/schema";
import {
IS_CLOUD,
findGithubById,
getGithubBranches,
getGithubRepositories,
@@ -20,8 +19,10 @@ export const githubRouter = createTRPCRouter({
.input(apiFindOneGithub)
.query(async ({ input, ctx }) => {
const githubProvider = await findGithubById(input.githubId);
if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider",
@@ -33,8 +34,10 @@ export const githubRouter = createTRPCRouter({
.input(apiFindOneGithub)
.query(async ({ input, ctx }) => {
const githubProvider = await findGithubById(input.githubId);
if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider",
@@ -46,7 +49,10 @@ export const githubRouter = createTRPCRouter({
.input(apiFindGithubBranches)
.query(async ({ input, ctx }) => {
const githubProvider = await findGithubById(input.githubId || "");
if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) {
if (
githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
@@ -62,12 +68,11 @@ export const githubRouter = createTRPCRouter({
},
});
if (IS_CLOUD) {
// TODO: mAyBe a rEfaCtoR 🤫
result = result.filter(
(provider) => provider.gitProvider.adminId === ctx.user.adminId,
);
}
result = result.filter(
(provider) =>
provider.gitProvider.organizationId ===
ctx.session.activeOrganizationId,
);
const filtered = result
.filter((provider) => haveGithubRequirements(provider))
@@ -89,10 +94,9 @@ export const githubRouter = createTRPCRouter({
try {
const githubProvider = await findGithubById(input.githubId);
if (
IS_CLOUD &&
githubProvider.gitProvider.adminId !== ctx.user.adminId
githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider",
@@ -111,8 +115,10 @@ export const githubRouter = createTRPCRouter({
.input(apiUpdateGithub)
.mutation(async ({ input, ctx }) => {
const githubProvider = await findGithubById(input.githubId);
if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
githubProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this github provider",
@@ -120,7 +126,7 @@ export const githubRouter = createTRPCRouter({
}
await updateGitProvider(input.gitProviderId, {
name: input.name,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
}),
});

View File

@@ -9,7 +9,6 @@ import {
import { db } from "@/server/db";
import {
IS_CLOUD,
createGitlab,
findGitlabById,
getGitlabBranches,
@@ -26,7 +25,7 @@ export const gitlabRouter = createTRPCRouter({
.input(apiCreateGitlab)
.mutation(async ({ input, ctx }) => {
try {
return await createGitlab(input, ctx.user.adminId);
return await createGitlab(input, ctx.session.activeOrganizationId);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -39,8 +38,10 @@ export const gitlabRouter = createTRPCRouter({
.input(apiFindOneGitlab)
.query(async ({ input, ctx }) => {
const gitlabProvider = await findGitlabById(input.gitlabId);
if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider",
@@ -55,12 +56,11 @@ export const gitlabRouter = createTRPCRouter({
},
});
if (IS_CLOUD) {
// TODO: mAyBe a rEfaCtoR 🤫
result = result.filter(
(provider) => provider.gitProvider.adminId === ctx.user.adminId,
);
}
result = result.filter(
(provider) =>
provider.gitProvider.organizationId ===
ctx.session.activeOrganizationId,
);
const filtered = result
.filter((provider) => haveGitlabRequirements(provider))
.map((provider) => {
@@ -78,8 +78,10 @@ export const gitlabRouter = createTRPCRouter({
.input(apiFindOneGitlab)
.query(async ({ input, ctx }) => {
const gitlabProvider = await findGitlabById(input.gitlabId);
if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider",
@@ -92,8 +94,10 @@ export const gitlabRouter = createTRPCRouter({
.input(apiFindGitlabBranches)
.query(async ({ input, ctx }) => {
const gitlabProvider = await findGitlabById(input.gitlabId || "");
if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider",
@@ -107,10 +111,9 @@ export const gitlabRouter = createTRPCRouter({
try {
const gitlabProvider = await findGitlabById(input.gitlabId || "");
if (
IS_CLOUD &&
gitlabProvider.gitProvider.adminId !== ctx.user.adminId
gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
//TODO: Remove this line when the cloud version is ready
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider",
@@ -130,8 +133,10 @@ export const gitlabRouter = createTRPCRouter({
.input(apiUpdateGitlab)
.mutation(async ({ input, ctx }) => {
const gitlabProvider = await findGitlabById(input.gitlabId);
if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) {
//TODO: Remove this line when the cloud version is ready
if (
gitlabProvider.gitProvider.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this Gitlab provider",
@@ -140,7 +145,7 @@ export const gitlabRouter = createTRPCRouter({
if (input.name) {
await updateGitProvider(input.gitProviderId, {
name: input.name,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
await updateGitlab(input.gitlabId, {

View File

@@ -9,6 +9,7 @@ import {
apiSaveExternalPortMariaDB,
apiUpdateMariaDB,
} from "@/server/db/schema";
import { cancelJobs } from "@/server/utils/backup";
import {
IS_CLOUD,
addNewService,
@@ -16,9 +17,9 @@ import {
createMariadb,
createMount,
deployMariadb,
findBackupsByDbId,
findMariadbById,
findProjectById,
findServerById,
removeMariadbById,
removeService,
startService,
@@ -35,8 +36,13 @@ export const mariadbRouter = createTRPCRouter({
.input(apiCreateMariaDB)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -47,15 +53,19 @@ export const mariadbRouter = createTRPCRouter({
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
});
}
const newMariadb = await createMariadb(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newMariadb.mariadbId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newMariadb.mariadbId,
project.organizationId,
);
}
await createMount({
@@ -77,11 +87,16 @@ export const mariadbRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOneMariaDB)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.mariadbId, "access");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.mariadbId,
ctx.session.activeOrganizationId,
"access",
);
}
const mariadb = await findMariadbById(input.mariadbId);
if (mariadb.project.adminId !== ctx.user.adminId) {
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this Mariadb",
@@ -94,7 +109,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiFindOneMariaDB)
.mutation(async ({ input, ctx }) => {
const service = await findMariadbById(input.mariadbId);
if (service.project.adminId !== ctx.user.adminId) {
if (service.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to start this Mariadb",
@@ -131,7 +146,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiSaveExternalPortMariaDB)
.mutation(async ({ input, ctx }) => {
const mongo = await findMariadbById(input.mariadbId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this external port",
@@ -147,7 +162,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiDeployMariaDB)
.mutation(async ({ input, ctx }) => {
const mariadb = await findMariadbById(input.mariadbId);
if (mariadb.project.adminId !== ctx.user.adminId) {
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this Mariadb",
@@ -168,7 +183,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiDeployMariaDB)
.subscription(async ({ input, ctx }) => {
const mariadb = await findMariadbById(input.mariadbId);
if (mariadb.project.adminId !== ctx.user.adminId) {
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this Mariadb",
@@ -185,7 +200,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiChangeMariaDBStatus)
.mutation(async ({ input, ctx }) => {
const mongo = await findMariadbById(input.mariadbId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to change this Mariadb status",
@@ -199,27 +214,34 @@ export const mariadbRouter = createTRPCRouter({
remove: protectedProcedure
.input(apiFindOneMariaDB)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.mariadbId, "delete");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.mariadbId,
ctx.session.activeOrganizationId,
"delete",
);
}
const mongo = await findMariadbById(input.mariadbId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this Mariadb",
});
}
const backups = await findBackupsByDbId(input.mariadbId, "mariadb");
const cleanupOperations = [
async () => await removeService(mongo?.appName, mongo.serverId),
async () => await cancelJobs(backups),
async () => await removeMariadbById(input.mariadbId),
];
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_) {}
}
return mongo;
@@ -228,7 +250,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiSaveEnvironmentVariablesMariaDB)
.mutation(async ({ input, ctx }) => {
const mariadb = await findMariadbById(input.mariadbId);
if (mariadb.project.adminId !== ctx.user.adminId) {
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this environment",
@@ -251,7 +273,7 @@ export const mariadbRouter = createTRPCRouter({
.input(apiResetMariadb)
.mutation(async ({ input, ctx }) => {
const mariadb = await findMariadbById(input.mariadbId);
if (mariadb.project.adminId !== ctx.user.adminId) {
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to reload this Mariadb",
@@ -281,7 +303,7 @@ export const mariadbRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const { mariadbId, ...rest } = input;
const mariadb = await findMariadbById(mariadbId);
if (mariadb.project.adminId !== ctx.user.adminId) {
if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this Mariadb",

View File

@@ -9,6 +9,7 @@ import {
apiSaveExternalPortMongo,
apiUpdateMongo,
} from "@/server/db/schema";
import { cancelJobs } from "@/server/utils/backup";
import {
IS_CLOUD,
addNewService,
@@ -16,6 +17,7 @@ import {
createMongo,
createMount,
deployMongo,
findBackupsByDbId,
findMongoById,
findProjectById,
removeMongoById,
@@ -34,8 +36,13 @@ export const mongoRouter = createTRPCRouter({
.input(apiCreateMongo)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -46,15 +53,19 @@ export const mongoRouter = createTRPCRouter({
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
});
}
const newMongo = await createMongo(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newMongo.mongoId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newMongo.mongoId,
project.organizationId,
);
}
await createMount({
@@ -80,12 +91,17 @@ export const mongoRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOneMongo)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.mongoId, "access");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.mongoId,
ctx.session.activeOrganizationId,
"access",
);
}
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this mongo",
@@ -99,7 +115,7 @@ export const mongoRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const service = await findMongoById(input.mongoId);
if (service.project.adminId !== ctx.user.adminId) {
if (service.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to start this mongo",
@@ -122,7 +138,7 @@ export const mongoRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this mongo",
@@ -144,7 +160,7 @@ export const mongoRouter = createTRPCRouter({
.input(apiSaveExternalPortMongo)
.mutation(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this external port",
@@ -160,7 +176,7 @@ export const mongoRouter = createTRPCRouter({
.input(apiDeployMongo)
.mutation(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this mongo",
@@ -180,7 +196,7 @@ export const mongoRouter = createTRPCRouter({
.input(apiDeployMongo)
.subscription(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this mongo",
@@ -197,7 +213,7 @@ export const mongoRouter = createTRPCRouter({
.input(apiChangeMongoStatus)
.mutation(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to change this mongo status",
@@ -212,7 +228,7 @@ export const mongoRouter = createTRPCRouter({
.input(apiResetMongo)
.mutation(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to reload this mongo",
@@ -240,28 +256,35 @@ export const mongoRouter = createTRPCRouter({
remove: protectedProcedure
.input(apiFindOneMongo)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.mongoId, "delete");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.mongoId,
ctx.session.activeOrganizationId,
"delete",
);
}
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this mongo",
});
}
const backups = await findBackupsByDbId(input.mongoId, "mongo");
const cleanupOperations = [
async () => await removeService(mongo?.appName, mongo.serverId),
async () => await cancelJobs(backups),
async () => await removeMongoById(input.mongoId),
];
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_) {}
}
return mongo;
@@ -270,7 +293,7 @@ export const mongoRouter = createTRPCRouter({
.input(apiSaveEnvironmentVariablesMongo)
.mutation(async ({ input, ctx }) => {
const mongo = await findMongoById(input.mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this environment",
@@ -294,7 +317,7 @@ export const mongoRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const { mongoId, ...rest } = input;
const mongo = await findMongoById(mongoId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this mongo",

View File

@@ -12,6 +12,7 @@ import {
import { TRPCError } from "@trpc/server";
import { cancelJobs } from "@/server/utils/backup";
import {
IS_CLOUD,
addNewService,
@@ -19,6 +20,7 @@ import {
createMount,
createMysql,
deployMySql,
findBackupsByDbId,
findMySqlById,
findProjectById,
removeMySqlById,
@@ -36,8 +38,13 @@ export const mysqlRouter = createTRPCRouter({
.input(apiCreateMySql)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -48,7 +55,7 @@ export const mysqlRouter = createTRPCRouter({
}
1;
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
@@ -56,8 +63,12 @@ export const mysqlRouter = createTRPCRouter({
}
const newMysql = await createMysql(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newMysql.mysqlId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newMysql.mysqlId,
project.organizationId,
);
}
await createMount({
@@ -83,11 +94,16 @@ export const mysqlRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOneMySql)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.mysqlId, "access");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.mysqlId,
ctx.session.activeOrganizationId,
"access",
);
}
const mysql = await findMySqlById(input.mysqlId);
if (mysql.project.adminId !== ctx.user.adminId) {
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this MySQL",
@@ -100,7 +116,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiFindOneMySql)
.mutation(async ({ input, ctx }) => {
const service = await findMySqlById(input.mysqlId);
if (service.project.adminId !== ctx.user.adminId) {
if (service.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to start this MySQL",
@@ -122,7 +138,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiFindOneMySql)
.mutation(async ({ input, ctx }) => {
const mongo = await findMySqlById(input.mysqlId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this MySQL",
@@ -143,7 +159,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiSaveExternalPortMySql)
.mutation(async ({ input, ctx }) => {
const mongo = await findMySqlById(input.mysqlId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this external port",
@@ -159,7 +175,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiDeployMySql)
.mutation(async ({ input, ctx }) => {
const mysql = await findMySqlById(input.mysqlId);
if (mysql.project.adminId !== ctx.user.adminId) {
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this MySQL",
@@ -179,7 +195,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiDeployMySql)
.subscription(async ({ input, ctx }) => {
const mysql = await findMySqlById(input.mysqlId);
if (mysql.project.adminId !== ctx.user.adminId) {
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this MySQL",
@@ -196,7 +212,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiChangeMySqlStatus)
.mutation(async ({ input, ctx }) => {
const mongo = await findMySqlById(input.mysqlId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to change this MySQL status",
@@ -211,7 +227,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiResetMysql)
.mutation(async ({ input, ctx }) => {
const mysql = await findMySqlById(input.mysqlId);
if (mysql.project.adminId !== ctx.user.adminId) {
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to reload this MySQL",
@@ -238,26 +254,33 @@ export const mysqlRouter = createTRPCRouter({
remove: protectedProcedure
.input(apiFindOneMySql)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.mysqlId, "delete");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.mysqlId,
ctx.session.activeOrganizationId,
"delete",
);
}
const mongo = await findMySqlById(input.mysqlId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this MySQL",
});
}
const backups = await findBackupsByDbId(input.mysqlId, "mysql");
const cleanupOperations = [
async () => await removeService(mongo?.appName, mongo.serverId),
async () => await cancelJobs(backups),
async () => await removeMySqlById(input.mysqlId),
];
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_) {}
}
return mongo;
@@ -266,7 +289,7 @@ export const mysqlRouter = createTRPCRouter({
.input(apiSaveEnvironmentVariablesMySql)
.mutation(async ({ input, ctx }) => {
const mysql = await findMySqlById(input.mysqlId);
if (mysql.project.adminId !== ctx.user.adminId) {
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this environment",
@@ -290,7 +313,7 @@ export const mysqlRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const { mysqlId, ...rest } = input;
const mysql = await findMySqlById(mysqlId);
if (mysql.project.adminId !== ctx.user.adminId) {
if (mysql.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this MySQL",

View File

@@ -23,6 +23,8 @@ import {
apiUpdateSlack,
apiUpdateTelegram,
notifications,
server,
users_temp,
} from "@/server/db/schema";
import {
IS_CLOUD,
@@ -36,6 +38,7 @@ import {
sendDiscordNotification,
sendEmailNotification,
sendGotifyNotification,
sendServerThresholdNotifications,
sendSlackNotification,
sendTelegramNotification,
updateDiscordNotification,
@@ -45,15 +48,18 @@ import {
updateTelegramNotification,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
import { desc, eq, sql } from "drizzle-orm";
import { z } from "zod";
// TODO: Uncomment the validations when is cloud ready
export const notificationRouter = createTRPCRouter({
createSlack: adminProcedure
.input(apiCreateSlack)
.mutation(async ({ input, ctx }) => {
try {
return await createSlackNotification(input, ctx.user.adminId);
return await createSlackNotification(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -67,8 +73,7 @@ export const notificationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (notification.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this notification",
@@ -76,7 +81,7 @@ export const notificationRouter = createTRPCRouter({
}
return await updateSlackNotification({
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw error;
@@ -103,7 +108,10 @@ export const notificationRouter = createTRPCRouter({
.input(apiCreateTelegram)
.mutation(async ({ input, ctx }) => {
try {
return await createTelegramNotification(input, ctx.user.adminId);
return await createTelegramNotification(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -118,8 +126,7 @@ export const notificationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (notification.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this notification",
@@ -127,7 +134,7 @@ export const notificationRouter = createTRPCRouter({
}
return await updateTelegramNotification({
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw new TRPCError({
@@ -155,7 +162,10 @@ export const notificationRouter = createTRPCRouter({
.input(apiCreateDiscord)
.mutation(async ({ input, ctx }) => {
try {
return await createDiscordNotification(input, ctx.user.adminId);
return await createDiscordNotification(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -170,8 +180,7 @@ export const notificationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (notification.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this notification",
@@ -179,7 +188,7 @@ export const notificationRouter = createTRPCRouter({
}
return await updateDiscordNotification({
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw new TRPCError({
@@ -216,7 +225,10 @@ export const notificationRouter = createTRPCRouter({
.input(apiCreateEmail)
.mutation(async ({ input, ctx }) => {
try {
return await createEmailNotification(input, ctx.user.adminId);
return await createEmailNotification(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -230,8 +242,7 @@ export const notificationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (notification.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this notification",
@@ -239,7 +250,7 @@ export const notificationRouter = createTRPCRouter({
}
return await updateEmailNotification({
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw new TRPCError({
@@ -272,8 +283,7 @@ export const notificationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (notification.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this notification",
@@ -281,9 +291,13 @@ export const notificationRouter = createTRPCRouter({
}
return await removeNotificationById(input.notificationId);
} catch (error) {
const message =
error instanceof Error
? error.message
: "Error deleting this notification";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error deleting this notification",
message,
});
}
}),
@@ -291,8 +305,7 @@ export const notificationRouter = createTRPCRouter({
.input(apiFindOneNotification)
.query(async ({ input, ctx }) => {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (notification.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this notification",
@@ -310,15 +323,81 @@ export const notificationRouter = createTRPCRouter({
gotify: true,
},
orderBy: desc(notifications.createdAt),
...(IS_CLOUD && { where: eq(notifications.adminId, ctx.user.adminId) }),
// TODO: Remove this line when the cloud version is ready
where: eq(notifications.organizationId, ctx.session.activeOrganizationId),
});
}),
receiveNotification: publicProcedure
.input(
z.object({
ServerType: z.enum(["Dokploy", "Remote"]).default("Dokploy"),
Type: z.enum(["Memory", "CPU"]),
Value: z.number(),
Threshold: z.number(),
Message: z.string(),
Timestamp: z.string(),
Token: z.string(),
}),
)
.mutation(async ({ input }) => {
try {
let organizationId = "";
let ServerName = "";
if (input.ServerType === "Dokploy") {
const result = await db
.select()
.from(users_temp)
.where(
sql`${users_temp.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`,
);
if (!result?.[0]?.id) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Token not found",
});
}
organizationId = result?.[0]?.id;
ServerName = "Dokploy";
} else {
const result = await db
.select()
.from(server)
.where(
sql`${server.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`,
);
if (!result?.[0]?.organizationId) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Token not found",
});
}
organizationId = result?.[0]?.organizationId;
ServerName = "Remote";
}
await sendServerThresholdNotifications(organizationId, {
...input,
ServerName,
});
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error sending the notification",
cause: error,
});
}
}),
createGotify: adminProcedure
.input(apiCreateGotify)
.mutation(async ({ input, ctx }) => {
try {
return await createGotifyNotification(input, ctx.user.adminId);
return await createGotifyNotification(
input,
ctx.session.activeOrganizationId,
);
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -332,7 +411,10 @@ export const notificationRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const notification = await findNotificationById(input.notificationId);
if (IS_CLOUD && notification.adminId !== ctx.user.adminId) {
if (
IS_CLOUD &&
notification.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this notification",
@@ -340,7 +422,7 @@ export const notificationRouter = createTRPCRouter({
}
return await updateGotifyNotification({
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw error;

View File

@@ -0,0 +1,148 @@
import { db } from "@/server/db";
import { invitation, member, organization } from "@/server/db/schema";
import { IS_CLOUD } from "@dokploy/server/index";
import { TRPCError } from "@trpc/server";
import { and, desc, eq, exists } from "drizzle-orm";
import { nanoid } from "nanoid";
import { z } from "zod";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
export const organizationRouter = createTRPCRouter({
create: protectedProcedure
.input(
z.object({
name: z.string(),
logo: z.string().optional(),
}),
)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol !== "owner" && !IS_CLOUD) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can create an organization",
});
}
const result = await db
.insert(organization)
.values({
...input,
slug: nanoid(),
createdAt: new Date(),
ownerId: ctx.user.id,
})
.returning()
.then((res) => res[0]);
console.log("result", result);
if (!result) {
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message: "Failed to create organization",
});
}
await db.insert(member).values({
organizationId: result.id,
role: "owner",
createdAt: new Date(),
userId: ctx.user.id,
});
return result;
}),
all: protectedProcedure.query(async ({ ctx }) => {
const memberResult = await db.query.organization.findMany({
where: (organization) =>
exists(
db
.select()
.from(member)
.where(
and(
eq(member.organizationId, organization.id),
eq(member.userId, ctx.user.id),
),
),
),
});
return memberResult;
}),
one: protectedProcedure
.input(
z.object({
organizationId: z.string(),
}),
)
.query(async ({ input }) => {
return await db.query.organization.findFirst({
where: eq(organization.id, input.organizationId),
});
}),
update: protectedProcedure
.input(
z.object({
organizationId: z.string(),
name: z.string(),
logo: z.string().optional(),
}),
)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol !== "owner" && !IS_CLOUD) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can update it",
});
}
const result = await db
.update(organization)
.set({
name: input.name,
logo: input.logo,
})
.where(eq(organization.id, input.organizationId))
.returning();
return result[0];
}),
delete: protectedProcedure
.input(
z.object({
organizationId: z.string(),
}),
)
.mutation(async ({ ctx, input }) => {
if (ctx.user.rol !== "owner" && !IS_CLOUD) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can delete it",
});
}
const org = await db.query.organization.findFirst({
where: eq(organization.id, input.organizationId),
});
if (!org) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Organization not found",
});
}
if (org.ownerId !== ctx.user.id) {
throw new TRPCError({
code: "FORBIDDEN",
message: "Only the organization owner can delete it",
});
}
const result = await db
.delete(organization)
.where(eq(organization.id, input.organizationId));
return result;
}),
allInvitations: adminProcedure.query(async ({ ctx }) => {
return await db.query.invitation.findMany({
where: eq(invitation.organizationId, ctx.session.activeOrganizationId),
orderBy: [desc(invitation.status), desc(invitation.expiresAt)],
});
}),
});

View File

@@ -44,9 +44,11 @@ export const portRouter = createTRPCRouter({
try {
return removePortById(input.portId);
} catch (error) {
const message =
error instanceof Error ? error.message : "Error input: Deleting port";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error input: Deleting port",
message,
});
}
}),
@@ -56,9 +58,11 @@ export const portRouter = createTRPCRouter({
try {
return updatePortById(input.portId, input);
} catch (error) {
const message =
error instanceof Error ? error.message : "Error updating the port";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error updating the port",
message,
});
}
}),

View File

@@ -1,9 +1,4 @@
import { EventEmitter } from "node:events";
import {
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "@/server/api/trpc";
import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc";
import {
apiChangePostgresStatus,
apiCreatePostgres,
@@ -14,6 +9,7 @@ import {
apiSaveExternalPortPostgres,
apiUpdatePostgres,
} from "@/server/db/schema";
import { cancelJobs } from "@/server/utils/backup";
import {
IS_CLOUD,
addNewService,
@@ -21,6 +17,7 @@ import {
createMount,
createPostgres,
deployPostgres,
findBackupsByDbId,
findPostgresById,
findProjectById,
removePostgresById,
@@ -33,17 +30,19 @@ import {
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import { z } from "zod";
const ee = new EventEmitter();
export const postgresRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreatePostgres)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -54,15 +53,19 @@ export const postgresRouter = createTRPCRouter({
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
});
}
const newPostgres = await createPostgres(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newPostgres.postgresId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newPostgres.postgresId,
project.organizationId,
);
}
await createMount({
@@ -88,12 +91,19 @@ export const postgresRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOnePostgres)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.postgresId, "access");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.postgresId,
ctx.session.activeOrganizationId,
"access",
);
}
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this Postgres",
@@ -107,7 +117,7 @@ export const postgresRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const service = await findPostgresById(input.postgresId);
if (service.project.adminId !== ctx.user.adminId) {
if (service.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to start this Postgres",
@@ -129,7 +139,9 @@ export const postgresRouter = createTRPCRouter({
.input(apiFindOnePostgres)
.mutation(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this Postgres",
@@ -151,7 +163,9 @@ export const postgresRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this external port",
@@ -167,7 +181,9 @@ export const postgresRouter = createTRPCRouter({
.input(apiDeployPostgres)
.mutation(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this Postgres",
@@ -188,7 +204,9 @@ export const postgresRouter = createTRPCRouter({
.input(apiDeployPostgres)
.subscription(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this Postgres",
@@ -205,7 +223,9 @@ export const postgresRouter = createTRPCRouter({
.input(apiChangePostgresStatus)
.mutation(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to change this Postgres status",
@@ -219,20 +239,30 @@ export const postgresRouter = createTRPCRouter({
remove: protectedProcedure
.input(apiFindOnePostgres)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.postgresId, "delete");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.postgresId,
ctx.session.activeOrganizationId,
"delete",
);
}
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this Postgres",
});
}
const backups = await findBackupsByDbId(input.postgresId, "postgres");
const cleanupOperations = [
removeService(postgres.appName, postgres.serverId),
cancelJobs(backups),
removePostgresById(input.postgresId),
];
@@ -244,7 +274,9 @@ export const postgresRouter = createTRPCRouter({
.input(apiSaveEnvironmentVariablesPostgres)
.mutation(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this environment",
@@ -267,7 +299,9 @@ export const postgresRouter = createTRPCRouter({
.input(apiResetPostgres)
.mutation(async ({ input, ctx }) => {
const postgres = await findPostgresById(input.postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to reload this Postgres",
@@ -297,7 +331,9 @@ export const postgresRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const { postgresId, ...rest } = input;
const postgres = await findPostgresById(postgresId);
if (postgres.project.adminId !== ctx.user.adminId) {
if (
postgres.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this Postgres",

View File

@@ -14,7 +14,9 @@ export const previewDeploymentRouter = createTRPCRouter({
.input(apiFindAllByApplication)
.query(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -28,7 +30,10 @@ export const previewDeploymentRouter = createTRPCRouter({
const previewDeployment = await findPreviewDeploymentById(
input.previewDeploymentId,
);
if (previewDeployment.application.project.adminId !== ctx.user.adminId) {
if (
previewDeployment.application.project.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this preview deployment",
@@ -43,7 +48,10 @@ export const previewDeploymentRouter = createTRPCRouter({
const previewDeployment = await findPreviewDeploymentById(
input.previewDeploymentId,
);
if (previewDeployment.application.project.adminId !== ctx.user.adminId) {
if (
previewDeployment.application.project.organizationId !==
ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this preview deployment",

View File

@@ -15,32 +15,34 @@ import {
redis,
} from "@/server/db/schema";
import { TRPCError } from "@trpc/server";
import { and, desc, eq, sql } from "drizzle-orm";
import type { AnyPgColumn } from "drizzle-orm/pg-core";
import {
IS_CLOUD,
addNewProject,
checkProjectAccess,
createProject,
deleteProject,
findAdminById,
findMemberById,
findProjectById,
findUserByAuthId,
findUserById,
updateProjectById,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { and, desc, eq, sql } from "drizzle-orm";
import type { AnyPgColumn } from "drizzle-orm/pg-core";
export const projectRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreateProject)
.mutation(async ({ ctx, input }) => {
try {
if (ctx.user.rol === "user") {
await checkProjectAccess(ctx.user.authId, "create");
if (ctx.user.rol === "member") {
await checkProjectAccess(
ctx.user.id,
"create",
ctx.session.activeOrganizationId,
);
}
const admin = await findAdminById(ctx.user.adminId);
const admin = await findUserById(ctx.user.ownerId);
if (admin.serversQuantity === 0 && IS_CLOUD) {
throw new TRPCError({
@@ -49,9 +51,16 @@ export const projectRouter = createTRPCRouter({
});
}
const project = await createProject(input, ctx.user.adminId);
if (ctx.user.rol === "user") {
await addNewProject(ctx.user.authId, project.projectId);
const project = await createProject(
input,
ctx.session.activeOrganizationId,
);
if (ctx.user.rol === "member") {
await addNewProject(
ctx.user.id,
project.projectId,
ctx.session.activeOrganizationId,
);
}
return project;
@@ -67,15 +76,23 @@ export const projectRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOneProject)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
const { accessedServices } = await findUserByAuthId(ctx.user.authId);
if (ctx.user.rol === "member") {
const { accessedServices } = await findMemberById(
ctx.user.id,
ctx.session.activeOrganizationId,
);
await checkProjectAccess(ctx.user.authId, "access", input.projectId);
await checkProjectAccess(
ctx.user.id,
"access",
ctx.session.activeOrganizationId,
input.projectId,
);
const project = await db.query.projects.findFirst({
where: and(
eq(projects.projectId, input.projectId),
eq(projects.adminId, ctx.user.adminId),
eq(projects.organizationId, ctx.session.activeOrganizationId),
),
with: {
compose: {
@@ -115,7 +132,7 @@ export const projectRouter = createTRPCRouter({
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
@@ -124,9 +141,11 @@ export const projectRouter = createTRPCRouter({
return project;
}),
all: protectedProcedure.query(async ({ ctx }) => {
if (ctx.user.rol === "user") {
const { accessedProjects, accessedServices } = await findUserByAuthId(
ctx.user.authId,
// console.log(ctx.user);
if (ctx.user.rol === "member") {
const { accessedProjects, accessedServices } = await findMemberById(
ctx.user.id,
ctx.session.activeOrganizationId,
);
if (accessedProjects.length === 0) {
@@ -139,7 +158,7 @@ export const projectRouter = createTRPCRouter({
accessedProjects.map((projectId) => sql`${projectId}`),
sql`, `,
)})`,
eq(projects.adminId, ctx.user.adminId),
eq(projects.organizationId, ctx.session.activeOrganizationId),
),
with: {
applications: {
@@ -193,19 +212,26 @@ export const projectRouter = createTRPCRouter({
},
},
},
where: eq(projects.adminId, ctx.user.adminId),
where: eq(projects.organizationId, ctx.session.activeOrganizationId),
orderBy: desc(projects.createdAt),
});
}),
remove: protectedProcedure
.input(apiRemoveProject)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkProjectAccess(ctx.user.authId, "delete");
if (ctx.user.rol === "member") {
await checkProjectAccess(
ctx.user.id,
"delete",
ctx.session.activeOrganizationId,
);
}
const currentProject = await findProjectById(input.projectId);
if (currentProject.adminId !== ctx.user.adminId) {
if (
currentProject.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this project",
@@ -223,7 +249,9 @@ export const projectRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const currentProject = await findProjectById(input.projectId);
if (currentProject.adminId !== ctx.user.adminId) {
if (
currentProject.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this project",

View File

@@ -18,7 +18,9 @@ export const redirectsRouter = createTRPCRouter({
.input(apiCreateRedirect)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -31,7 +33,9 @@ export const redirectsRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
const redirect = await findRedirectById(input.redirectId);
const application = await findApplicationById(redirect.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -44,7 +48,9 @@ export const redirectsRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const redirect = await findRedirectById(input.redirectId);
const application = await findApplicationById(redirect.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -57,7 +63,9 @@ export const redirectsRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const redirect = await findRedirectById(input.redirectId);
const application = await findApplicationById(redirect.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",

View File

@@ -36,8 +36,13 @@ export const redisRouter = createTRPCRouter({
.input(apiCreateRedis)
.mutation(async ({ input, ctx }) => {
try {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.projectId, "create");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.projectId,
ctx.session.activeOrganizationId,
"create",
);
}
if (IS_CLOUD && !input.serverId) {
@@ -48,15 +53,19 @@ export const redisRouter = createTRPCRouter({
}
const project = await findProjectById(input.projectId);
if (project.adminId !== ctx.user.adminId) {
if (project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this project",
});
}
const newRedis = await createRedis(input);
if (ctx.user.rol === "user") {
await addNewService(ctx.user.authId, newRedis.redisId);
if (ctx.user.rol === "member") {
await addNewService(
ctx.user.id,
newRedis.redisId,
project.organizationId,
);
}
await createMount({
@@ -75,12 +84,17 @@ export const redisRouter = createTRPCRouter({
one: protectedProcedure
.input(apiFindOneRedis)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.redisId, "access");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.redisId,
ctx.session.activeOrganizationId,
"access",
);
}
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this Redis",
@@ -93,7 +107,7 @@ export const redisRouter = createTRPCRouter({
.input(apiFindOneRedis)
.mutation(async ({ input, ctx }) => {
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to start this Redis",
@@ -115,7 +129,7 @@ export const redisRouter = createTRPCRouter({
.input(apiResetRedis)
.mutation(async ({ input, ctx }) => {
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to reload this Redis",
@@ -145,7 +159,7 @@ export const redisRouter = createTRPCRouter({
.input(apiFindOneRedis)
.mutation(async ({ input, ctx }) => {
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to stop this Redis",
@@ -166,7 +180,7 @@ export const redisRouter = createTRPCRouter({
.input(apiSaveExternalPortRedis)
.mutation(async ({ input, ctx }) => {
const mongo = await findRedisById(input.redisId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this external port",
@@ -182,7 +196,7 @@ export const redisRouter = createTRPCRouter({
.input(apiDeployRedis)
.mutation(async ({ input, ctx }) => {
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this Redis",
@@ -202,7 +216,7 @@ export const redisRouter = createTRPCRouter({
.input(apiDeployRedis)
.subscription(async ({ input, ctx }) => {
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to deploy this Redis",
@@ -218,7 +232,7 @@ export const redisRouter = createTRPCRouter({
.input(apiChangeRedisStatus)
.mutation(async ({ input, ctx }) => {
const mongo = await findRedisById(input.redisId);
if (mongo.project.adminId !== ctx.user.adminId) {
if (mongo.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to change this Redis status",
@@ -232,19 +246,23 @@ export const redisRouter = createTRPCRouter({
remove: protectedProcedure
.input(apiFindOneRedis)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
await checkServiceAccess(ctx.user.authId, input.redisId, "delete");
if (ctx.user.rol === "member") {
await checkServiceAccess(
ctx.user.id,
input.redisId,
ctx.session.activeOrganizationId,
"delete",
);
}
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this Redis",
});
}
const cleanupOperations = [
async () => await removeService(redis?.appName, redis.serverId),
async () => await removeRedisById(input.redisId),
@@ -253,7 +271,7 @@ export const redisRouter = createTRPCRouter({
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_) {}
}
return redis;
@@ -262,7 +280,7 @@ export const redisRouter = createTRPCRouter({
.input(apiSaveEnvironmentVariablesRedis)
.mutation(async ({ input, ctx }) => {
const redis = await findRedisById(input.redisId);
if (redis.project.adminId !== ctx.user.adminId) {
if (redis.project.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to save this environment",

View File

@@ -1,34 +1,35 @@
import { db } from "@/server/db";
import {
apiCreateRegistry,
apiFindOneRegistry,
apiRemoveRegistry,
apiTestRegistry,
apiUpdateRegistry,
registry,
} from "@/server/db/schema";
import {
IS_CLOUD,
createRegistry,
execAsync,
execAsyncRemote,
findAllRegistryByAdminId,
findRegistryById,
removeRegistry,
updateRegistry,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
export const registryRouter = createTRPCRouter({
create: adminProcedure
.input(apiCreateRegistry)
.mutation(async ({ ctx, input }) => {
return await createRegistry(input, ctx.user.adminId);
return await createRegistry(input, ctx.session.activeOrganizationId);
}),
remove: adminProcedure
.input(apiRemoveRegistry)
.mutation(async ({ ctx, input }) => {
const registry = await findRegistryById(input.registryId);
if (registry.adminId !== ctx.user.adminId) {
if (registry.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this registry",
@@ -41,7 +42,7 @@ export const registryRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const { registryId, ...rest } = input;
const registry = await findRegistryById(registryId);
if (registry.adminId !== ctx.user.adminId) {
if (registry.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this registry",
@@ -61,13 +62,16 @@ export const registryRouter = createTRPCRouter({
return true;
}),
all: protectedProcedure.query(async ({ ctx }) => {
return await findAllRegistryByAdminId(ctx.user.adminId);
const registryResponse = await db.query.registry.findMany({
where: eq(registry.organizationId, ctx.session.activeOrganizationId),
});
return registryResponse;
}),
one: adminProcedure
.input(apiFindOneRegistry)
.query(async ({ input, ctx }) => {
const registry = await findRegistryById(input.registryId);
if (registry.adminId !== ctx.user.adminId) {
if (registry.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this registry",

View File

@@ -18,7 +18,9 @@ export const securityRouter = createTRPCRouter({
.input(apiCreateSecurity)
.mutation(async ({ input, ctx }) => {
const application = await findApplicationById(input.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -31,7 +33,9 @@ export const securityRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
const security = await findSecurityById(input.securityId);
const application = await findApplicationById(security.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -44,7 +48,9 @@ export const securityRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const security = await findSecurityById(input.securityId);
const application = await findApplicationById(security.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",
@@ -57,7 +63,9 @@ export const securityRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
const security = await findSecurityById(input.securityId);
const application = await findApplicationById(security.applicationId);
if (application.project.adminId !== ctx.user.adminId) {
if (
application.project.organizationId !== ctx.session.activeOrganizationId
) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this application",

View File

@@ -6,11 +6,13 @@ import {
apiFindOneServer,
apiRemoveServer,
apiUpdateServer,
apiUpdateServerMonitoring,
applications,
compose,
mariadb,
mongo,
mysql,
organization,
postgres,
redis,
server,
@@ -20,35 +22,40 @@ import {
createServer,
defaultCommand,
deleteServer,
findAdminById,
findServerById,
findServersByAdminId,
findServersByUserId,
findUserById,
getPublicIpWithFallback,
haveActiveServices,
removeDeploymentsByServerId,
serverAudit,
serverSetup,
serverValidate,
setupMonitoring,
updateServerById,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import { observable } from "@trpc/server/observable";
import { and, desc, eq, getTableColumns, isNotNull, sql } from "drizzle-orm";
import { z } from "zod";
export const serverRouter = createTRPCRouter({
create: protectedProcedure
.input(apiCreateServer)
.mutation(async ({ ctx, input }) => {
try {
const admin = await findAdminById(ctx.user.adminId);
const servers = await findServersByAdminId(admin.adminId);
if (IS_CLOUD && servers.length >= admin.serversQuantity) {
const user = await findUserById(ctx.user.ownerId);
const servers = await findServersByUserId(user.id);
if (IS_CLOUD && servers.length >= user.serversQuantity) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "You cannot create more servers",
});
}
const project = await createServer(input, ctx.user.adminId);
const project = await createServer(
input,
ctx.session.activeOrganizationId,
);
return project;
} catch (error) {
throw new TRPCError({
@@ -63,7 +70,7 @@ export const serverRouter = createTRPCRouter({
.input(apiFindOneServer)
.query(async ({ input, ctx }) => {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this server",
@@ -74,7 +81,7 @@ export const serverRouter = createTRPCRouter({
}),
getDefaultCommand: protectedProcedure
.input(apiFindOneServer)
.query(async ({ input, ctx }) => {
.query(async () => {
return defaultCommand();
}),
all: protectedProcedure.query(async ({ ctx }) => {
@@ -91,22 +98,37 @@ export const serverRouter = createTRPCRouter({
.leftJoin(mongo, eq(mongo.serverId, server.serverId))
.leftJoin(mysql, eq(mysql.serverId, server.serverId))
.leftJoin(postgres, eq(postgres.serverId, server.serverId))
.where(eq(server.adminId, ctx.user.adminId))
.where(eq(server.organizationId, ctx.session.activeOrganizationId))
.orderBy(desc(server.createdAt))
.groupBy(server.serverId);
return result;
}),
count: protectedProcedure.query(async ({ ctx }) => {
const organizations = await db.query.organization.findMany({
where: eq(organization.ownerId, ctx.user.id),
with: {
servers: true,
},
});
const servers = organizations.flatMap((org) => org.servers);
return servers.length ?? 0;
}),
withSSHKey: protectedProcedure.query(async ({ ctx }) => {
const result = await db.query.server.findMany({
orderBy: desc(server.createdAt),
where: IS_CLOUD
? and(
isNotNull(server.sshKeyId),
eq(server.adminId, ctx.user.adminId),
eq(server.organizationId, ctx.session.activeOrganizationId),
eq(server.serverStatus, "active"),
)
: and(isNotNull(server.sshKeyId), eq(server.adminId, ctx.user.adminId)),
: and(
isNotNull(server.sshKeyId),
eq(server.organizationId, ctx.session.activeOrganizationId),
),
});
return result;
}),
@@ -115,7 +137,7 @@ export const serverRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to setup this server",
@@ -140,7 +162,7 @@ export const serverRouter = createTRPCRouter({
.subscription(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to setup this server",
@@ -160,7 +182,7 @@ export const serverRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to validate this server",
@@ -202,7 +224,7 @@ export const serverRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to validate this server",
@@ -247,12 +269,54 @@ export const serverRouter = createTRPCRouter({
});
}
}),
setupMonitoring: protectedProcedure
.input(apiUpdateServerMonitoring)
.mutation(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to setup this server",
});
}
await updateServerById(input.serverId, {
metricsConfig: {
server: {
type: "Remote",
refreshRate: input.metricsConfig.server.refreshRate,
retentionDays: input.metricsConfig.server.retentionDays,
port: input.metricsConfig.server.port,
token: input.metricsConfig.server.token,
urlCallback: input.metricsConfig.server.urlCallback,
cronJob: input.metricsConfig.server.cronJob,
thresholds: {
cpu: input.metricsConfig.server.thresholds.cpu,
memory: input.metricsConfig.server.thresholds.memory,
},
},
containers: {
refreshRate: input.metricsConfig.containers.refreshRate,
services: {
include: input.metricsConfig.containers.services.include || [],
exclude: input.metricsConfig.containers.services.exclude || [],
},
},
},
});
const currentServer = await setupMonitoring(input.serverId);
return currentServer;
} catch (error) {
throw error;
}
}),
remove: protectedProcedure
.input(apiRemoveServer)
.mutation(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this server",
@@ -271,12 +335,9 @@ export const serverRouter = createTRPCRouter({
await deleteServer(input.serverId);
if (IS_CLOUD) {
const admin = await findAdminById(ctx.user.adminId);
const admin = await findUserById(ctx.user.ownerId);
await updateServersBasedOnQuantity(
admin.adminId,
admin.serversQuantity,
);
await updateServersBasedOnQuantity(admin.id, admin.serversQuantity);
}
return currentServer;
@@ -289,7 +350,7 @@ export const serverRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to update this server",
@@ -311,11 +372,69 @@ export const serverRouter = createTRPCRouter({
throw error;
}
}),
publicIp: protectedProcedure.query(async ({ ctx }) => {
publicIp: protectedProcedure.query(async () => {
if (IS_CLOUD) {
return "";
}
const ip = await getPublicIpWithFallback();
return ip;
}),
getServerMetrics: protectedProcedure
.input(
z.object({
url: z.string(),
token: z.string(),
dataPoints: z.string(),
}),
)
.query(async ({ input }) => {
try {
const url = new URL(input.url);
url.searchParams.append("limit", input.dataPoints);
const response = await fetch(url.toString(), {
headers: {
Authorization: `Bearer ${input.token}`,
},
});
if (!response.ok) {
throw new Error(
`Error ${response.status}: ${response.statusText}. Ensure the container is running and this service is included in the monitoring configuration.`,
);
}
const data = await response.json();
if (!Array.isArray(data) || data.length === 0) {
throw new Error(
[
"No monitoring data available. This could be because:",
"",
"1. You don't have setup the monitoring service, you can do in web server section.",
"2. If you already have setup the monitoring service, wait a few minutes and refresh the page.",
].join("\n"),
);
}
return data as {
cpu: string;
cpuModel: string;
cpuCores: number;
cpuPhysicalCores: number;
cpuSpeed: number;
os: string;
distro: string;
kernel: string;
arch: string;
memUsed: string;
memUsedGB: string;
memTotal: string;
uptime: number;
diskUsed: string;
totalDisk: string;
networkIn: string;
networkOut: string;
timestamp: string;
}[];
} catch (error) {
throw error;
}
}),
});

View File

@@ -22,9 +22,8 @@ import {
cleanUpUnusedVolumes,
execAsync,
execAsyncRemote,
findAdmin,
findAdminById,
findServerById,
findUserById,
getDokployImage,
getDokployImageTag,
getUpdateData,
@@ -47,10 +46,10 @@ import {
startServiceRemote,
stopService,
stopServiceRemote,
updateAdmin,
updateLetsEncryptEmail,
updateServerById,
updateServerTraefik,
updateUser,
writeConfig,
writeMainConfig,
writeTraefikConfigInPath,
@@ -164,7 +163,7 @@ export const settingsRouter = createTRPCRouter({
if (IS_CLOUD) {
return true;
}
await updateAdmin(ctx.user.authId, {
await updateUser(ctx.user.id, {
sshPrivateKey: input.sshPrivateKey,
});
@@ -176,7 +175,7 @@ export const settingsRouter = createTRPCRouter({
if (IS_CLOUD) {
return true;
}
const admin = await updateAdmin(ctx.user.authId, {
const user = await updateUser(ctx.user.id, {
host: input.host,
...(input.letsEncryptEmail && {
letsEncryptEmail: input.letsEncryptEmail,
@@ -184,25 +183,25 @@ export const settingsRouter = createTRPCRouter({
certificateType: input.certificateType,
});
if (!admin) {
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Admin not found",
message: "User not found",
});
}
updateServerTraefik(admin, input.host);
updateServerTraefik(user, input.host);
if (input.letsEncryptEmail) {
updateLetsEncryptEmail(input.letsEncryptEmail);
}
return admin;
return user;
}),
cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => {
if (IS_CLOUD) {
return true;
}
await updateAdmin(ctx.user.authId, {
await updateUser(ctx.user.id, {
sshPrivateKey: null,
});
return true;
@@ -217,7 +216,7 @@ export const settingsRouter = createTRPCRouter({
const server = await findServerById(input.serverId);
if (server.adminId !== ctx.user.adminId) {
if (server.organizationId !== ctx.session?.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this server",
@@ -246,7 +245,7 @@ export const settingsRouter = createTRPCRouter({
await cleanUpUnusedImages(server.serverId);
await cleanUpDockerBuilder(server.serverId);
await cleanUpSystemPrune(server.serverId);
await sendDockerCleanupNotifications(server.adminId);
await sendDockerCleanupNotifications(server.organizationId);
});
}
} else {
@@ -262,19 +261,11 @@ export const settingsRouter = createTRPCRouter({
}
}
} else if (!IS_CLOUD) {
const admin = await findAdminById(ctx.user.adminId);
if (admin.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to access this admin",
});
}
const adminUpdated = await updateAdmin(ctx.user.authId, {
const userUpdated = await updateUser(ctx.user.id, {
enableDockerCleanup: input.enableDockerCleanup,
});
if (adminUpdated?.enableDockerCleanup) {
if (userUpdated?.enableDockerCleanup) {
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
console.log(
`Docker Cleanup ${new Date().toLocaleString()}] Running...`,
@@ -282,7 +273,9 @@ export const settingsRouter = createTRPCRouter({
await cleanUpUnusedImages();
await cleanUpDockerBuilder();
await cleanUpSystemPrune();
await sendDockerCleanupNotifications(admin.adminId);
await sendDockerCleanupNotifications(
ctx.session.activeOrganizationId,
);
});
} else {
const currentJob = scheduledJobs["docker-cleanup"];
@@ -345,7 +338,7 @@ export const settingsRouter = createTRPCRouter({
writeConfig("middlewares", input.traefikConfig);
return true;
}),
getUpdateData: adminProcedure.mutation(async () => {
getUpdateData: protectedProcedure.mutation(async () => {
if (IS_CLOUD) {
return DEFAULT_UPDATE_DATA;
}
@@ -373,18 +366,21 @@ export const settingsRouter = createTRPCRouter({
return true;
}),
getDokployVersion: adminProcedure.query(() => {
getDokployVersion: protectedProcedure.query(() => {
return packageInfo.version;
}),
getReleaseTag: adminProcedure.query(() => {
getReleaseTag: protectedProcedure.query(() => {
return getDokployImageTag();
}),
readDirectories: protectedProcedure
.input(apiServerSchema)
.query(async ({ ctx, input }) => {
try {
if (ctx.user.rol === "user") {
const canAccess = await canAccessToTraefikFiles(ctx.user.authId);
if (ctx.user.rol === "member") {
const canAccess = await canAccessToTraefikFiles(
ctx.user.id,
ctx.session.activeOrganizationId,
);
if (!canAccess) {
throw new TRPCError({ code: "UNAUTHORIZED" });
@@ -401,8 +397,11 @@ export const settingsRouter = createTRPCRouter({
updateTraefikFile: protectedProcedure
.input(apiModifyTraefikConfig)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
const canAccess = await canAccessToTraefikFiles(ctx.user.authId);
if (ctx.user.rol === "member") {
const canAccess = await canAccessToTraefikFiles(
ctx.user.id,
ctx.session.activeOrganizationId,
);
if (!canAccess) {
throw new TRPCError({ code: "UNAUTHORIZED" });
@@ -419,8 +418,11 @@ export const settingsRouter = createTRPCRouter({
readTraefikFile: protectedProcedure
.input(apiReadTraefikConfig)
.query(async ({ input, ctx }) => {
if (ctx.user.rol === "user") {
const canAccess = await canAccessToTraefikFiles(ctx.user.authId);
if (ctx.user.rol === "member") {
const canAccess = await canAccessToTraefikFiles(
ctx.user.id,
ctx.session.activeOrganizationId,
);
if (!canAccess) {
throw new TRPCError({ code: "UNAUTHORIZED" });
@@ -428,12 +430,12 @@ export const settingsRouter = createTRPCRouter({
}
return readConfigInPath(input.path, input.serverId);
}),
getIp: protectedProcedure.query(async () => {
getIp: protectedProcedure.query(async ({ ctx }) => {
if (IS_CLOUD) {
return true;
}
const admin = await findAdmin();
return admin.serverIp;
const user = await findUserById(ctx.user.ownerId);
return user.serverIp;
}),
getOpenApiDocument: protectedProcedure.query(
@@ -480,10 +482,28 @@ export const settingsRouter = createTRPCRouter({
openApiDocument.info = {
title: "Dokploy API",
description: "Endpoints for dokploy",
// TODO: get version from package.json
version: "1.0.0",
};
// Add security schemes configuration
openApiDocument.components = {
...openApiDocument.components,
securitySchemes: {
apiKey: {
type: "apiKey",
in: "header",
name: "x-api-key",
description: "API key authentication",
},
},
};
// Apply security globally to all endpoints
openApiDocument.security = [
{
apiKey: [],
},
];
return openApiDocument;
},
),
@@ -655,7 +675,7 @@ export const settingsRouter = createTRPCRouter({
return true;
}),
isCloud: protectedProcedure.query(async () => {
isCloud: publicProcedure.query(async () => {
return IS_CLOUD;
}),
health: publicProcedure.query(async () => {
@@ -715,7 +735,12 @@ export const settingsRouter = createTRPCRouter({
try {
return await checkGPUStatus(input.serverId || "");
} catch (error) {
throw new Error("Failed to check GPU status");
const message =
error instanceof Error ? error.message : "Failed to check GPU status";
throw new TRPCError({
code: "BAD_REQUEST",
message,
});
}
}),
updateTraefikPorts: adminProcedure

View File

@@ -9,7 +9,6 @@ import {
sshKeys,
} from "@/server/db/schema";
import {
IS_CLOUD,
createSshKey,
findSSHKeyById,
generateSSHKey,
@@ -26,7 +25,7 @@ export const sshRouter = createTRPCRouter({
try {
await createSshKey({
...input,
adminId: ctx.user.adminId,
organizationId: ctx.session.activeOrganizationId,
});
} catch (error) {
throw new TRPCError({
@@ -41,8 +40,7 @@ export const sshRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const sshKey = await findSSHKeyById(input.sshKeyId);
if (IS_CLOUD && sshKey.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (sshKey.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to delete this SSH key",
@@ -59,8 +57,7 @@ export const sshRouter = createTRPCRouter({
.query(async ({ input, ctx }) => {
const sshKey = await findSSHKeyById(input.sshKeyId);
if (IS_CLOUD && sshKey.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (sshKey.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this SSH key",
@@ -70,10 +67,9 @@ export const sshRouter = createTRPCRouter({
}),
all: protectedProcedure.query(async ({ ctx }) => {
return await db.query.sshKeys.findMany({
...(IS_CLOUD && { where: eq(sshKeys.adminId, ctx.user.adminId) }),
where: eq(sshKeys.organizationId, ctx.session.activeOrganizationId),
orderBy: desc(sshKeys.createdAt),
});
// TODO: Remove this line when the cloud version is ready
}),
generate: protectedProcedure
.input(apiGenerateSSHKey)
@@ -85,8 +81,7 @@ export const sshRouter = createTRPCRouter({
.mutation(async ({ input, ctx }) => {
try {
const sshKey = await findSSHKeyById(input.sshKeyId);
if (IS_CLOUD && sshKey.adminId !== ctx.user.adminId) {
// TODO: Remove isCloud in the next versions of dokploy
if (sshKey.organizationId !== ctx.session.activeOrganizationId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this SSH key",

View File

@@ -1,9 +1,9 @@
import { WEBSITE_URL, getStripeItems } from "@/server/utils/stripe";
import {
IS_CLOUD,
findAdminById,
findServersByAdminId,
updateAdmin,
findServersByUserId,
findUserById,
updateUser,
} from "@dokploy/server";
import { TRPCError } from "@trpc/server";
import Stripe from "stripe";
@@ -12,8 +12,8 @@ import { adminProcedure, createTRPCRouter } from "../trpc";
export const stripeRouter = createTRPCRouter({
getProducts: adminProcedure.query(async ({ ctx }) => {
const admin = await findAdminById(ctx.user.adminId);
const stripeCustomerId = admin.stripeCustomerId;
const user = await findUserById(ctx.user.ownerId);
const stripeCustomerId = user.stripeCustomerId;
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-09-30.acacia",
@@ -56,15 +56,15 @@ export const stripeRouter = createTRPCRouter({
});
const items = getStripeItems(input.serverQuantity, input.isAnnual);
const admin = await findAdminById(ctx.user.adminId);
const user = await findUserById(ctx.user.id);
let stripeCustomerId = admin.stripeCustomerId;
let stripeCustomerId = user.stripeCustomerId;
if (stripeCustomerId) {
const customer = await stripe.customers.retrieve(stripeCustomerId);
if (customer.deleted) {
await updateAdmin(admin.authId, {
await updateUser(user.id, {
stripeCustomerId: null,
});
stripeCustomerId = null;
@@ -78,7 +78,7 @@ export const stripeRouter = createTRPCRouter({
customer: stripeCustomerId,
}),
metadata: {
adminId: admin.adminId,
adminId: user.id,
},
allow_promotion_codes: true,
success_url: `${WEBSITE_URL}/dashboard/settings/servers?success=true`,
@@ -87,45 +87,43 @@ export const stripeRouter = createTRPCRouter({
return { sessionId: session.id };
}),
createCustomerPortalSession: adminProcedure.mutation(
async ({ ctx, input }) => {
const admin = await findAdminById(ctx.user.adminId);
createCustomerPortalSession: adminProcedure.mutation(async ({ ctx }) => {
const user = await findUserById(ctx.user.id);
if (!admin.stripeCustomerId) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Stripe Customer ID not found",
});
}
const stripeCustomerId = admin.stripeCustomerId;
if (!user.stripeCustomerId) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Stripe Customer ID not found",
});
}
const stripeCustomerId = user.stripeCustomerId;
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-09-30.acacia",
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
apiVersion: "2024-09-30.acacia",
});
try {
const session = await stripe.billingPortal.sessions.create({
customer: stripeCustomerId,
return_url: `${WEBSITE_URL}/dashboard/settings/billing`,
});
try {
const session = await stripe.billingPortal.sessions.create({
customer: stripeCustomerId,
return_url: `${WEBSITE_URL}/dashboard/settings/billing`,
});
return { url: session.url };
} catch (error) {
return {
url: "",
};
}
},
),
return { url: session.url };
} catch (_) {
return {
url: "",
};
}
}),
canCreateMoreServers: adminProcedure.query(async ({ ctx }) => {
const admin = await findAdminById(ctx.user.adminId);
const servers = await findServersByAdminId(admin.adminId);
const user = await findUserById(ctx.user.ownerId);
const servers = await findServersByUserId(user.id);
if (!IS_CLOUD) {
return true;
}
return servers.length < admin.serversQuantity;
return servers.length < user.serversQuantity;
}),
});

View File

@@ -1,34 +1,330 @@
import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema";
import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server";
import {
IS_CLOUD,
findOrganizationById,
findUserById,
getUserByToken,
removeUserById,
updateUser,
createApiKey,
} from "@dokploy/server";
import { db } from "@dokploy/server/db";
import {
account,
apiAssignPermissions,
apiFindOneToken,
apiUpdateUser,
invitation,
member,
apikey,
} from "@dokploy/server/db/schema";
import * as bcrypt from "bcrypt";
import { TRPCError } from "@trpc/server";
import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc";
import { and, asc, eq, gt } from "drizzle-orm";
import { z } from "zod";
import {
adminProcedure,
createTRPCRouter,
protectedProcedure,
publicProcedure,
} from "../trpc";
const apiCreateApiKey = z.object({
name: z.string().min(1),
prefix: z.string().optional(),
expiresIn: z.number().optional(),
metadata: z.object({
organizationId: z.string(),
}),
// Rate limiting
rateLimitEnabled: z.boolean().optional(),
rateLimitTimeWindow: z.number().optional(),
rateLimitMax: z.number().optional(),
// Request limiting
remaining: z.number().optional(),
refillAmount: z.number().optional(),
refillInterval: z.number().optional(),
});
export const userRouter = createTRPCRouter({
all: adminProcedure.query(async ({ ctx }) => {
return await findUsers(ctx.user.adminId);
return await db.query.member.findMany({
where: eq(member.organizationId, ctx.session.activeOrganizationId),
with: {
user: true,
},
orderBy: [asc(member.createdAt)],
});
}),
byAuthId: protectedProcedure
.input(apiFindOneUserByAuth)
one: protectedProcedure
.input(
z.object({
userId: z.string(),
}),
)
.query(async ({ input, ctx }) => {
const user = await findUserByAuthId(input.authId);
if (user.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this user",
});
}
return user;
const memberResult = await db.query.member.findFirst({
where: and(
eq(member.userId, input.userId),
eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
),
with: {
user: true,
},
});
return memberResult;
}),
byUserId: protectedProcedure
.input(apiFindOneUser)
.query(async ({ input, ctx }) => {
const user = await findUserById(input.userId);
if (user.adminId !== ctx.user.adminId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to access this user",
get: protectedProcedure.query(async ({ ctx }) => {
const memberResult = await db.query.member.findFirst({
where: and(
eq(member.userId, ctx.user.id),
eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
),
with: {
user: {
with: {
apiKeys: true,
},
},
},
});
return memberResult;
}),
getServerMetrics: protectedProcedure.query(async ({ ctx }) => {
const memberResult = await db.query.member.findFirst({
where: and(
eq(member.userId, ctx.user.id),
eq(member.organizationId, ctx.session?.activeOrganizationId || ""),
),
with: {
user: true,
},
});
return memberResult?.user;
}),
update: protectedProcedure
.input(apiUpdateUser)
.mutation(async ({ input, ctx }) => {
if (input.password || input.currentPassword) {
const currentAuth = await db.query.account.findFirst({
where: eq(account.userId, ctx.user.id),
});
const correctPassword = bcrypt.compareSync(
input.currentPassword || "",
currentAuth?.password || "",
);
if (!correctPassword) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Current password is incorrect",
});
}
if (!input.password) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "New password is required",
});
}
await db
.update(account)
.set({
password: bcrypt.hashSync(input.password, 10),
})
.where(eq(account.userId, ctx.user.id));
}
return user;
return await updateUser(ctx.user.id, input);
}),
getUserByToken: publicProcedure
.input(apiFindOneToken)
.query(async ({ input }) => {
return await getUserByToken(input.token);
}),
getMetricsToken: protectedProcedure.query(async ({ ctx }) => {
const user = await findUserById(ctx.user.ownerId);
return {
serverIp: user.serverIp,
enabledFeatures: user.enablePaidFeatures,
metricsConfig: user?.metricsConfig,
};
}),
remove: protectedProcedure
.input(
z.object({
userId: z.string(),
}),
)
.mutation(async ({ input }) => {
if (IS_CLOUD) {
return true;
}
return await removeUserById(input.userId);
}),
assignPermissions: adminProcedure
.input(apiAssignPermissions)
.mutation(async ({ input, ctx }) => {
try {
const organization = await findOrganizationById(
ctx.session?.activeOrganizationId || "",
);
if (organization?.ownerId !== ctx.user.ownerId) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to assign permissions",
});
}
const { id, ...rest } = input;
await db
.update(member)
.set({
...rest,
})
.where(
and(
eq(member.userId, input.id),
eq(
member.organizationId,
ctx.session?.activeOrganizationId || "",
),
),
);
} catch (error) {
throw error;
}
}),
getInvitations: protectedProcedure.query(async ({ ctx }) => {
return await db.query.invitation.findMany({
where: and(
eq(invitation.email, ctx.user.email),
gt(invitation.expiresAt, new Date()),
eq(invitation.status, "pending"),
),
with: {
organization: true,
},
});
}),
getContainerMetrics: protectedProcedure
.input(
z.object({
url: z.string(),
token: z.string(),
appName: z.string(),
dataPoints: z.string(),
}),
)
.query(async ({ input }) => {
try {
if (!input.appName) {
throw new Error(
[
"No Application Selected:",
"",
"Make Sure to select an application to monitor.",
].join("\n"),
);
}
const url = new URL(`${input.url}/metrics/containers`);
url.searchParams.append("limit", input.dataPoints);
url.searchParams.append("appName", input.appName);
const response = await fetch(url.toString(), {
headers: {
Authorization: `Bearer ${input.token}`,
},
});
if (!response.ok) {
throw new Error(
`Error ${response.status}: ${response.statusText}. Please verify that the application "${input.appName}" is running and this service is included in the monitoring configuration.`,
);
}
const data = await response.json();
if (!Array.isArray(data) || data.length === 0) {
throw new Error(
[
`No monitoring data available for "${input.appName}". This could be because:`,
"",
"1. The container was recently started - wait a few minutes for data to be collected",
"2. The container is not running - verify its status",
"3. The service is not included in your monitoring configuration",
].join("\n"),
);
}
return data as {
containerId: string;
containerName: string;
containerImage: string;
containerLabels: string;
containerCommand: string;
containerCreated: string;
}[];
} catch (error) {
throw error;
}
}),
generateToken: protectedProcedure.mutation(async () => {
return "token";
}),
deleteApiKey: protectedProcedure
.input(
z.object({
apiKeyId: z.string(),
}),
)
.mutation(async ({ input, ctx }) => {
try {
const apiKeyToDelete = await db.query.apikey.findFirst({
where: eq(apikey.id, input.apiKeyId),
});
if (!apiKeyToDelete) {
throw new TRPCError({
code: "NOT_FOUND",
message: "API key not found",
});
}
if (apiKeyToDelete.userId !== ctx.user.id) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not authorized to delete this API key",
});
}
await db.delete(apikey).where(eq(apikey.id, input.apiKeyId));
return true;
} catch (error) {
throw error;
}
}),
createApiKey: protectedProcedure
.input(apiCreateApiKey)
.mutation(async ({ input, ctx }) => {
const apiKey = await createApiKey(ctx.user.id, input);
return apiKey;
}),
checkUserOrganizations: protectedProcedure
.input(
z.object({
userId: z.string(),
}),
)
.query(async ({ input }) => {
const organizations = await db.query.member.findMany({
where: eq(member.userId, input.userId),
});
return organizations.length;
}),
});

View File

@@ -9,7 +9,7 @@
// import { getServerAuthSession } from "@/server/auth";
import { db } from "@/server/db";
import { validateBearerToken, validateRequest } from "@dokploy/server";
import { validateRequest } from "@dokploy/server/lib/auth";
import type { OpenApiMeta } from "@dokploy/trpc-openapi";
import { TRPCError, initTRPC } from "@trpc/server";
import type { CreateNextContextOptions } from "@trpc/server/adapters/next";
@@ -18,7 +18,7 @@ import {
experimental_isMultipartFormDataRequest,
experimental_parseMultipartFormData,
} from "@trpc/server/adapters/node-http/content-type/form-data";
import type { Session, User } from "lucia";
import type { Session, User } from "better-auth";
import superjson from "superjson";
import { ZodError } from "zod";
/**
@@ -30,8 +30,8 @@ import { ZodError } from "zod";
*/
interface CreateContextOptions {
user: (User & { authId: string; adminId: string }) | null;
session: Session | null;
user: (User & { rol: "member" | "admin" | "owner"; ownerId: string }) | null;
session: (Session & { activeOrganizationId: string }) | null;
req: CreateNextContextOptions["req"];
res: CreateNextContextOptions["res"];
}
@@ -65,30 +65,29 @@ const createInnerTRPCContext = (opts: CreateContextOptions) => {
export const createTRPCContext = async (opts: CreateNextContextOptions) => {
const { req, res } = opts;
let { session, user } = await validateBearerToken(req);
if (!session) {
const cookieResult = await validateRequest(req, res);
session = cookieResult.session;
user = cookieResult.user;
}
// Get from the request
const { session, user } = await validateRequest(req);
return createInnerTRPCContext({
req,
res,
session: session,
...((user && {
user: {
authId: user.id,
email: user.email,
rol: user.rol,
id: user.id,
secret: user.secret,
adminId: user.adminId,
},
}) || {
user: null,
}),
// @ts-ignore
session: session
? {
...session,
activeOrganizationId: session.activeOrganizationId || "",
}
: null,
// @ts-ignore
user: user
? {
...user,
email: user.email,
rol: user.role as "owner" | "member" | "admin",
id: user.id,
ownerId: user.ownerId,
}
: null,
});
};
@@ -181,7 +180,7 @@ export const uploadProcedure = async (opts: any) => {
};
export const cliProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session || !ctx.user || ctx.user.rol !== "admin") {
if (!ctx.session || !ctx.user || ctx.user.rol !== "owner") {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({
@@ -195,7 +194,7 @@ export const cliProcedure = t.procedure.use(({ ctx, next }) => {
});
export const adminProcedure = t.procedure.use(({ ctx, next }) => {
if (!ctx.session || !ctx.user || ctx.user.rol !== "admin") {
if (!ctx.session || !ctx.user || ctx.user.rol !== "owner") {
throw new TRPCError({ code: "UNAUTHORIZED" });
}
return next({

View File

@@ -1,16 +1,10 @@
import bc from "bcrypt";
import { drizzle } from "drizzle-orm/postgres-js";
import postgres from "postgres";
import { users } from "./schema";
const connectionString = process.env.DATABASE_URL!;
const pg = postgres(connectionString, { max: 1 });
const db = drizzle(pg);
function password(txt: string) {
return bc.hashSync(txt, 10);
}
const _db = drizzle(pg);
async function seed() {
console.log("> Seed:", process.env.DATABASE_PATH, "\n");

View File

@@ -1,3 +1,9 @@
import {
type BackupScheduleList,
IS_CLOUD,
removeScheduleBackup,
} from "@dokploy/server/index";
type QueueJob =
| {
type: "backup";
@@ -59,3 +65,19 @@ export const updateJob = async (job: QueueJob) => {
throw error;
}
};
export const cancelJobs = async (backups: BackupScheduleList) => {
for (const backup of backups) {
if (backup.enabled) {
if (IS_CLOUD) {
await removeJob({
cronSchedule: backup.schedule,
backupId: backup.backupId,
type: "backup",
});
} else {
removeScheduleBackup(backup.backupId);
}
}
}
};

View File

@@ -6,7 +6,7 @@ export const isWSL = async () => {
const { stdout } = await execAsync("uname -r");
const isWSL = stdout.includes("microsoft");
return isWSL;
} catch (error) {
} catch (_error) {
return false;
}
};

View File

@@ -1,5 +1,5 @@
import type http from "node:http";
import { findServerById, validateWebSocketRequest } from "@dokploy/server";
import { findServerById, validateRequest } from "@dokploy/server";
import { spawn } from "node-pty";
import { Client } from "ssh2";
import { WebSocketServer } from "ws";
@@ -35,7 +35,7 @@ export const setupDockerContainerLogsWebSocketServer = (
const since = url.searchParams.get("since");
const serverId = url.searchParams.get("serverId");
const runType = url.searchParams.get("runType");
const { user, session } = await validateWebSocketRequest(req);
const { user, session } = await validateRequest(req);
if (!containerId) {
ws.close(4000, "containerId no provided");

View File

@@ -1,5 +1,5 @@
import type http from "node:http";
import { findServerById, validateWebSocketRequest } from "@dokploy/server";
import { findServerById, validateRequest } from "@dokploy/server";
import { spawn } from "node-pty";
import { Client } from "ssh2";
import { WebSocketServer } from "ws";
@@ -32,7 +32,7 @@ export const setupDockerContainerTerminalWebSocketServer = (
const containerId = url.searchParams.get("containerId");
const activeWay = url.searchParams.get("activeWay");
const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req);
const { user, session } = await validateRequest(req);
if (!containerId) {
ws.close(4000, "containerId no provided");
@@ -50,8 +50,8 @@ export const setupDockerContainerTerminalWebSocketServer = (
throw new Error("No SSH key available for this server");
const conn = new Client();
let stdout = "";
let stderr = "";
let _stdout = "";
let _stderr = "";
conn
.once("ready", () => {
conn.exec(
@@ -61,16 +61,16 @@ export const setupDockerContainerTerminalWebSocketServer = (
if (err) throw err;
stream
.on("close", (code: number, signal: string) => {
.on("close", (code: number, _signal: string) => {
ws.send(`\nContainer closed with code: ${code}\n`);
conn.end();
})
.on("data", (data: string) => {
stdout += data.toString();
_stdout += data.toString();
ws.send(data.toString());
})
.stderr.on("data", (data) => {
stderr += data.toString();
_stderr += data.toString();
ws.send(data.toString());
console.error("Error: ", data.toString());
});

View File

@@ -1,9 +1,10 @@
import type http from "node:http";
import {
docker,
execAsync,
getLastAdvancedStatsFile,
recordAdvancedStats,
validateWebSocketRequest,
validateRequest,
} from "@dokploy/server";
import { WebSocketServer } from "ws";
@@ -35,7 +36,7 @@ export const setupDockerStatsMonitoringSocketServer = (
| "application"
| "stack"
| "docker-compose";
const { user, session } = await validateWebSocketRequest(req);
const { user, session } = await validateRequest(req);
if (!appName) {
ws.close(4000, "appName no provided");
@@ -70,12 +71,16 @@ export const setupDockerStatsMonitoringSocketServer = (
ws.close(4000, "Container not running");
return;
}
const { stdout, stderr } = await execAsync(
`docker stats ${container.Id} --no-stream --format \'{"BlockIO":"{{.BlockIO}}","CPUPerc":"{{.CPUPerc}}","Container":"{{.Container}}","ID":"{{.ID}}","MemPerc":"{{.MemPerc}}","MemUsage":"{{.MemUsage}}","Name":"{{.Name}}","NetIO":"{{.NetIO}}"}\'`,
);
if (stderr) {
console.error("Docker stats error:", stderr);
return;
}
const stat = JSON.parse(stdout);
const stats = await docker.getContainer(container.Id).stats({
stream: false,
});
await recordAdvancedStats(stats, appName);
await recordAdvancedStats(stat, appName);
const data = await getLastAdvancedStatsFile(appName);
ws.send(

View File

@@ -1,4 +1,5 @@
import type http from "node:http";
import { validateRequest } from "@dokploy/server/index";
import { applyWSSHandler } from "@trpc/server/adapters/ws";
import { WebSocketServer } from "ws";
import { appRouter } from "../api/root";
@@ -31,6 +32,12 @@ export const setupDrawerLogsWebSocketServer = (
});
wssTerm.on("connection", async (ws, req) => {
const url = new URL(req.url || "", `http://${req.headers.host}`);
const _url = new URL(req.url || "", `http://${req.headers.host}`);
const { user, session } = await validateRequest(req);
if (!user || !session) {
ws.close();
return;
}
});
};

View File

@@ -1,6 +1,6 @@
import { spawn } from "node:child_process";
import type http from "node:http";
import { findServerById, validateWebSocketRequest } from "@dokploy/server";
import { findServerById, validateRequest } from "@dokploy/server";
import { Client } from "ssh2";
import { WebSocketServer } from "ws";
@@ -29,7 +29,7 @@ export const setupDeploymentLogsWebSocketServer = (
const url = new URL(req.url || "", `http://${req.headers.host}`);
const logPath = url.searchParams.get("logPath");
const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req);
const { user, session } = await validateRequest(req);
if (!logPath) {
console.log("logPath no provided");
@@ -103,7 +103,7 @@ export const setupDeploymentLogsWebSocketServer = (
ws.close();
});
}
} catch (error) {
} catch (_error) {
// @ts-ignore
// const errorMessage = error?.message as unknown as string;
// ws.send(errorMessage);

View File

@@ -1,9 +1,5 @@
import type http from "node:http";
import {
IS_CLOUD,
findServerById,
validateWebSocketRequest,
} from "@dokploy/server";
import { IS_CLOUD, findServerById, validateRequest } from "@dokploy/server";
import { publicIpv4, publicIpv6 } from "public-ip";
import { Client, type ConnectConfig } from "ssh2";
import { WebSocketServer } from "ws";
@@ -71,7 +67,7 @@ export const setupTerminalWebSocketServer = (
wssTerm.on("connection", async (ws, req) => {
const url = new URL(req.url || "", `http://${req.headers.host}`);
const serverId = url.searchParams.get("serverId");
const { user, session } = await validateWebSocketRequest(req);
const { user, session } = await validateRequest(req);
if (!user || !session || !serverId) {
ws.close();
return;
@@ -148,8 +144,8 @@ export const setupTerminalWebSocketServer = (
}
const conn = new Client();
let stdout = "";
let stderr = "";
let _stdout = "";
let _stderr = "";
ws.send("Connecting...\n");
@@ -162,16 +158,16 @@ export const setupTerminalWebSocketServer = (
if (err) throw err;
stream
.on("close", (code: number, signal: string) => {
.on("close", (code: number, _signal: string) => {
ws.send(`\nContainer closed with code: ${code}\n`);
conn.end();
})
.on("data", (data: string) => {
stdout += data.toString();
_stdout += data.toString();
ws.send(data.toString());
})
.stderr.on("data", (data) => {
stderr += data.toString();
_stderr += data.toString();
ws.send(data.toString());
console.error("Error: ", data.toString());
});