refactor: remove old references

This commit is contained in:
Mauricio Siu
2025-02-16 13:55:27 -06:00
parent a8d1471b16
commit 9856502ece
30 changed files with 250 additions and 819 deletions

View File

@@ -45,7 +45,7 @@ const baseApp: ApplicationNested = {
previewWildcard: "",
project: {
env: "",
adminId: "",
organizationId: "",
name: "",
description: "",
createdAt: "",

View File

@@ -5,7 +5,7 @@ vi.mock("node:fs", () => ({
default: fs,
}));
import type { Admin, FileConfig } from "@dokploy/server";
import type { Admin, FileConfig, User } from "@dokploy/server";
import {
createDefaultServerTraefikConfig,
loadOrCreateConfig,
@@ -13,7 +13,7 @@ import {
} from "@dokploy/server";
import { beforeEach, expect, test, vi } from "vitest";
const baseAdmin: Admin = {
const baseAdmin: Partial<User> = {
enablePaidFeatures: false,
metricsConfig: {
containers: {
@@ -40,9 +40,7 @@ const baseAdmin: Admin = {
cleanupCacheApplications: false,
cleanupCacheOnCompose: false,
cleanupCacheOnPreviews: false,
createdAt: "",
authId: "",
adminId: "string",
createdAt: new Date(),
serverIp: null,
certificateType: "none",
host: null,

View File

@@ -1,6 +1,6 @@
import { buffer } from "node:stream/consumers";
import { db } from "@/server/db";
import { admins, server, users_temp } from "@/server/db/schema";
import { server, users_temp } from "@/server/db/schema";
import { findAdminById, findUserById } from "@dokploy/server";
import { asc, eq } from "drizzle-orm";
import type { NextApiRequest, NextApiResponse } from "next";

View File

@@ -201,14 +201,7 @@ const Project = (
const [isBulkActionLoading, setIsBulkActionLoading] = useState(false);
const { projectId } = props;
const { data: auth } = api.auth.get.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
const { data, isLoading, refetch } = api.project.one.useQuery({ projectId });
const router = useRouter();
@@ -335,7 +328,7 @@ const Project = (
</CardTitle>
<CardDescription>{data?.description}</CardDescription>
</CardHeader>
{(auth?.role === "owner" || user?.canCreateServices) && (
{(auth?.role === "owner" || auth?.user?.canCreateServices) && (
<div className="flex flex-row gap-4 flex-wrap">
<ProjectEnvironment projectId={projectId}>
<Button variant="outline">Project Environment</Button>

View File

@@ -40,7 +40,6 @@ import { cn } from "@/lib/utils";
import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api";
import { validateRequest } from "@dokploy/server/lib/auth";
// import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server";
import copy from "copy-to-clipboard";
import { GlobeIcon, HelpCircle, ServerOff, Trash2 } from "lucide-react";
@@ -89,14 +88,6 @@ const Service = (
const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: auth } = api.auth.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
return (
<div className="pb-10">
@@ -187,7 +178,8 @@ const Service = (
<div className="flex flex-row gap-2 justify-end">
<UpdateApplication applicationId={applicationId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={applicationId} type="application" />
)}
</div>
@@ -387,8 +379,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -82,14 +82,6 @@ const Service = (
const { data: auth } = api.auth.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
return (
<div className="pb-10">
@@ -181,7 +173,8 @@ const Service = (
<div className="flex flex-row gap-2 justify-end">
<UpdateCompose composeId={composeId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={composeId} type="compose" />
)}
</div>
@@ -382,8 +375,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -63,14 +63,7 @@ const Mariadb = (
const { data } = api.mariadb.one.useQuery({ mariadbId });
const { data: auth } = api.auth.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
const { data: isCloud } = api.settings.isCloud.useQuery();
return (
@@ -154,7 +147,8 @@ const Mariadb = (
</div>
<div className="flex flex-row gap-2 justify-end">
<UpdateMariadb mariadbId={mariadbId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={mariadbId} type="mariadb" />
)}
</div>
@@ -332,8 +326,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -63,14 +63,6 @@ const Mongo = (
const { data: auth } = api.auth.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
const { data: isCloud } = api.settings.isCloud.useQuery();
@@ -156,7 +148,8 @@ const Mongo = (
<div className="flex flex-row gap-2 justify-end">
<UpdateMongo mongoId={mongoId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={mongoId} type="mongo" />
)}
</div>
@@ -334,8 +327,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -62,14 +62,6 @@ const MySql = (
const { data } = api.mysql.one.useQuery({ mysqlId });
const { data: auth } = api.auth.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
const { data: isCloud } = api.settings.isCloud.useQuery();
@@ -156,7 +148,8 @@ const MySql = (
<div className="flex flex-row gap-2 justify-end">
<UpdateMysql mysqlId={mysqlId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={mysqlId} type="mysql" />
)}
</div>
@@ -339,8 +332,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -61,14 +61,7 @@ const Postgresql = (
const [tab, setSab] = useState<TabState>(activeTab);
const { data } = api.postgres.one.useQuery({ postgresId });
const { data: auth } = api.auth.get.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: isCloud } = api.settings.isCloud.useQuery();
@@ -154,7 +147,8 @@ const Postgresql = (
<div className="flex flex-row gap-2 justify-end">
<UpdatePostgres postgresId={postgresId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={postgresId} type="postgres" />
)}
</div>
@@ -335,8 +329,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -62,14 +62,6 @@ const Redis = (
const { data: auth } = api.auth.get.useQuery();
const { data: monitoring } = api.admin.getMetricsToken.useQuery();
const { data: user } = api.user.byAuthId.useQuery(
{
authId: auth?.id || "",
},
{
enabled: !!auth?.id && auth?.role === "member",
},
);
const { data: isCloud } = api.settings.isCloud.useQuery();
@@ -155,7 +147,8 @@ const Redis = (
<div className="flex flex-row gap-2 justify-end">
<UpdateRedis redisId={redisId} />
{(auth?.role === "owner" || user?.canDeleteServices) && (
{(auth?.role === "owner" ||
auth?.user?.canDeleteServices) && (
<DeleteService id={redisId} type="redis" />
)}
</div>
@@ -327,8 +320,8 @@ export async function getServerSideProps(
req: req as any,
res: res as any,
db: null as any,
session: session,
user: user,
session: session as any,
user: user as any,
},
transformer: superjson,
});

View File

@@ -4,7 +4,6 @@ import {
apiCreateUserInvitation,
apiFindOneToken,
apiRemoveUser,
apiUpdateAdmin,
apiUpdateWebServerMonitoring,
} from "@/server/db/schema";
import {
@@ -36,19 +35,17 @@ export const adminRouter = createTRPCRouter({
...rest,
};
}),
update: adminProcedure
.input(apiUpdateAdmin)
.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "member") {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this admin",
});
}
const { id } = await findUserById(ctx.user.id);
// @ts-ignore
return updateAdmin(id, input);
}),
update: adminProcedure.mutation(async ({ input, ctx }) => {
if (ctx.user.rol === "member") {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "You are not allowed to update this admin",
});
}
const { id } = await findUserById(ctx.user.id);
// @ts-ignore
return updateAdmin(id, input);
}),
createUserInvitation: adminProcedure
.input(apiCreateUserInvitation)
.mutation(async ({ input, ctx }) => {

View File

@@ -1,30 +1,23 @@
import {
apiCreateAdmin,
apiCreateUser,
apiFindOneAuth,
apiLogin,
apiUpdateAuth,
apiVerify2FA,
apiVerifyLogin2FA,
auth,
// apiCreateAdmin,
// apiCreateUser,
// apiFindOneAuth,
// apiLogin,
// apiUpdateAuth,
// apiVerify2FA,
// apiVerifyLogin2FA,
// auth,
member,
} from "@/server/db/schema";
import { WEBSITE_URL } from "@/server/utils/stripe";
import {
type Auth,
IS_CLOUD,
createAdmin,
createUser,
findAuthByEmail,
findAuthById,
findUserById,
generate2FASecret,
getUserByToken,
removeAdminByAuthId,
sendDiscordNotification,
sendEmailNotification,
updateAuthById,
updateUser,
validateRequest,
verify2FA,
} from "@dokploy/server";
@@ -43,81 +36,77 @@ import {
} 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",
});
}
createAdmin: publicProcedure.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);
}
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(),
// );
if (IS_CLOUD) {
await sendDiscordNotificationWelcome(newAdmin);
await sendVerificationEmail(newAdmin.id);
return {
status: "success",
type: "selfhosted",
type: "cloud",
};
} 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 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 ({ 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);
// 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,
});
}
}),
// 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 }) => {
login: publicProcedure.mutation(async ({ ctx, input }) => {
try {
const auth = await findAuthByEmail(input.email);
@@ -192,33 +181,31 @@ export const authRouter = createTRPCRouter({
return true;
}),
update: protectedProcedure
.input(apiUpdateAuth)
.mutation(async ({ ctx, input }) => {
const currentAuth = await findAuthByEmail(ctx.user.email);
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",
});
}
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 }),
// });
}
// 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;
}),
return auth;
}),
removeSelfAccount: protectedProcedure
.input(
z.object({
@@ -279,7 +266,7 @@ export const authRouter = createTRPCRouter({
verifyToken: protectedProcedure.mutation(async () => {
return true;
}),
one: adminProcedure.input(apiFindOneAuth).query(async ({ input }) => {
one: adminProcedure.query(async ({ input }) => {
const auth = await findAuthById(input.id);
return auth;
}),
@@ -287,34 +274,30 @@ export const authRouter = createTRPCRouter({
generate2FASecret: protectedProcedure.query(async ({ ctx }) => {
return await generate2FASecret(ctx.user.id);
}),
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;
}),
verify2FASetup: protectedProcedure.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);
verifyLogin2FA: publicProcedure.mutation(async ({ ctx, input }) => {
// const auth = await findAuthById(input.id);
// await verify2FA(auth, auth.secret || "", input.pin);
// await verify2FA(auth, auth.secret || "", input.pin);
// const session = await lucia.createSession(auth.id, {});
// const session = await lucia.createSession(auth.id, {});
// ctx.res.appendHeader(
// "Set-Cookie",
// lucia.createSessionCookie(session.id).serialize(),
// );
// ctx.res.appendHeader(
// "Set-Cookie",
// lucia.createSessionCookie(session.id).serialize(),
// );
return true;
}),
return true;
}),
disable2FA: protectedProcedure.mutation(async ({ ctx }) => {
// const auth = await findAuthById(ctx.user.authId);
// await updateAuthById(auth.id, {

View File

@@ -1,210 +0,0 @@
import { relations } from "drizzle-orm";
import {
boolean,
integer,
json,
jsonb,
pgTable,
text,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { auth } from "./auth";
import { certificates } from "./certificate";
import { registry } from "./registry";
import { certificateType } from "./shared";
import { sshKeys } from "./ssh-key";
import { users } from "./user";
export const admins = pgTable("admin", {
adminId: text("adminId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
serverIp: text("serverIp"),
certificateType: certificateType("certificateType").notNull().default("none"),
host: text("host"),
letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
enableLogRotation: boolean("enableLogRotation").notNull().default(false),
authId: text("authId")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
stripeCustomerId: text("stripeCustomerId"),
stripeSubscriptionId: text("stripeSubscriptionId"),
serversQuantity: integer("serversQuantity").notNull().default(0),
// Metrics
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),
metricsConfig: jsonb("metricsConfig")
.$type<{
server: {
type: "Dokploy" | "Remote";
refreshRate: number;
port: number;
token: string;
urlCallback: string;
retentionDays: number;
cronJob: string;
thresholds: {
cpu: number;
memory: number;
};
};
containers: {
refreshRate: number;
services: {
include: string[];
exclude: string[];
};
};
}>()
.notNull()
.default({
server: {
type: "Dokploy",
refreshRate: 60,
port: 4500,
token: "",
retentionDays: 2,
cronJob: "",
urlCallback: "",
thresholds: {
cpu: 0,
memory: 0,
},
},
containers: {
refreshRate: 60,
services: {
include: [],
exclude: [],
},
},
}),
cleanupCacheApplications: boolean("cleanupCacheApplications")
.notNull()
.default(false),
cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews")
.notNull()
.default(false),
cleanupCacheOnCompose: boolean("cleanupCacheOnCompose")
.notNull()
.default(false),
});
export const adminsRelations = relations(admins, ({ one, many }) => ({
auth: one(auth, {
fields: [admins.authId],
references: [auth.id],
}),
users: many(users),
registry: many(registry),
sshKeys: many(sshKeys),
certificates: many(certificates),
}));
const createSchema = createInsertSchema(admins, {
adminId: z.string(),
enableDockerCleanup: z.boolean().optional(),
sshPrivateKey: z.string().optional(),
certificateType: z.enum(["letsencrypt", "none"]).default("none"),
serverIp: z.string().optional(),
letsEncryptEmail: z.string().optional(),
});
export const apiUpdateAdmin = createSchema.partial();
export const apiSaveSSHKey = createSchema
.pick({
sshPrivateKey: true,
})
.required();
export const apiAssignDomain = createSchema
.pick({
host: true,
certificateType: true,
letsEncryptEmail: true,
})
.required()
.partial({
letsEncryptEmail: true,
});
export const apiUpdateDockerCleanup = createSchema
.pick({
enableDockerCleanup: true,
})
.required()
.extend({
serverId: z.string().optional(),
});
export const apiTraefikConfig = z.object({
traefikConfig: z.string().min(1),
});
export const apiModifyTraefikConfig = z.object({
path: z.string().min(1),
traefikConfig: z.string().min(1),
serverId: z.string().optional(),
});
export const apiReadTraefikConfig = z.object({
path: z.string().min(1),
serverId: z.string().optional(),
});
export const apiEnableDashboard = z.object({
enableDashboard: z.boolean().optional(),
serverId: z.string().optional(),
});
export const apiServerSchema = z
.object({
serverId: z.string().optional(),
})
.optional();
export const apiReadStatsLogs = z.object({
page: z
.object({
pageIndex: z.number(),
pageSize: z.number(),
})
.optional(),
status: z.string().array().optional(),
search: z.string().optional(),
sort: z.object({ id: z.string(), desc: z.boolean() }).optional(),
});
export const apiUpdateWebServerMonitoring = z.object({
metricsConfig: z
.object({
server: z.object({
refreshRate: z.number().min(2),
port: z.number().min(1),
token: z.string(),
urlCallback: z.string().url(),
retentionDays: z.number().min(1),
cronJob: z.string().min(1),
thresholds: z.object({
cpu: z.number().min(0),
memory: z.number().min(0),
}),
}),
containers: z.object({
refreshRate: z.number().min(2),
services: z.object({
include: z.array(z.string()).optional(),
exclude: z.array(z.string()).optional(),
}),
}),
})
.required(),
});

View File

@@ -1,130 +0,0 @@
import { getRandomValues } from "node:crypto";
import { relations } from "drizzle-orm";
import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
// import { admins } from "./admin";
import { users } from "./user";
const randomImages = [
"/avatars/avatar-1.png",
"/avatars/avatar-2.png",
"/avatars/avatar-3.png",
"/avatars/avatar-4.png",
"/avatars/avatar-5.png",
"/avatars/avatar-6.png",
"/avatars/avatar-7.png",
"/avatars/avatar-8.png",
"/avatars/avatar-9.png",
"/avatars/avatar-10.png",
"/avatars/avatar-11.png",
"/avatars/avatar-12.png",
];
const generateRandomImage = () => {
return (
randomImages[
// @ts-ignore
getRandomValues(new Uint32Array(1))[0] % randomImages.length
] || "/avatars/avatar-1.png"
);
};
export type DatabaseUser = typeof auth.$inferSelect;
export const roles = pgEnum("Roles", ["admin", "user"]);
export const auth = pgTable("auth", {
id: text("id")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
email: text("email").notNull().unique(),
password: text("password").notNull(),
rol: roles("rol").notNull(),
image: text("image").$defaultFn(() => generateRandomImage()),
secret: text("secret"),
token: text("token"),
is2FAEnabled: boolean("is2FAEnabled").notNull().default(false),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
resetPasswordToken: text("resetPasswordToken"),
resetPasswordExpiresAt: text("resetPasswordExpiresAt"),
confirmationToken: text("confirmationToken"),
confirmationExpiresAt: text("confirmationExpiresAt"),
});
export const authRelations = relations(auth, ({ many }) => ({
// admins: many(admins),
users: many(users),
}));
const createSchema = createInsertSchema(auth, {
email: z.string().email(),
password: z.string().min(8),
rol: z.enum(["admin", "user"]),
image: z.string().optional(),
});
export const apiCreateAdmin = createSchema.pick({
email: true,
password: true,
});
export const apiCreateUser = createSchema
.pick({
password: true,
id: true,
token: true,
})
.required()
.extend({
token: z.string().min(1),
});
export const apiLogin = createSchema
.pick({
email: true,
password: true,
})
.required();
export const apiUpdateAuth = createSchema.partial().extend({
email: z.string().nullable(),
password: z.string().nullable(),
image: z.string().optional(),
currentPassword: z.string().nullable(),
});
export const apiUpdateAuthByAdmin = createSchema.partial().extend({
email: z.string().nullable(),
password: z.string().nullable(),
image: z.string().optional(),
id: z.string().min(1),
});
export const apiFindOneAuth = createSchema
.pick({
id: true,
})
.required();
export const apiVerify2FA = createSchema
.extend({
pin: z.string().min(6),
secret: z.string().min(1),
})
.pick({
pin: true,
secret: true,
})
.required();
export const apiVerifyLogin2FA = createSchema
.extend({
pin: z.string().min(6),
})
.pick({
pin: true,
id: true,
})
.required();

View File

@@ -4,10 +4,7 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { admins } from "./admin";
import { server } from "./server";
import { users_temp } from "./user";
// import { user } from "./user";
import { generateAppName } from "./utils";
export const certificates = pgTable("certificate", {

View File

@@ -1,4 +1,4 @@
import { is, relations } from "drizzle-orm";
import { relations } from "drizzle-orm";
import {
type AnyPgColumn,
boolean,

View File

@@ -4,10 +4,7 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { admins } from "./admin";
import { backups } from "./backups";
import { users_temp } from "./user";
// import { user } from "./user";
export const destinations = pgTable("destination", {
destinationId: text("destinationId")
@@ -20,7 +17,6 @@ export const destinations = pgTable("destination", {
secretAccessKey: text("secretAccessKey").notNull(),
bucket: text("bucket").notNull(),
region: text("region").notNull(),
// maybe it can be null
endpoint: text("endpoint").notNull(),
organizationId: text("organizationId")
.notNull()
@@ -35,10 +31,6 @@ export const destinationsRelations = relations(
fields: [destinations.organizationId],
references: [organization.id],
}),
// user: one(user, {
// fields: [destinations.userId],
// references: [user.id],
// }),
}),
);

View File

@@ -4,12 +4,9 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { admins } from "./admin";
import { bitbucket } from "./bitbucket";
import { github } from "./github";
import { gitlab } from "./gitlab";
import { users_temp } from "./user";
// import { user } from "./user";
export const gitProviderType = pgEnum("gitProviderType", [
"github",

View File

@@ -1,8 +1,6 @@
export * from "./application";
export * from "./postgres";
export * from "./user";
export * from "./admin";
export * from "./auth";
export * from "./project";
export * from "./domain";
export * from "./mariadb";

View File

@@ -4,8 +4,6 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { users_temp } from "./user";
// import { user } from "./user";
export const notificationType = pgEnum("notificationType", [
"slack",

View File

@@ -1,12 +1,9 @@
import { relations } from "drizzle-orm";
import { pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { admins } from "./admin";
// import { admins } from "./admin";
import { applications } from "./application";
import { compose } from "./compose";
import { mariadb } from "./mariadb";
@@ -14,7 +11,6 @@ import { mongo } from "./mongo";
import { mysql } from "./mysql";
import { postgres } from "./postgres";
import { redis } from "./redis";
import { users, users_temp } from "./user";
export const projects = pgTable("project", {
projectId: text("projectId")

View File

@@ -4,10 +4,7 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { admins } from "./admin";
import { applications } from "./application";
import { users_temp } from "./user";
// import { user } from "./user";
/**
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects.
@@ -36,10 +33,6 @@ export const registry = pgTable("registry", {
});
export const registryRelations = relations(registry, ({ one, many }) => ({
// user: one(user, {
// fields: [registry.userId],
// references: [user.id],
// }),
applications: many(applications),
}));

View File

@@ -10,9 +10,7 @@ import {
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { organization } from "./account";
import { admins } from "./admin";
import { applications } from "./application";
import { certificates } from "./certificate";
import { compose } from "./compose";
@@ -23,8 +21,6 @@ import { mysql } from "./mysql";
import { postgres } from "./postgres";
import { redis } from "./redis";
import { sshKeys } from "./ssh-key";
import { users_temp } from "./user";
// import { user } from "./user";
import { generateAppName } from "./utils";
export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]);
@@ -101,10 +97,6 @@ export const server = pgTable("server", {
});
export const serverRelations = relations(server, ({ one, many }) => ({
// user: one(user, {
// fields: [server.userId],
// references: [user.id],
// }),
deployments: many(deployments),
sshKey: one(sshKeys, {
fields: [server.sshKeyId],

View File

@@ -1,6 +1,4 @@
import { sql } from "drizzle-orm";
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { auth } from "./auth";
import { users_temp } from "./user";
// OLD TABLE
@@ -18,14 +16,3 @@ export const session = pgTable("session_temp", {
impersonatedBy: text("impersonated_by"),
activeOrganizationId: text("active_organization_id"),
});
export const sessionTable = pgTable("session", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
expiresAt: timestamp("expires_at", {
withTimezone: true,
mode: "date",
}).notNull(),
});

View File

@@ -1,27 +0,0 @@
import { pgTable, text } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
export const source = pgTable("project", {
projectId: text("projectId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
description: text("description"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
});
const createSchema = createInsertSchema(source, {
name: z.string().min(1),
description: z.string(),
projectId: z.string(),
});
export const apiCreate = createSchema.pick({
name: true,
description: true,
});

View File

@@ -4,12 +4,9 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { sshKeyCreate, sshKeyType } from "../validations";
import { organization } from "./account";
import { admins } from "./admin";
import { applications } from "./application";
import { compose } from "./compose";
import { server } from "./server";
import { users_temp } from "./user";
// import { user } from "./user";
export const sshKeys = pgTable("ssh-key", {
sshKeyId: text("sshKeyId")

View File

@@ -11,8 +11,6 @@ import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { account, organization } from "./account";
import { admins } from "./admin";
import { auth } from "./auth";
import { projects } from "./project";
import { certificateType } from "./shared";
/**
@@ -24,50 +22,6 @@ import { certificateType } from "./shared";
// OLD TABLE
export const users = pgTable("user", {
userId: text("userId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
token: text("token").notNull(),
isRegistered: boolean("isRegistered").notNull().default(false),
expirationDate: timestamp("expirationDate", {
precision: 3,
mode: "string",
}).notNull(),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
canCreateProjects: boolean("canCreateProjects").notNull().default(false),
canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false),
canCreateServices: boolean("canCreateServices").notNull().default(false),
canDeleteProjects: boolean("canDeleteProjects").notNull().default(false),
canDeleteServices: boolean("canDeleteServices").notNull().default(false),
canAccessToDocker: boolean("canAccessToDocker").notNull().default(false),
canAccessToAPI: boolean("canAccessToAPI").notNull().default(false),
canAccessToGitProviders: boolean("canAccessToGitProviders")
.notNull()
.default(false),
canAccessToTraefikFiles: boolean("canAccessToTraefikFiles")
.notNull()
.default(false),
accessedProjects: text("accesedProjects")
.array()
.notNull()
.default(sql`ARRAY[]::text[]`),
accessedServices: text("accesedServices")
.array()
.notNull()
.default(sql`ARRAY[]::text[]`),
adminId: text("adminId")
.notNull()
.references(() => admins.adminId, { onDelete: "cascade" }),
authId: text("authId")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
});
// TEMP
export const users_temp = pgTable("user_temp", {
id: text("id")
@@ -187,19 +141,11 @@ export const users_temp = pgTable("user_temp", {
});
export const usersRelations = relations(users_temp, ({ one, many }) => ({
// auth: one(auth, {
// fields: [users.authId],
// references: [auth.id],
// }),
account: one(account, {
fields: [users_temp.id],
references: [account.userId],
}),
organizations: many(organization),
// admin: one(admins, {
// fields: [users.adminId],
// references: [admins.adminId],
// }),
projects: many(projects),
}));
@@ -263,3 +209,91 @@ export const apiFindOneUserByAuth = createSchema
// authId: true,
})
.required();
export const apiSaveSSHKey = createSchema
.pick({
sshPrivateKey: true,
})
.required();
export const apiAssignDomain = createSchema
.pick({
host: true,
certificateType: true,
letsEncryptEmail: true,
})
.required()
.partial({
letsEncryptEmail: true,
});
export const apiUpdateDockerCleanup = createSchema
.pick({
enableDockerCleanup: true,
})
.required()
.extend({
serverId: z.string().optional(),
});
export const apiTraefikConfig = z.object({
traefikConfig: z.string().min(1),
});
export const apiModifyTraefikConfig = z.object({
path: z.string().min(1),
traefikConfig: z.string().min(1),
serverId: z.string().optional(),
});
export const apiReadTraefikConfig = z.object({
path: z.string().min(1),
serverId: z.string().optional(),
});
export const apiEnableDashboard = z.object({
enableDashboard: z.boolean().optional(),
serverId: z.string().optional(),
});
export const apiServerSchema = z
.object({
serverId: z.string().optional(),
})
.optional();
export const apiReadStatsLogs = z.object({
page: z
.object({
pageIndex: z.number(),
pageSize: z.number(),
})
.optional(),
status: z.string().array().optional(),
search: z.string().optional(),
sort: z.object({ id: z.string(), desc: z.boolean() }).optional(),
});
export const apiUpdateWebServerMonitoring = z.object({
metricsConfig: z
.object({
server: z.object({
refreshRate: z.number().min(2),
port: z.number().min(1),
token: z.string(),
urlCallback: z.string().url(),
retentionDays: z.number().min(1),
cronJob: z.string().min(1),
thresholds: z.object({
cpu: z.number().min(0),
memory: z.number().min(0),
}),
}),
containers: z.object({
refreshRate: z.number().min(2),
services: z.object({
include: z.array(z.string()).optional(),
exclude: z.array(z.string()).optional(),
}),
}),
})
.required(),
});

View File

@@ -2,9 +2,7 @@ import { randomBytes } from "node:crypto";
import { db } from "@dokploy/server/db";
import {
account,
admins,
type apiCreateUserInvitation,
auth,
member,
organization,
users_temp,

View File

@@ -1,12 +1,6 @@
import { randomBytes } from "node:crypto";
import { db } from "@dokploy/server/db";
import {
admins,
type apiCreateAdmin,
type apiCreateUser,
auth,
users_temp,
} from "@dokploy/server/db/schema";
import { users_temp } from "@dokploy/server/db/schema";
import { getPublicIpWithFallback } from "@dokploy/server/wss/utils";
import { TRPCError } from "@trpc/server";
import * as bcrypt from "bcrypt";
@@ -17,89 +11,6 @@ import QRCode from "qrcode";
import { IS_CLOUD } from "../constants";
import { findUserById } from "./admin";
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.users_temp.findFirst({
where: eq(users_temp.id, authId),
@@ -117,21 +28,6 @@ export const findAuthById = async (authId: string) => {
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 (userId: string) => {
const user = await findUserById(userId);