import { paths } from "@dokploy/server/constants";import { relations, sql } from "drizzle-orm";import { boolean, integer, pgTable, text, timestamp,} from "drizzle-orm/pg-core";import { createInsertSchema } from "drizzle-zod";import { nanoid } from "nanoid";import { z } from "zod";import { account, apikey, organization } from "./account";import { backups } from "./backups";import { projects } from "./project";import { schedules } from "./schedule";import { ssoProvider } from "./sso";/** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. * * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ // OLD TABLE // TEMPexport const user = pgTable("user", { id: text("id") .notNull() .primaryKey() .$defaultFn(() => nanoid()), firstName: text("firstName").notNull().default(""), lastName: text("lastName").notNull().default(""), isRegistered: boolean("isRegistered").notNull().default(false), expirationDate: text("expirationDate") .notNull() .$defaultFn(() => new Date().toISOString()), createdAt2: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), createdAt: timestamp("created_at").defaultNow(), // Auth twoFactorEnabled: boolean("two_factor_enabled"), email: text("email").notNull().unique(), emailVerified: boolean("email_verified").notNull(), image: text("image"), banned: boolean("banned"), banReason: text("ban_reason"), banExpires: timestamp("ban_expires"), updatedAt: timestamp("updated_at").notNull(), // Admin role: text("role").notNull().default("user"), // Metrics enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), allowImpersonation: boolean("allowImpersonation").notNull().default(false), // Enterprise / proprietary features enableEnterpriseFeatures: boolean("enableEnterpriseFeatures") .notNull() .default(false), licenseKey: text("licenseKey"), isValidEnterpriseLicense: boolean("isValidEnterpriseLicense") .notNull() .default(false), stripeCustomerId: text("stripeCustomerId"), stripeSubscriptionId: text("stripeSubscriptionId"), serversQuantity: integer("serversQuantity").notNull().default(0), sendInvoiceNotifications: boolean("sendInvoiceNotifications") .notNull() .default(false), isEnterpriseCloud: boolean("isEnterpriseCloud").notNull().default(false), trustedOrigins: text("trustedOrigins").array(), bookmarkedTemplates: text("bookmarkedTemplates") .array() .default(sql`ARRAY[]::text[]`),}); export const usersRelations = relations(user, ({ one, many }) => ({ account: one(account, { fields: [user.id], references: [account.userId], }), organizations: many(organization), projects: many(projects), apiKeys: many(apikey), ssoProviders: many(ssoProvider), backups: many(backups), schedules: many(schedules),})); const createSchema = createInsertSchema(user, { id: z.string().min(1), isRegistered: z.boolean().optional(),}).omit({ role: true, trustedOrigins: true, bookmarkedTemplates: true, isValidEnterpriseLicense: true, isEnterpriseCloud: true,}); export const apiCreateUserInvitation = createSchema.pick({}).extend({ email: z.string().email(),}); export const apiRemoveUser = createSchema .pick({ id: true, }) .required(); export const apiFindOneToken = createSchema .pick({}) .required() .extend({ token: z.string().min(1), }); export const apiAssignPermissions = createSchema .pick({ id: true, // canCreateProjects: true, // canCreateServices: true, // canDeleteProjects: true, // canDeleteServices: true, // accessedProjects: true, // accessedServices: true, // canAccessToTraefikFiles: true, // canAccessToDocker: true, // canAccessToAPI: true, // canAccessToSSHKeys: true, // canAccessToGitProviders: true, }) .extend({ accessedProjects: z.array(z.string()).optional(), accessedEnvironments: z.array(z.string()).optional(), accessedServices: z.array(z.string()).optional(), accessedGitProviders: z.array(z.string()).optional(), accessedServers: z.array(z.string()).optional(), canCreateProjects: z.boolean().optional(), canCreateServices: z.boolean().optional(), canDeleteProjects: z.boolean().optional(), canDeleteServices: z.boolean().optional(), canAccessToDocker: z.boolean().optional(), canAccessToTraefikFiles: z.boolean().optional(), canAccessToAPI: z.boolean().optional(), canAccessToSSHKeys: z.boolean().optional(), canAccessToGitProviders: z.boolean().optional(), canDeleteEnvironments: z.boolean().optional(), canCreateEnvironments: z.boolean().optional(), }) .required(); export const apiFindOneUser = createSchema .pick({ id: true, }) .required(); export const apiFindOneUserByAuth = createSchema .pick({ // authId: true, }) .required(); 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) .refine( (path) => { // Prevent directory traversal attacks if (path.includes("../") || path.includes("..\\")) { return false; } const { MAIN_TRAEFIK_PATH } = paths(); if (path.startsWith("/") && !path.startsWith(MAIN_TRAEFIK_PATH)) { return false; } // Prevent null bytes and other dangerous characters if (path.includes("\0") || path.includes("\x00")) { return false; } return true; }, { message: "Invalid path: path traversal or unauthorized directory access detected", }, ), 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(), dateRange: z .object({ start: z.string().optional(), end: z.string().optional(), }) .optional(),}); export const apiUpdateUser = createSchema.partial().extend({ email: z .string() .email("Please enter a valid email address") .min(1, "Email is required") .optional(), password: z.string().optional(), currentPassword: z.string().optional(), firstName: z.string().optional(), lastName: z.string().optional(),});
import { paths } from "@dokploy/server/constants";
import { relations, sql } from "drizzle-orm";
import {
boolean,
integer,
pgTable,
text,
timestamp,
} from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { account, apikey, organization } from "./account";
import { backups } from "./backups";
import { projects } from "./project";
import { schedules } from "./schedule";
import { ssoProvider } from "./sso";
/**
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects.
*
* @see https://orm.drizzle.team/docs/goodies#multi-project-schema
*/
// OLD TABLE
// TEMP
export const user = pgTable("user", {
id: text("id")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
firstName: text("firstName").notNull().default(""),
lastName: text("lastName").notNull().default(""),
isRegistered: boolean("isRegistered").notNull().default(false),
expirationDate: text("expirationDate")
.notNull()
.$defaultFn(() => new Date().toISOString()),
createdAt2: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
createdAt: timestamp("created_at").defaultNow(),
// Auth
twoFactorEnabled: boolean("two_factor_enabled"),
email: text("email").notNull().unique(),
emailVerified: boolean("email_verified").notNull(),
image: text("image"),
banned: boolean("banned"),
banReason: text("ban_reason"),
banExpires: timestamp("ban_expires"),
updatedAt: timestamp("updated_at").notNull(),
// Admin
role: text("role").notNull().default("user"),
// Metrics
enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false),
allowImpersonation: boolean("allowImpersonation").notNull().default(false),
// Enterprise / proprietary features
enableEnterpriseFeatures: boolean("enableEnterpriseFeatures")
.notNull()
.default(false),
licenseKey: text("licenseKey"),
isValidEnterpriseLicense: boolean("isValidEnterpriseLicense")
.notNull()
.default(false),
stripeCustomerId: text("stripeCustomerId"),
stripeSubscriptionId: text("stripeSubscriptionId"),
serversQuantity: integer("serversQuantity").notNull().default(0),
sendInvoiceNotifications: boolean("sendInvoiceNotifications")
.notNull()
.default(false),
isEnterpriseCloud: boolean("isEnterpriseCloud").notNull().default(false),
trustedOrigins: text("trustedOrigins").array(),
bookmarkedTemplates: text("bookmarkedTemplates")
.array()
.default(sql`ARRAY[]::text[]`),
});
export const usersRelations = relations(user, ({ one, many }) => ({
account: one(account, {
fields: [user.id],
references: [account.userId],
}),
organizations: many(organization),
projects: many(projects),
apiKeys: many(apikey),
ssoProviders: many(ssoProvider),
backups: many(backups),
schedules: many(schedules),
}));
const createSchema = createInsertSchema(user, {
id: z.string().min(1),
isRegistered: z.boolean().optional(),
}).omit({
role: true,
trustedOrigins: true,
bookmarkedTemplates: true,
isValidEnterpriseLicense: true,
isEnterpriseCloud: true,
});
export const apiCreateUserInvitation = createSchema.pick({}).extend({
email: z.string().email(),
});
export const apiRemoveUser = createSchema
.pick({
id: true,
})
.required();
export const apiFindOneToken = createSchema
.pick({})
.required()
.extend({
token: z.string().min(1),
});
export const apiAssignPermissions = createSchema
.pick({
id: true,
// canCreateProjects: true,
// canCreateServices: true,
// canDeleteProjects: true,
// canDeleteServices: true,
// accessedProjects: true,
// accessedServices: true,
// canAccessToTraefikFiles: true,
// canAccessToDocker: true,
// canAccessToAPI: true,
// canAccessToSSHKeys: true,
// canAccessToGitProviders: true,
})
.extend({
accessedProjects: z.array(z.string()).optional(),
accessedEnvironments: z.array(z.string()).optional(),
accessedServices: z.array(z.string()).optional(),
accessedGitProviders: z.array(z.string()).optional(),
accessedServers: z.array(z.string()).optional(),
canCreateProjects: z.boolean().optional(),
canCreateServices: z.boolean().optional(),
canDeleteProjects: z.boolean().optional(),
canDeleteServices: z.boolean().optional(),
canAccessToDocker: z.boolean().optional(),
canAccessToTraefikFiles: z.boolean().optional(),
canAccessToAPI: z.boolean().optional(),
canAccessToSSHKeys: z.boolean().optional(),
canAccessToGitProviders: z.boolean().optional(),
canDeleteEnvironments: z.boolean().optional(),
canCreateEnvironments: z.boolean().optional(),
})
.required();
export const apiFindOneUser = createSchema
.pick({
id: true,
})
.required();
export const apiFindOneUserByAuth = createSchema
.pick({
// authId: true,
})
.required();
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)
.refine(
(path) => {
// Prevent directory traversal attacks
if (path.includes("../") || path.includes("..\\")) {
return false;
}
const { MAIN_TRAEFIK_PATH } = paths();
if (path.startsWith("/") && !path.startsWith(MAIN_TRAEFIK_PATH)) {
return false;
}
// Prevent null bytes and other dangerous characters
if (path.includes("\0") || path.includes("\x00")) {
return false;
}
return true;
},
{
message:
"Invalid path: path traversal or unauthorized directory access detected",
},
),
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(),
dateRange: z
.object({
start: z.string().optional(),
end: z.string().optional(),
})
.optional(),
});
export const apiUpdateUser = createSchema.partial().extend({
email: z
.string()
.email("Please enter a valid email address")
.min(1, "Email is required")
.optional(),
password: z.string().optional(),
currentPassword: z.string().optional(),
firstName: z.string().optional(),
lastName: z.string().optional(),
});