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

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

View File

@@ -1,108 +1,56 @@
import { randomBytes } from "node:crypto";
import { db } from "@dokploy/server/db";
import {
admins,
type apiCreateUserInvitation,
auth,
users,
invitation,
member,
organization,
users_temp,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
import { eq } from "drizzle-orm";
import { IS_CLOUD } from "../constants";
export type Admin = typeof admins.$inferSelect;
export const createInvitation = async (
input: typeof apiCreateUserInvitation._type,
adminId: string,
) => {
await db.transaction(async (tx) => {
const result = await tx
.insert(auth)
.values({
email: input.email.toLowerCase(),
rol: "user",
password: bcrypt.hashSync("01231203012312", 10),
})
.returning()
.then((res) => res[0]);
if (!result) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the user",
});
}
const expiresIn24Hours = new Date();
expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1);
const token = randomBytes(32).toString("hex");
await tx
.insert(users)
.values({
adminId: adminId,
authId: result.id,
token,
expirationDate: expiresIn24Hours.toISOString(),
})
.returning();
export const findUserById = async (userId: string) => {
const user = await db.query.users_temp.findFirst({
where: eq(users_temp.id, userId),
// with: {
// account: true,
// },
});
};
export const findAdminById = async (adminId: string) => {
const admin = await db.query.admins.findFirst({
where: eq(admins.adminId, adminId),
});
if (!admin) {
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Admin not found",
message: "User not found",
});
}
return admin;
return user;
};
export const updateAdmin = async (
authId: string,
adminData: Partial<Admin>,
) => {
const admin = await db
.update(admins)
.set({
...adminData,
})
.where(eq(admins.authId, authId))
.returning()
.then((res) => res[0]);
return admin;
export const findOrganizationById = async (organizationId: string) => {
const organizationResult = await db.query.organization.findFirst({
where: eq(organization.id, organizationId),
});
return organizationResult;
};
export const isAdminPresent = async () => {
const admin = await db.query.admins.findFirst();
const admin = await db.query.member.findFirst({
where: eq(member.role, "owner"),
});
if (!admin) {
return false;
}
return true;
};
export const findAdminByAuthId = async (authId: string) => {
const admin = await db.query.admins.findFirst({
where: eq(admins.authId, authId),
export const findAdmin = async () => {
const admin = await db.query.member.findFirst({
where: eq(member.role, "owner"),
with: {
users: true,
user: true,
},
});
if (!admin) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Admin not found",
});
}
return admin;
};
export const findAdmin = async () => {
const admin = await db.query.admins.findFirst({});
if (!admin) {
throw new TRPCError({
code: "NOT_FOUND",
@@ -113,14 +61,15 @@ export const findAdmin = async () => {
};
export const getUserByToken = async (token: string) => {
const user = await db.query.users.findFirst({
where: eq(users.token, token),
with: {
auth: {
columns: {
password: false,
},
},
const user = await db.query.invitation.findFirst({
where: eq(invitation.id, token),
columns: {
id: true,
email: true,
status: true,
expiresAt: true,
role: true,
inviterId: true,
},
});
@@ -130,34 +79,23 @@ export const getUserByToken = async (token: string) => {
message: "Invitation not found",
});
}
const userAlreadyExists = await db.query.users_temp.findFirst({
where: eq(users_temp.email, user?.email || ""),
});
const { expiresAt, ...rest } = user;
return {
...user,
isExpired: user.isRegistered,
...rest,
isExpired: user.expiresAt < new Date(),
userAlreadyExists: !!userAlreadyExists,
};
};
export const removeUserByAuthId = async (authId: string) => {
export const removeUserById = async (userId: string) => {
await db
.delete(auth)
.where(eq(auth.id, authId))
.returning()
.then((res) => res[0]);
};
export const removeAdminByAuthId = async (authId: string) => {
const admin = await findAdminByAuthId(authId);
if (!admin) return null;
// First delete all associated users
const users = admin.users;
for (const user of users) {
await removeUserByAuthId(user.authId);
}
// Then delete the auth record which will cascade delete the admin
return await db
.delete(auth)
.where(eq(auth.id, authId))
.delete(users_temp)
.where(eq(users_temp.id, userId))
.returning()
.then((res) => res[0]);
};
@@ -168,8 +106,8 @@ export const getDokployUrl = async () => {
}
const admin = await findAdmin();
if (admin.host) {
return `https://${admin.host}`;
if (admin.user.host) {
return `https://${admin.user.host}`;
}
return `http://${admin.serverIp}:${process.env.PORT}`;
return `http://${admin.user.serverIp}:${process.env.PORT}`;
};

View File

@@ -4,9 +4,8 @@ import {
type apiCreateApplication,
applications,
buildAppName,
cleanAppName,
} from "@dokploy/server/db/schema";
import { getAdvancedStats } from "@dokploy/server/monitoring/utilts";
import { getAdvancedStats } from "@dokploy/server/monitoring/utils";
import {
buildApplication,
getBuildCommand,
@@ -28,7 +27,6 @@ import {
getCustomGitCloneCommand,
} from "@dokploy/server/utils/providers/git";
import {
authGithub,
cloneGithubRepository,
getGithubCloneCommand,
} from "@dokploy/server/utils/providers/github";
@@ -175,6 +173,7 @@ export const deployApplication = async ({
descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`;
const deployment = await createDeployment({
applicationId: applicationId,
@@ -183,6 +182,12 @@ export const deployApplication = async ({
});
try {
// const admin = await findUserById(application.project.userId);
// if (admin.cleanupCacheApplications) {
// await cleanupFullDocker(application?.serverId);
// }
if (application.sourceType === "github") {
await cloneGithubRepository({
...application,
@@ -212,7 +217,7 @@ export const deployApplication = async ({
applicationName: application.name,
applicationType: "application",
buildLink,
adminId: application.project.adminId,
organizationId: application.project.organizationId,
domains: application.domains,
});
} catch (error) {
@@ -225,7 +230,7 @@ export const deployApplication = async ({
// @ts-ignore
errorMessage: error?.message || "Error building",
buildLink,
adminId: application.project.adminId,
organizationId: application.project.organizationId,
});
throw error;
@@ -244,6 +249,7 @@ export const rebuildApplication = async ({
descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const deployment = await createDeployment({
applicationId: applicationId,
title: titleLog,
@@ -251,6 +257,11 @@ export const rebuildApplication = async ({
});
try {
// const admin = await findUserById(application.project.userId);
// if (admin.cleanupCacheApplications) {
// await cleanupFullDocker(application?.serverId);
// }
if (application.sourceType === "github") {
await buildApplication(application, deployment.logPath);
} else if (application.sourceType === "gitlab") {
@@ -285,6 +296,7 @@ export const deployRemoteApplication = async ({
descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`;
const deployment = await createDeployment({
applicationId: applicationId,
@@ -294,6 +306,11 @@ export const deployRemoteApplication = async ({
try {
if (application.serverId) {
// const admin = await findUserById(application.project.userId);
// if (admin.cleanupCacheApplications) {
// await cleanupFullDocker(application?.serverId);
// }
let command = "set -e;";
if (application.sourceType === "github") {
command += await getGithubCloneCommand({
@@ -332,7 +349,7 @@ export const deployRemoteApplication = async ({
applicationName: application.name,
applicationType: "application",
buildLink,
adminId: application.project.adminId,
organizationId: application.project.organizationId,
domains: application.domains,
});
} catch (error) {
@@ -356,17 +373,9 @@ export const deployRemoteApplication = async ({
// @ts-ignore
errorMessage: error?.message || "Error building",
buildLink,
adminId: application.project.adminId,
organizationId: application.project.organizationId,
});
console.log(
"Error on ",
application.buildType,
"/",
application.sourceType,
error,
);
throw error;
}
@@ -385,6 +394,7 @@ export const deployPreviewApplication = async ({
previewDeploymentId: string;
}) => {
const application = await findApplicationById(applicationId);
const deployment = await createDeploymentPreview({
title: titleLog,
description: descriptionLog,
@@ -438,9 +448,15 @@ export const deployPreviewApplication = async ({
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
});
application.appName = previewDeployment.appName;
application.env = application.previewEnv;
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
application.buildArgs = application.previewBuildArgs;
// const admin = await findUserById(application.project.userId);
// if (admin.cleanupCacheOnPreviews) {
// await cleanupFullDocker(application?.serverId);
// }
if (application.sourceType === "github") {
await cloneGithubRepository({
...application,
@@ -450,7 +466,6 @@ export const deployPreviewApplication = async ({
});
await buildApplication(application, deployment.logPath);
}
// 4eef09efc46009187d668cf1c25f768d0bde4f91
const successComment = getIssueComment(
application.name,
"success",
@@ -492,6 +507,7 @@ export const deployRemotePreviewApplication = async ({
previewDeploymentId: string;
}) => {
const application = await findApplicationById(applicationId);
const deployment = await createDeploymentPreview({
title: titleLog,
description: descriptionLog,
@@ -545,14 +561,21 @@ export const deployRemotePreviewApplication = async ({
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
});
application.appName = previewDeployment.appName;
application.env = application.previewEnv;
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
application.buildArgs = application.previewBuildArgs;
if (application.serverId) {
// const admin = await findUserById(application.project.userId);
// if (admin.cleanupCacheOnPreviews) {
// await cleanupFullDocker(application?.serverId);
// }
let command = "set -e;";
if (application.sourceType === "github") {
command += await getGithubCloneCommand({
...application,
appName: previewDeployment.appName,
branch: previewDeployment.branch,
serverId: application.serverId,
logPath: deployment.logPath,
});
@@ -602,6 +625,7 @@ export const rebuildRemoteApplication = async ({
descriptionLog: string;
}) => {
const application = await findApplicationById(applicationId);
const deployment = await createDeployment({
applicationId: applicationId,
title: titleLog,
@@ -610,6 +634,11 @@ export const rebuildRemoteApplication = async ({
try {
if (application.serverId) {
// const admin = await findUserById(application.project.userId);
// if (admin.cleanupCacheApplications) {
// await cleanupFullDocker(application?.serverId);
// }
if (application.sourceType !== "docker") {
let command = "set -e;";
command += getBuildCommand(application, deployment.logPath);

View File

@@ -1,184 +0,0 @@
import { randomBytes } from "node:crypto";
import { db } from "@dokploy/server/db";
import {
admins,
type apiCreateAdmin,
type apiCreateUser,
auth,
users,
} from "@dokploy/server/db/schema";
import { getPublicIpWithFallback } from "@dokploy/server/wss/utils";
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
import { eq } from "drizzle-orm";
import encode from "hi-base32";
import { TOTP } from "otpauth";
import QRCode from "qrcode";
import { IS_CLOUD } from "../constants";
export type Auth = typeof auth.$inferSelect;
export const createAdmin = async (input: typeof apiCreateAdmin._type) => {
return await db.transaction(async (tx) => {
const hashedPassword = bcrypt.hashSync(input.password, 10);
const newAuth = await tx
.insert(auth)
.values({
email: input.email.toLowerCase(),
password: hashedPassword,
rol: "admin",
})
.returning()
.then((res) => res[0]);
if (!newAuth) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the user",
});
}
await tx
.insert(admins)
.values({
authId: newAuth.id,
...(!IS_CLOUD && {
serverIp:
process.env.ADVERTISE_ADDR || (await getPublicIpWithFallback()),
}),
})
.returning();
return newAuth;
});
};
export const createUser = async (input: typeof apiCreateUser._type) => {
return await db.transaction(async (tx) => {
const hashedPassword = bcrypt.hashSync(input.password, 10);
const res = await tx
.update(auth)
.set({
password: hashedPassword,
})
.where(eq(auth.id, input.id))
.returning()
.then((res) => res[0]);
if (!res) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the user",
});
}
const user = await tx
.update(users)
.set({
isRegistered: true,
expirationDate: undefined,
})
.where(eq(users.token, input.token))
.returning()
.then((res) => res[0]);
return user;
});
};
export const findAuthByEmail = async (email: string) => {
const result = await db.query.auth.findFirst({
where: eq(auth.email, email),
});
if (!result) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
return result;
};
export const findAuthById = async (authId: string) => {
const result = await db.query.auth.findFirst({
where: eq(auth.id, authId),
columns: {
password: false,
},
});
if (!result) {
throw new TRPCError({
code: "NOT_FOUND",
message: "Auth not found",
});
}
return result;
};
export const updateAuthById = async (
authId: string,
authData: Partial<Auth>,
) => {
const result = await db
.update(auth)
.set({
...authData,
})
.where(eq(auth.id, authId))
.returning();
return result[0];
};
export const generate2FASecret = async (authId: string) => {
const auth = await findAuthById(authId);
const base32_secret = generateBase32Secret();
const totp = new TOTP({
issuer: "Dokploy",
label: `${auth?.email}`,
algorithm: "SHA1",
digits: 6,
secret: base32_secret,
});
const otpauth_url = totp.toString();
const qrUrl = await QRCode.toDataURL(otpauth_url);
return {
qrCodeUrl: qrUrl,
secret: base32_secret,
};
};
export const verify2FA = async (
auth: Omit<Auth, "password">,
secret: string,
pin: string,
) => {
const totp = new TOTP({
issuer: "Dokploy",
label: `${auth?.email}`,
algorithm: "SHA1",
digits: 6,
secret: secret,
});
const delta = totp.validate({ token: pin });
if (delta === null) {
throw new TRPCError({
code: "BAD_REQUEST",
message: "Invalid 2FA code",
});
}
return auth;
};
const generateBase32Secret = () => {
const buffer = randomBytes(15);
const base32 = encode.encode(buffer).replace(/=/g, "").substring(0, 24);
return base32;
};

View File

@@ -6,7 +6,7 @@ import { eq } from "drizzle-orm";
export type Backup = typeof backups.$inferSelect;
export type BackupSchedule = Awaited<ReturnType<typeof findBackupById>>;
export type BackupScheduleList = Awaited<ReturnType<typeof findBackupsByDbId>>;
export const createBackup = async (input: typeof apiCreateBackup._type) => {
const newBackup = await db
.insert(backups)
@@ -69,3 +69,20 @@ export const removeBackupById = async (backupId: string) => {
return result[0];
};
export const findBackupsByDbId = async (
id: string,
type: "postgres" | "mysql" | "mariadb" | "mongo",
) => {
const result = await db.query.backups.findMany({
where: eq(backups[`${type}Id`], id),
with: {
postgres: true,
mysql: true,
mariadb: true,
mongo: true,
destination: true,
},
});
return result || [];
};

View File

@@ -12,14 +12,14 @@ export type Bitbucket = typeof bitbucket.$inferSelect;
export const createBitbucket = async (
input: typeof apiCreateBitbucket._type,
adminId: string,
organizationId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
.insert(gitProvider)
.values({
providerType: "bitbucket",
adminId: adminId,
organizationId: organizationId,
name: input.name,
})
.returning()
@@ -74,12 +74,12 @@ export const updateBitbucket = async (
.where(eq(bitbucket.bitbucketId, bitbucketId))
.returning();
if (input.name || input.adminId) {
if (input.name || input.organizationId) {
await tx
.update(gitProvider)
.set({
name: input.name,
adminId: input.adminId,
organizationId: input.organizationId,
})
.where(eq(gitProvider.gitProviderId, input.gitProviderId))
.returning();

View File

@@ -33,13 +33,13 @@ export const findCertificateById = async (certificateId: string) => {
export const createCertificate = async (
certificateData: z.infer<typeof apiCreateCertificate>,
adminId: string,
organizationId: string,
) => {
const certificate = await db
.insert(certificates)
.values({
...certificateData,
adminId: adminId,
organizationId: organizationId,
})
.returning();

View File

@@ -3,7 +3,6 @@ import { paths } from "@dokploy/server/constants";
import { db } from "@dokploy/server/db";
import { type apiCreateCompose, compose } from "@dokploy/server/db/schema";
import { buildAppName, cleanAppName } from "@dokploy/server/db/schema";
import { generatePassword } from "@dokploy/server/templates/utils";
import {
buildCompose,
getBuildComposeCommand,
@@ -206,6 +205,7 @@ export const deployCompose = async ({
descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const buildLink = `${await getDokployUrl()}/dashboard/project/${
compose.projectId
}/services/compose/${compose.composeId}?tab=deployments`;
@@ -216,6 +216,10 @@ export const deployCompose = async ({
});
try {
// const admin = await findUserById(compose.project.userId);
// if (admin.cleanupCacheOnCompose) {
// await cleanupFullDocker(compose?.serverId);
// }
if (compose.sourceType === "github") {
await cloneGithubRepository({
...compose,
@@ -242,7 +246,7 @@ export const deployCompose = async ({
applicationName: compose.name,
applicationType: "compose",
buildLink,
adminId: compose.project.adminId,
organizationId: compose.project.organizationId,
domains: compose.domains,
});
} catch (error) {
@@ -257,7 +261,7 @@ export const deployCompose = async ({
// @ts-ignore
errorMessage: error?.message || "Error building",
buildLink,
adminId: compose.project.adminId,
organizationId: compose.project.organizationId,
});
throw error;
}
@@ -273,6 +277,7 @@ export const rebuildCompose = async ({
descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,
@@ -280,6 +285,10 @@ export const rebuildCompose = async ({
});
try {
// const admin = await findUserById(compose.project.userId);
// if (admin.cleanupCacheOnCompose) {
// await cleanupFullDocker(compose?.serverId);
// }
if (compose.serverId) {
await getBuildComposeCommand(compose, deployment.logPath);
} else {
@@ -311,6 +320,7 @@ export const deployRemoteCompose = async ({
descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const buildLink = `${await getDokployUrl()}/dashboard/project/${
compose.projectId
}/services/compose/${compose.composeId}?tab=deployments`;
@@ -321,6 +331,10 @@ export const deployRemoteCompose = async ({
});
try {
if (compose.serverId) {
// const admin = await findUserById(compose.project.userId);
// if (admin.cleanupCacheOnCompose) {
// await cleanupFullDocker(compose?.serverId);
// }
let command = "set -e;";
if (compose.sourceType === "github") {
@@ -366,7 +380,7 @@ export const deployRemoteCompose = async ({
applicationName: compose.name,
applicationType: "compose",
buildLink,
adminId: compose.project.adminId,
organizationId: compose.project.organizationId,
domains: compose.domains,
});
} catch (error) {
@@ -391,7 +405,7 @@ export const deployRemoteCompose = async ({
// @ts-ignore
errorMessage: error?.message || "Error building",
buildLink,
adminId: compose.project.adminId,
organizationId: compose.project.organizationId,
});
throw error;
}
@@ -407,6 +421,7 @@ export const rebuildRemoteCompose = async ({
descriptionLog: string;
}) => {
const compose = await findComposeById(composeId);
const deployment = await createDeploymentCompose({
composeId: composeId,
title: titleLog,
@@ -414,6 +429,10 @@ export const rebuildRemoteCompose = async ({
});
try {
// const admin = await findUserById(compose.project.userId);
// if (admin.cleanupCacheOnCompose) {
// await cleanupFullDocker(compose?.serverId);
// }
if (compose.serverId) {
await getBuildComposeCommand(compose, deployment.logPath);
}
@@ -538,6 +557,17 @@ export const stopCompose = async (composeId: string) => {
}
}
if (compose.composeType === "stack") {
if (compose.serverId) {
await execAsyncRemote(
compose.serverId,
`docker stack rm ${compose.appName}`,
);
} else {
await execAsync(`docker stack rm ${compose.appName}`);
}
}
await updateCompose(composeId, {
composeStatus: "idle",
});

View File

@@ -12,7 +12,7 @@ import {
import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory";
import { TRPCError } from "@trpc/server";
import { format } from "date-fns";
import { and, desc, eq, isNull } from "drizzle-orm";
import { desc, eq } from "drizzle-orm";
import {
type Application,
findApplicationById,
@@ -98,6 +98,17 @@ export const createDeployment = async (
}
return deploymentCreate[0];
} catch (error) {
await db
.insert(deployments)
.values({
applicationId: deployment.applicationId,
title: deployment.title || "Deployment",
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
})
.returning();
await updateApplicationStatus(application.applicationId, "error");
console.log(error);
throw new TRPCError({
@@ -164,6 +175,17 @@ export const createDeploymentPreview = async (
}
return deploymentCreate[0];
} catch (error) {
await db
.insert(deployments)
.values({
previewDeploymentId: deployment.previewDeploymentId,
title: deployment.title || "Deployment",
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
})
.returning();
await updatePreviewDeployment(deployment.previewDeploymentId, {
previewStatus: "error",
});
@@ -226,6 +248,17 @@ echo "Initializing deployment" >> ${logFilePath};
}
return deploymentCreate[0];
} catch (error) {
await db
.insert(deployments)
.values({
composeId: deployment.composeId,
title: deployment.title || "Deployment",
status: "error",
logPath: "",
description: deployment.description || "",
errorMessage: `An error have occured: ${error instanceof Error ? error.message : error}`,
})
.returning();
await updateCompose(compose.composeId, {
composeStatus: "error",
});
@@ -245,9 +278,11 @@ export const removeDeployment = async (deploymentId: string) => {
.returning();
return deployment[0];
} catch (error) {
const message =
error instanceof Error ? error.message : "Error creating the deployment";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error deleting this deployment",
message,
});
}
};
@@ -502,9 +537,11 @@ export const createServerDeployment = async (
}
return deploymentCreate[0];
} catch (error) {
const message =
error instanceof Error ? error.message : "Error creating the deployment";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the deployment",
message,
});
}
};

View File

@@ -10,13 +10,13 @@ export type Destination = typeof destinations.$inferSelect;
export const createDestintation = async (
input: typeof apiCreateDestination._type,
adminId: string,
organizationId: string,
) => {
const newDestination = await db
.insert(destinations)
.values({
...input,
adminId: adminId,
organizationId: organizationId,
})
.returning()
.then((value) => value[0]);
@@ -46,14 +46,14 @@ export const findDestinationById = async (destinationId: string) => {
export const removeDestinationById = async (
destinationId: string,
adminId: string,
organizationId: string,
) => {
const result = await db
.delete(destinations)
.where(
and(
eq(destinations.destinationId, destinationId),
eq(destinations.adminId, adminId),
eq(destinations.organizationId, organizationId),
),
)
.returning();
@@ -73,7 +73,7 @@ export const updateDestinationById = async (
.where(
and(
eq(destinations.destinationId, destinationId),
eq(destinations.adminId, destinationData.adminId || ""),
eq(destinations.organizationId, destinationData.organizationId || ""),
),
)
.returning();

View File

@@ -58,7 +58,11 @@ export const getContainers = async (serverId?: string | null) => {
serverId,
};
})
.filter((container) => !container.name.includes("dokploy"));
.filter(
(container) =>
!container.name.includes("dokploy") ||
container.name.includes("dokploy-monitoring"),
);
return containers;
} catch (error) {
@@ -94,7 +98,7 @@ export const getConfig = async (
const config = JSON.parse(stdout);
return config;
} catch (error) {}
} catch (_error) {}
};
export const getContainersByAppNameMatch = async (
@@ -152,7 +156,7 @@ export const getContainersByAppNameMatch = async (
});
return containers || [];
} catch (error) {}
} catch (_error) {}
return [];
};
@@ -210,7 +214,7 @@ export const getStackContainersByAppName = async (
});
return containers || [];
} catch (error) {}
} catch (_error) {}
return [];
};
@@ -270,7 +274,7 @@ export const getServiceContainersByAppName = async (
});
return containers || [];
} catch (error) {}
} catch (_error) {}
return [];
};
@@ -321,7 +325,7 @@ export const getContainersByAppLabel = async (
});
return containers || [];
} catch (error) {}
} catch (_error) {}
return [];
};
@@ -340,7 +344,7 @@ export const containerRestart = async (containerId: string) => {
const config = JSON.parse(stdout);
return config;
} catch (error) {}
} catch (_error) {}
};
export const getSwarmNodes = async (serverId?: string) => {
@@ -369,7 +373,7 @@ export const getSwarmNodes = async (serverId?: string) => {
.split("\n")
.map((line) => JSON.parse(line));
return nodesArray;
} catch (error) {}
} catch (_error) {}
};
export const getNodeInfo = async (nodeId: string, serverId?: string) => {
@@ -395,7 +399,7 @@ export const getNodeInfo = async (nodeId: string, serverId?: string) => {
const nodeInfo = JSON.parse(stdout);
return nodeInfo;
} catch (error) {}
} catch (_error) {}
};
export const getNodeApplications = async (serverId?: string) => {
@@ -427,7 +431,7 @@ export const getNodeApplications = async (serverId?: string) => {
.filter((service) => !service.Name.startsWith("dokploy-"));
return appArray;
} catch (error) {}
} catch (_error) {}
};
export const getApplicationInfo = async (
@@ -460,5 +464,5 @@ export const getApplicationInfo = async (
.map((line) => JSON.parse(line));
return appArray;
} catch (error) {}
} catch (_error) {}
};

View File

@@ -4,7 +4,7 @@ import { manageDomain } from "@dokploy/server/utils/traefik/domain";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { type apiCreateDomain, domains } from "../db/schema";
import { findAdmin, findAdminById } from "./admin";
import { findUserById } from "./admin";
import { findApplicationById } from "./application";
import { findServerById } from "./server";
@@ -40,7 +40,7 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => {
export const generateTraefikMeDomain = async (
appName: string,
adminId: string,
userId: string,
serverId?: string,
) => {
if (serverId) {
@@ -57,7 +57,7 @@ export const generateTraefikMeDomain = async (
projectName: appName,
});
}
const admin = await findAdminById(adminId);
const admin = await findUserById(userId);
return generateRandomDomain({
serverIp: admin?.serverIp || "",
projectName: appName,
@@ -126,7 +126,6 @@ export const updateDomainById = async (
export const removeDomainById = async (domainId: string) => {
await findDomainById(domainId);
// TODO: fix order
const result = await db
.delete(domains)
.where(eq(domains.domainId, domainId))

View File

@@ -12,14 +12,14 @@ import { updatePreviewDeployment } from "./preview-deployment";
export type Github = typeof github.$inferSelect;
export const createGithub = async (
input: typeof apiCreateGithub._type,
adminId: string,
organizationId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
.insert(gitProvider)
.values({
providerType: "github",
adminId: adminId,
organizationId: organizationId,
name: input.name,
})
.returning()
@@ -119,7 +119,7 @@ export const issueCommentExists = async ({
comment_id: comment_id,
});
return true;
} catch (error) {
} catch (_error) {
return false;
}
};

View File

@@ -1,9 +1,7 @@
import { db } from "@dokploy/server/db";
import {
type apiCreateGitlab,
type bitbucket,
gitProvider,
type github,
gitlab,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
@@ -13,14 +11,14 @@ export type Gitlab = typeof gitlab.$inferSelect;
export const createGitlab = async (
input: typeof apiCreateGitlab._type,
adminId: string,
organizationId: string,
) => {
return await db.transaction(async (tx) => {
const newGitProvider = await tx
.insert(gitProvider)
.values({
providerType: "gitlab",
adminId: adminId,
organizationId: organizationId,
name: input.name,
})
.returning()

View File

@@ -4,7 +4,7 @@ import {
backups,
mariadb,
} from "@dokploy/server/db/schema";
import { buildAppName, cleanAppName } from "@dokploy/server/db/schema";
import { buildAppName } from "@dokploy/server/db/schema";
import { generatePassword } from "@dokploy/server/templates/utils";
import { buildMariadb } from "@dokploy/server/utils/databases/mariadb";
import { pullImage } from "@dokploy/server/utils/docker/utils";

View File

@@ -1,6 +1,6 @@
import { db } from "@dokploy/server/db";
import { type apiCreateMongo, backups, mongo } from "@dokploy/server/db/schema";
import { buildAppName, cleanAppName } from "@dokploy/server/db/schema";
import { buildAppName } from "@dokploy/server/db/schema";
import { generatePassword } from "@dokploy/server/templates/utils";
import { buildMongo } from "@dokploy/server/utils/databases/mongo";
import { pullImage } from "@dokploy/server/utils/docker/utils";

View File

@@ -64,7 +64,7 @@ export const createMount = async (input: typeof apiCreateMount._type) => {
console.log(error);
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the mount",
message: `Error ${error instanceof Error ? error.message : error}`,
cause: error,
});
}
@@ -91,7 +91,7 @@ export const createFileMount = async (mountId: string) => {
console.log(`Error creating the file mount: ${error}`);
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error creating the mount",
message: `Error creating the mount ${error instanceof Error ? error.message : error}`,
cause: error,
});
}
@@ -123,8 +123,8 @@ export const updateMount = async (
mountId: string,
mountData: Partial<Mount>,
) => {
return await db.transaction(async (transaction) => {
const mount = await db
return await db.transaction(async (tx) => {
const mount = await tx
.update(mounts)
.set({
...mountData,
@@ -211,7 +211,7 @@ export const deleteFileMount = async (mountId: string) => {
} else {
await removeFileOrDirectory(fullPath);
}
} catch (error) {}
} catch (_error) {}
};
export const getBaseFilesPath = async (mountId: string) => {

View File

@@ -24,7 +24,7 @@ export type Notification = typeof notifications.$inferSelect;
export const createSlackNotification = async (
input: typeof apiCreateSlack._type,
adminId: string,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newSlack = await tx
@@ -54,7 +54,8 @@ export const createSlackNotification = async (
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "slack",
adminId: adminId,
organizationId: organizationId,
serverThreshold: input.serverThreshold,
})
.returning()
.then((value) => value[0]);
@@ -83,7 +84,8 @@ export const updateSlackNotification = async (
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
adminId: input.adminId,
organizationId: input.organizationId,
serverThreshold: input.serverThreshold,
})
.where(eq(notifications.notificationId, input.notificationId))
.returning()
@@ -112,7 +114,7 @@ export const updateSlackNotification = async (
export const createTelegramNotification = async (
input: typeof apiCreateTelegram._type,
adminId: string,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newTelegram = await tx
@@ -120,6 +122,7 @@ export const createTelegramNotification = async (
.values({
botToken: input.botToken,
chatId: input.chatId,
messageThreadId: input.messageThreadId,
})
.returning()
.then((value) => value[0]);
@@ -142,7 +145,8 @@ export const createTelegramNotification = async (
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "telegram",
adminId: adminId,
organizationId: organizationId,
serverThreshold: input.serverThreshold,
})
.returning()
.then((value) => value[0]);
@@ -171,7 +175,8 @@ export const updateTelegramNotification = async (
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
adminId: input.adminId,
organizationId: input.organizationId,
serverThreshold: input.serverThreshold,
})
.where(eq(notifications.notificationId, input.notificationId))
.returning()
@@ -189,6 +194,7 @@ export const updateTelegramNotification = async (
.set({
botToken: input.botToken,
chatId: input.chatId,
messageThreadId: input.messageThreadId,
})
.where(eq(telegram.telegramId, input.telegramId))
.returning()
@@ -200,7 +206,7 @@ export const updateTelegramNotification = async (
export const createDiscordNotification = async (
input: typeof apiCreateDiscord._type,
adminId: string,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newDiscord = await tx
@@ -230,7 +236,8 @@ export const createDiscordNotification = async (
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "discord",
adminId: adminId,
organizationId: organizationId,
serverThreshold: input.serverThreshold,
})
.returning()
.then((value) => value[0]);
@@ -259,7 +266,8 @@ export const updateDiscordNotification = async (
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
adminId: input.adminId,
organizationId: input.organizationId,
serverThreshold: input.serverThreshold,
})
.where(eq(notifications.notificationId, input.notificationId))
.returning()
@@ -288,7 +296,7 @@ export const updateDiscordNotification = async (
export const createEmailNotification = async (
input: typeof apiCreateEmail._type,
adminId: string,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newEmail = await tx
@@ -322,7 +330,8 @@ export const createEmailNotification = async (
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "email",
adminId: adminId,
organizationId: organizationId,
serverThreshold: input.serverThreshold,
})
.returning()
.then((value) => value[0]);
@@ -351,7 +360,8 @@ export const updateEmailNotification = async (
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
adminId: input.adminId,
organizationId: input.organizationId,
serverThreshold: input.serverThreshold,
})
.where(eq(notifications.notificationId, input.notificationId))
.returning()
@@ -384,7 +394,7 @@ export const updateEmailNotification = async (
export const createGotifyNotification = async (
input: typeof apiCreateGotify._type,
adminId: string,
organizationId: string,
) => {
await db.transaction(async (tx) => {
const newGotify = await tx
@@ -416,7 +426,7 @@ export const createGotifyNotification = async (
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
notificationType: "gotify",
adminId: adminId,
organizationId: organizationId,
})
.returning()
.then((value) => value[0]);
@@ -445,7 +455,7 @@ export const updateGotifyNotification = async (
databaseBackup: input.databaseBackup,
dokployRestart: input.dokployRestart,
dockerCleanup: input.dockerCleanup,
adminId: input.adminId,
organizationId: input.organizationId,
})
.where(eq(notifications.notificationId, input.notificationId))
.returning()

View File

@@ -4,7 +4,7 @@ import {
backups,
postgres,
} from "@dokploy/server/db/schema";
import { buildAppName, cleanAppName } from "@dokploy/server/db/schema";
import { buildAppName } from "@dokploy/server/db/schema";
import { generatePassword } from "@dokploy/server/templates/utils";
import { buildPostgres } from "@dokploy/server/utils/databases/postgres";
import { pullImage } from "@dokploy/server/utils/docker/utils";

View File

@@ -2,23 +2,20 @@ import { db } from "@dokploy/server/db";
import {
type apiCreatePreviewDeployment,
deployments,
organization,
previewDeployments,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { and, desc, eq } from "drizzle-orm";
import { slugify } from "../setup/server-setup";
import { generatePassword, generateRandomDomain } from "../templates/utils";
import { generatePassword } from "../templates/utils";
import { removeService } from "../utils/docker/utils";
import { removeDirectoryCode } from "../utils/filesystem/directory";
import { authGithub } from "../utils/providers/github";
import { removeTraefikConfig } from "../utils/traefik/application";
import { manageDomain } from "../utils/traefik/domain";
import { findAdminById } from "./admin";
import { findUserById } from "./admin";
import { findApplicationById } from "./application";
import {
removeDeployments,
removeDeploymentsByPreviewDeploymentId,
} from "./deployment";
import { removeDeploymentsByPreviewDeploymentId } from "./deployment";
import { createDomain } from "./domain";
import { type Github, getIssueComment } from "./github";
@@ -106,13 +103,17 @@ export const removePreviewDeployment = async (previewDeploymentId: string) => {
for (const operation of cleanupOperations) {
try {
await operation();
} catch (error) {}
} catch (_error) {}
}
return deployment[0];
} catch (error) {
const message =
error instanceof Error
? error.message
: "Error deleting this preview deployment";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error deleting this preview deployment",
message,
});
}
};
@@ -154,11 +155,14 @@ export const createPreviewDeployment = async (
const application = await findApplicationById(schema.applicationId);
const appName = `preview-${application.appName}-${generatePassword(6)}`;
const org = await db.query.organization.findFirst({
where: eq(organization.id, application.project.organizationId),
});
const generateDomain = await generateWildcardDomain(
application.previewWildcard || "*.traefik.me",
appName,
application.server?.ipAddress || "",
application.project.adminId,
org?.ownerId || "",
);
const octokit = authGithub(application?.github as Github);
@@ -250,7 +254,7 @@ const generateWildcardDomain = async (
baseDomain: string,
appName: string,
serverIp: string,
adminId: string,
userId: string,
): Promise<string> => {
if (!baseDomain.startsWith("*.")) {
throw new Error('The base domain must start with "*."');
@@ -268,7 +272,7 @@ const generateWildcardDomain = async (
}
if (!ip) {
const admin = await findAdminById(adminId);
const admin = await findUserById(userId);
ip = admin?.serverIp || "";
}

View File

@@ -16,13 +16,13 @@ export type Project = typeof projects.$inferSelect;
export const createProject = async (
input: typeof apiCreateProject._type,
adminId: string,
organizationId: string,
) => {
const newProject = await db
.insert(projects)
.values({
...input,
adminId: adminId,
organizationId: organizationId,
})
.returning()
.then((value) => value[0]);

View File

@@ -6,7 +6,7 @@ import {
updateRedirectMiddleware,
} from "@dokploy/server/utils/traefik/redirect";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
import { eq } from "drizzle-orm";
import type { z } from "zod";
import { findApplicationById } from "./application";
export type Redirect = typeof redirects.$inferSelect;
@@ -114,9 +114,11 @@ export const updateRedirectById = async (
return redirect;
} catch (error) {
const message =
error instanceof Error ? error.message : "Error updating this redirect";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error updating this redirect",
message,
});
}
};

View File

@@ -1,6 +1,6 @@
import { db } from "@dokploy/server/db";
import { type apiCreateRedis, redis } from "@dokploy/server/db/schema";
import { buildAppName, cleanAppName } from "@dokploy/server/db/schema";
import { buildAppName } from "@dokploy/server/db/schema";
import { generatePassword } from "@dokploy/server/templates/utils";
import { buildRedis } from "@dokploy/server/utils/databases/redis";
import { pullImage } from "@dokploy/server/utils/docker/utils";

View File

@@ -12,14 +12,14 @@ export type Registry = typeof registry.$inferSelect;
export const createRegistry = async (
input: typeof apiCreateRegistry._type,
adminId: string,
organizationId: string,
) => {
return await db.transaction(async (tx) => {
const newRegistry = await tx
.insert(registry)
.values({
...input,
adminId: adminId,
organizationId: organizationId,
})
.returning()
.then((value) => value[0]);
@@ -112,9 +112,11 @@ export const updateRegistry = async (
return response;
} catch (error) {
const message =
error instanceof Error ? error.message : "Error updating this registry";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error updating this registry",
message,
});
}
};
@@ -135,9 +137,11 @@ export const findRegistryById = async (registryId: string) => {
return registryResponse;
};
export const findAllRegistryByAdminId = async (adminId: string) => {
export const findAllRegistryByOrganizationId = async (
organizationId: string,
) => {
const registryResponse = await db.query.registry.findMany({
where: eq(registry.adminId, adminId),
where: eq(registry.organizationId, organizationId),
});
return registryResponse;
};

View File

@@ -76,9 +76,11 @@ export const deleteSecurityById = async (securityId: string) => {
await removeSecurityMiddleware(application, result);
return result;
} catch (error) {
const message =
error instanceof Error ? error.message : "Error removing this security";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error removing this security",
message,
});
}
};
@@ -98,9 +100,11 @@ export const updateSecurityById = async (
return response[0];
} catch (error) {
const message =
error instanceof Error ? error.message : "Error updating this security";
throw new TRPCError({
code: "BAD_REQUEST",
message: "Error updating this security",
message,
});
}
};

View File

@@ -1,19 +1,24 @@
import { db } from "@dokploy/server/db";
import { type apiCreateServer, server } from "@dokploy/server/db/schema";
import {
type apiCreateServer,
organization,
server,
} from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { desc, eq } from "drizzle-orm";
import { eq } from "drizzle-orm";
export type Server = typeof server.$inferSelect;
export const createServer = async (
input: typeof apiCreateServer._type,
adminId: string,
organizationId: string,
) => {
const newServer = await db
.insert(server)
.values({
...input,
adminId: adminId,
organizationId: organizationId,
createdAt: new Date().toISOString(),
})
.returning()
.then((value) => value[0]);
@@ -45,12 +50,16 @@ export const findServerById = async (serverId: string) => {
return currentServer;
};
export const findServersByAdminId = async (adminId: string) => {
const servers = await db.query.server.findMany({
where: eq(server.adminId, adminId),
orderBy: desc(server.createdAt),
export const findServersByUserId = async (userId: string) => {
const orgs = await db.query.organization.findMany({
where: eq(organization.ownerId, userId),
with: {
servers: true,
},
});
const servers = orgs.flatMap((org) => org.servers);
return servers;
};

View File

@@ -5,7 +5,6 @@ import {
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
// import packageInfo from "../../../package.json";
export interface IUpdateData {
latestVersion: string | null;
@@ -170,7 +169,6 @@ echo "$json_output"
const result = JSON.parse(stdout);
return result;
}
const items = readdirSync(dirPath, { withFileTypes: true });
const stack = [dirPath];
const result: TreeDataItem[] = [];
@@ -213,3 +211,35 @@ echo "$json_output"
}
return result;
};
export const cleanupFullDocker = async (serverId?: string | null) => {
const cleanupImages = "docker image prune --force";
const cleanupVolumes = "docker volume prune --force";
const cleanupContainers = "docker container prune --force";
const cleanupSystem = "docker system prune --force --volumes";
const cleanupBuilder = "docker builder prune --force";
try {
if (serverId) {
await execAsyncRemote(
serverId,
`
${cleanupImages}
${cleanupVolumes}
${cleanupContainers}
${cleanupSystem}
${cleanupBuilder}
`,
);
}
await execAsync(`
${cleanupImages}
${cleanupVolumes}
${cleanupContainers}
${cleanupSystem}
${cleanupBuilder}
`);
} catch (error) {
console.log(error);
}
};

View File

@@ -1,80 +1,53 @@
import { db } from "@dokploy/server/db";
import { users } from "@dokploy/server/db/schema";
import { apikey, member, users_temp } from "@dokploy/server/db/schema";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { and, eq } from "drizzle-orm";
import { auth } from "../lib/auth";
export type User = typeof users.$inferSelect;
export type User = typeof users_temp.$inferSelect;
export const findUserById = async (userId: string) => {
const user = await db.query.users.findFirst({
where: eq(users.userId, userId),
});
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
return user;
};
export const findUserByAuthId = async (authId: string) => {
const user = await db.query.users.findFirst({
where: eq(users.authId, authId),
with: {
auth: true,
},
});
if (!user) {
throw new TRPCError({
code: "NOT_FOUND",
message: "User not found",
});
}
return user;
};
export const findUsers = async (adminId: string) => {
const currentUsers = await db.query.users.findMany({
where: eq(users.adminId, adminId),
with: {
auth: {
columns: {
secret: false,
},
},
},
});
return currentUsers;
};
export const addNewProject = async (authId: string, projectId: string) => {
const user = await findUserByAuthId(authId);
export const addNewProject = async (
userId: string,
projectId: string,
organizationId: string,
) => {
const userR = await findMemberById(userId, organizationId);
await db
.update(users)
.update(member)
.set({
accessedProjects: [...user.accessedProjects, projectId],
accessedProjects: [...userR.accessedProjects, projectId],
})
.where(eq(users.authId, authId));
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
};
export const addNewService = async (authId: string, serviceId: string) => {
const user = await findUserByAuthId(authId);
export const addNewService = async (
userId: string,
serviceId: string,
organizationId: string,
) => {
const userR = await findMemberById(userId, organizationId);
await db
.update(users)
.update(member)
.set({
accessedServices: [...user.accessedServices, serviceId],
accessedServices: [...userR.accessedServices, serviceId],
})
.where(eq(users.authId, authId));
.where(
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
);
};
export const canPerformCreationService = async (
userId: string,
projectId: string,
organizationId: string,
) => {
const { accessedProjects, canCreateServices } =
await findUserByAuthId(userId);
const { accessedProjects, canCreateServices } = await findMemberById(
userId,
organizationId,
);
const haveAccessToProject = accessedProjects.includes(projectId);
if (canCreateServices && haveAccessToProject) {
@@ -87,8 +60,9 @@ export const canPerformCreationService = async (
export const canPerformAccessService = async (
userId: string,
serviceId: string,
organizationId: string,
) => {
const { accessedServices } = await findUserByAuthId(userId);
const { accessedServices } = await findMemberById(userId, organizationId);
const haveAccessToService = accessedServices.includes(serviceId);
if (haveAccessToService) {
@@ -99,11 +73,14 @@ export const canPerformAccessService = async (
};
export const canPeformDeleteService = async (
authId: string,
userId: string,
serviceId: string,
organizationId: string,
) => {
const { accessedServices, canDeleteServices } =
await findUserByAuthId(authId);
const { accessedServices, canDeleteServices } = await findMemberById(
userId,
organizationId,
);
const haveAccessToService = accessedServices.includes(serviceId);
if (canDeleteServices && haveAccessToService) {
@@ -113,8 +90,11 @@ export const canPeformDeleteService = async (
return false;
};
export const canPerformCreationProject = async (authId: string) => {
const { canCreateProjects } = await findUserByAuthId(authId);
export const canPerformCreationProject = async (
userId: string,
organizationId: string,
) => {
const { canCreateProjects } = await findMemberById(userId, organizationId);
if (canCreateProjects) {
return true;
@@ -123,8 +103,11 @@ export const canPerformCreationProject = async (authId: string) => {
return false;
};
export const canPerformDeleteProject = async (authId: string) => {
const { canDeleteProjects } = await findUserByAuthId(authId);
export const canPerformDeleteProject = async (
userId: string,
organizationId: string,
) => {
const { canDeleteProjects } = await findMemberById(userId, organizationId);
if (canDeleteProjects) {
return true;
@@ -134,10 +117,11 @@ export const canPerformDeleteProject = async (authId: string) => {
};
export const canPerformAccessProject = async (
authId: string,
userId: string,
projectId: string,
organizationId: string,
) => {
const { accessedProjects } = await findUserByAuthId(authId);
const { accessedProjects } = await findMemberById(userId, organizationId);
const haveAccessToProject = accessedProjects.includes(projectId);
@@ -147,26 +131,45 @@ export const canPerformAccessProject = async (
return false;
};
export const canAccessToTraefikFiles = async (authId: string) => {
const { canAccessToTraefikFiles } = await findUserByAuthId(authId);
export const canAccessToTraefikFiles = async (
userId: string,
organizationId: string,
) => {
const { canAccessToTraefikFiles } = await findMemberById(
userId,
organizationId,
);
return canAccessToTraefikFiles;
};
export const checkServiceAccess = async (
authId: string,
userId: string,
serviceId: string,
organizationId: string,
action = "access" as "access" | "create" | "delete",
) => {
let hasPermission = false;
switch (action) {
case "create":
hasPermission = await canPerformCreationService(authId, serviceId);
hasPermission = await canPerformCreationService(
userId,
serviceId,
organizationId,
);
break;
case "access":
hasPermission = await canPerformAccessService(authId, serviceId);
hasPermission = await canPerformAccessService(
userId,
serviceId,
organizationId,
);
break;
case "delete":
hasPermission = await canPeformDeleteService(authId, serviceId);
hasPermission = await canPeformDeleteService(
userId,
serviceId,
organizationId,
);
break;
default:
hasPermission = false;
@@ -182,6 +185,7 @@ export const checkServiceAccess = async (
export const checkProjectAccess = async (
authId: string,
action: "create" | "delete" | "access",
organizationId: string,
projectId?: string,
) => {
let hasPermission = false;
@@ -190,13 +194,14 @@ export const checkProjectAccess = async (
hasPermission = await canPerformAccessProject(
authId,
projectId as string,
organizationId,
);
break;
case "create":
hasPermission = await canPerformCreationProject(authId);
hasPermission = await canPerformCreationProject(authId, organizationId);
break;
case "delete":
hasPermission = await canPerformDeleteProject(authId);
hasPermission = await canPerformDeleteProject(authId, organizationId);
break;
default:
hasPermission = false;
@@ -208,3 +213,82 @@ export const checkProjectAccess = async (
});
}
};
export const findMemberById = async (
userId: string,
organizationId: string,
) => {
const result = await db.query.member.findFirst({
where: and(
eq(member.userId, userId),
eq(member.organizationId, organizationId),
),
with: {
user: true,
},
});
if (!result) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Permission denied",
});
}
return result;
};
export const updateUser = async (userId: string, userData: Partial<User>) => {
const user = await db
.update(users_temp)
.set({
...userData,
})
.where(eq(users_temp.id, userId))
.returning()
.then((res) => res[0]);
return user;
};
export const createApiKey = async (
userId: string,
input: {
name: string;
prefix?: string;
expiresIn?: number;
metadata: {
organizationId: string;
};
rateLimitEnabled?: boolean;
rateLimitTimeWindow?: number;
rateLimitMax?: number;
remaining?: number;
refillAmount?: number;
refillInterval?: number;
},
) => {
const apiKey = await auth.createApiKey({
body: {
name: input.name,
expiresIn: input.expiresIn,
prefix: input.prefix,
rateLimitEnabled: input.rateLimitEnabled,
rateLimitTimeWindow: input.rateLimitTimeWindow,
rateLimitMax: input.rateLimitMax,
remaining: input.remaining,
refillAmount: input.refillAmount,
refillInterval: input.refillInterval,
userId,
},
});
if (input.metadata) {
await db
.update(apikey)
.set({
metadata: JSON.stringify(input.metadata),
})
.where(eq(apikey.id, apiKey.id));
}
return apiKey;
};