mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Reapply "Merge branch 'canary' into kucherenko/canary"
This reverts commit e6cb6454db.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
// },
|
||||
// },
|
||||
// );
|
||||
// };
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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),
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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: "",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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, {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
148
apps/dokploy/server/api/routers/organization.ts
Normal file
148
apps/dokploy/server/api/routers/organization.ts
Normal 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)],
|
||||
});
|
||||
}),
|
||||
});
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user