mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Revert "Merge branch 'canary' into kucherenko/canary"
This reverts commit819822f30b, reversing changes made tobda9b05134.
This commit is contained in:
117
packages/server/src/auth/auth.ts
Normal file
117
packages/server/src/auth/auth.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type { IncomingMessage, ServerResponse } from "node:http";
|
||||
import { findAdminByAuthId } from "@dokploy/server/services/admin";
|
||||
import { findUserByAuthId } from "@dokploy/server/services/user";
|
||||
import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle";
|
||||
import { TimeSpan } from "lucia";
|
||||
import { Lucia } from "lucia/dist/core.js";
|
||||
import type { Session, User } from "lucia/dist/core.js";
|
||||
import { db } from "../db";
|
||||
import { type DatabaseUser, auth, sessionTable } from "../db/schema";
|
||||
|
||||
export const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, auth);
|
||||
|
||||
export const lucia = new Lucia(adapter, {
|
||||
sessionCookie: {
|
||||
attributes: {
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
sessionExpiresIn: new TimeSpan(1, "d"),
|
||||
getUserAttributes: (attributes) => {
|
||||
return {
|
||||
email: attributes.email,
|
||||
rol: attributes.rol,
|
||||
secret: attributes.secret !== null,
|
||||
adminId: attributes.adminId,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
declare module "lucia" {
|
||||
interface Register {
|
||||
Lucia: typeof lucia;
|
||||
DatabaseUserAttributes: Omit<DatabaseUser, "id"> & {
|
||||
authId: string;
|
||||
adminId: string;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export type ReturnValidateToken = Promise<{
|
||||
user: (User & { authId: string; adminId: string }) | null;
|
||||
session: Session | null;
|
||||
}>;
|
||||
|
||||
export async function validateRequest(
|
||||
req: IncomingMessage,
|
||||
res: ServerResponse,
|
||||
): ReturnValidateToken {
|
||||
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
||||
|
||||
if (!sessionId) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
const result = await lucia.validateSession(sessionId);
|
||||
if (result?.session?.fresh) {
|
||||
res.appendHeader(
|
||||
"Set-Cookie",
|
||||
lucia.createSessionCookie(result.session.id).serialize(),
|
||||
);
|
||||
}
|
||||
if (!result.session) {
|
||||
res.appendHeader(
|
||||
"Set-Cookie",
|
||||
lucia.createBlankSessionCookie().serialize(),
|
||||
);
|
||||
}
|
||||
if (result.user) {
|
||||
try {
|
||||
if (result.user?.rol === "admin") {
|
||||
const admin = await findAdminByAuthId(result.user.id);
|
||||
result.user.adminId = admin.adminId;
|
||||
} else if (result.user?.rol === "user") {
|
||||
const userResult = await findUserByAuthId(result.user.id);
|
||||
result.user.adminId = userResult.adminId;
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
session: result.session,
|
||||
...((result.user && {
|
||||
user: {
|
||||
authId: result.user.id,
|
||||
email: result.user.email,
|
||||
rol: result.user.rol,
|
||||
id: result.user.id,
|
||||
secret: result.user.secret,
|
||||
adminId: result.user.adminId,
|
||||
},
|
||||
}) || {
|
||||
user: null,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
export async function validateWebSocketRequest(
|
||||
req: IncomingMessage,
|
||||
): Promise<{ user: User; session: Session } | { user: null; session: null }> {
|
||||
const sessionId = lucia.readSessionCookie(req.headers.cookie ?? "");
|
||||
|
||||
if (!sessionId) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
const result = await lucia.validateSession(sessionId);
|
||||
return result;
|
||||
}
|
||||
99
packages/server/src/auth/token.ts
Normal file
99
packages/server/src/auth/token.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
import type { IncomingMessage } from "node:http";
|
||||
import { TimeSpan } from "lucia";
|
||||
import { Lucia } from "lucia/dist/core.js";
|
||||
import { findAdminByAuthId } from "../services/admin";
|
||||
import { findUserByAuthId } from "../services/user";
|
||||
import { type ReturnValidateToken, adapter } from "./auth";
|
||||
|
||||
export const luciaToken = new Lucia(adapter, {
|
||||
sessionCookie: {
|
||||
attributes: {
|
||||
secure: false,
|
||||
},
|
||||
},
|
||||
sessionExpiresIn: new TimeSpan(365, "d"),
|
||||
getUserAttributes: (attributes) => {
|
||||
return {
|
||||
email: attributes.email,
|
||||
rol: attributes.rol,
|
||||
secret: attributes.secret !== null,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const validateBearerToken = async (
|
||||
req: IncomingMessage,
|
||||
): ReturnValidateToken => {
|
||||
const authorizationHeader = req.headers.authorization;
|
||||
const sessionId = luciaToken.readBearerToken(authorizationHeader ?? "");
|
||||
if (!sessionId) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
const result = await luciaToken.validateSession(sessionId);
|
||||
|
||||
if (result.user) {
|
||||
if (result.user?.rol === "admin") {
|
||||
const admin = await findAdminByAuthId(result.user.id);
|
||||
result.user.adminId = admin.adminId;
|
||||
} else if (result.user?.rol === "user") {
|
||||
const userResult = await findUserByAuthId(result.user.id);
|
||||
result.user.adminId = userResult.adminId;
|
||||
}
|
||||
}
|
||||
return {
|
||||
session: result.session,
|
||||
...((result.user && {
|
||||
user: {
|
||||
adminId: result.user.adminId,
|
||||
authId: result.user.id,
|
||||
email: result.user.email,
|
||||
rol: result.user.rol,
|
||||
id: result.user.id,
|
||||
secret: result.user.secret,
|
||||
},
|
||||
}) || {
|
||||
user: null,
|
||||
}),
|
||||
};
|
||||
};
|
||||
|
||||
export const validateBearerTokenAPI = async (
|
||||
authorizationHeader: string,
|
||||
): ReturnValidateToken => {
|
||||
const sessionId = luciaToken.readBearerToken(authorizationHeader ?? "");
|
||||
if (!sessionId) {
|
||||
return {
|
||||
user: null,
|
||||
session: null,
|
||||
};
|
||||
}
|
||||
const result = await luciaToken.validateSession(sessionId);
|
||||
|
||||
if (result.user) {
|
||||
if (result.user?.rol === "admin") {
|
||||
const admin = await findAdminByAuthId(result.user.id);
|
||||
result.user.adminId = admin.adminId;
|
||||
} else if (result.user?.rol === "user") {
|
||||
const userResult = await findUserByAuthId(result.user.id);
|
||||
result.user.adminId = userResult.adminId;
|
||||
}
|
||||
}
|
||||
return {
|
||||
session: result.session,
|
||||
...((result.user && {
|
||||
user: {
|
||||
adminId: result.user.adminId,
|
||||
authId: result.user.id,
|
||||
email: result.user.email,
|
||||
rol: result.user.rol,
|
||||
id: result.user.id,
|
||||
secret: result.user.secret,
|
||||
},
|
||||
}) || {
|
||||
user: null,
|
||||
}),
|
||||
};
|
||||
};
|
||||
@@ -1,194 +0,0 @@
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
integer,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { nanoid } from "nanoid";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { users_temp } from "./user";
|
||||
|
||||
export const account = pgTable("account", {
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
accountId: text("account_id")
|
||||
.notNull()
|
||||
.$defaultFn(() => nanoid()),
|
||||
providerId: text("provider_id").notNull(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
accessToken: text("access_token"),
|
||||
refreshToken: text("refresh_token"),
|
||||
idToken: text("id_token"),
|
||||
accessTokenExpiresAt: timestamp("access_token_expires_at"),
|
||||
refreshTokenExpiresAt: timestamp("refresh_token_expires_at"),
|
||||
scope: text("scope"),
|
||||
password: text("password"),
|
||||
is2FAEnabled: boolean("is2FAEnabled").notNull().default(false),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
updatedAt: timestamp("updated_at").notNull(),
|
||||
resetPasswordToken: text("resetPasswordToken"),
|
||||
resetPasswordExpiresAt: text("resetPasswordExpiresAt"),
|
||||
confirmationToken: text("confirmationToken"),
|
||||
confirmationExpiresAt: text("confirmationExpiresAt"),
|
||||
});
|
||||
|
||||
export const accountRelations = relations(account, ({ one }) => ({
|
||||
user: one(users_temp, {
|
||||
fields: [account.userId],
|
||||
references: [users_temp.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const verification = pgTable("verification", {
|
||||
id: text("id").primaryKey(),
|
||||
identifier: text("identifier").notNull(),
|
||||
value: text("value").notNull(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
createdAt: timestamp("created_at"),
|
||||
updatedAt: timestamp("updated_at"),
|
||||
});
|
||||
|
||||
export const organization = pgTable("organization", {
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
name: text("name").notNull(),
|
||||
slug: text("slug").unique(),
|
||||
logo: text("logo"),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
metadata: text("metadata"),
|
||||
ownerId: text("owner_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const organizationRelations = relations(
|
||||
organization,
|
||||
({ one, many }) => ({
|
||||
owner: one(users_temp, {
|
||||
fields: [organization.ownerId],
|
||||
references: [users_temp.id],
|
||||
}),
|
||||
servers: many(server),
|
||||
projects: many(projects),
|
||||
members: many(member),
|
||||
}),
|
||||
);
|
||||
|
||||
export const member = pgTable("member", {
|
||||
id: text("id")
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
organizationId: text("organization_id")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
role: text("role").notNull().$type<"owner" | "member" | "admin">(),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
teamId: text("team_id"),
|
||||
// Permissions
|
||||
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[]`),
|
||||
});
|
||||
|
||||
export const memberRelations = relations(member, ({ one }) => ({
|
||||
organization: one(organization, {
|
||||
fields: [member.organizationId],
|
||||
references: [organization.id],
|
||||
}),
|
||||
user: one(users_temp, {
|
||||
fields: [member.userId],
|
||||
references: [users_temp.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const invitation = pgTable("invitation", {
|
||||
id: text("id").primaryKey(),
|
||||
organizationId: text("organization_id")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
email: text("email").notNull(),
|
||||
role: text("role").$type<"owner" | "member" | "admin">(),
|
||||
status: text("status").notNull(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
inviterId: text("inviter_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
teamId: text("team_id"),
|
||||
});
|
||||
|
||||
export const invitationRelations = relations(invitation, ({ one }) => ({
|
||||
organization: one(organization, {
|
||||
fields: [invitation.organizationId],
|
||||
references: [organization.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
export const twoFactor = pgTable("two_factor", {
|
||||
id: text("id").primaryKey(),
|
||||
secret: text("secret").notNull(),
|
||||
backupCodes: text("backup_codes").notNull(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const apikey = pgTable("apikey", {
|
||||
id: text("id").primaryKey(),
|
||||
name: text("name"),
|
||||
start: text("start"),
|
||||
prefix: text("prefix"),
|
||||
key: text("key").notNull(),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
refillInterval: integer("refill_interval"),
|
||||
refillAmount: integer("refill_amount"),
|
||||
lastRefillAt: timestamp("last_refill_at"),
|
||||
enabled: boolean("enabled"),
|
||||
rateLimitEnabled: boolean("rate_limit_enabled"),
|
||||
rateLimitTimeWindow: integer("rate_limit_time_window"),
|
||||
rateLimitMax: integer("rate_limit_max"),
|
||||
requestCount: integer("request_count"),
|
||||
remaining: integer("remaining"),
|
||||
lastRequest: timestamp("last_request"),
|
||||
expiresAt: timestamp("expires_at"),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
updatedAt: timestamp("updated_at").notNull(),
|
||||
permissions: text("permissions"),
|
||||
metadata: text("metadata"),
|
||||
});
|
||||
|
||||
export const apikeyRelations = relations(apikey, ({ one }) => ({
|
||||
user: one(users_temp, {
|
||||
fields: [apikey.userId],
|
||||
references: [users_temp.id],
|
||||
}),
|
||||
}));
|
||||
121
packages/server/src/db/schema/admin.ts
Normal file
121
packages/server/src/db/schema/admin.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { ai } from "./ai";
|
||||
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),
|
||||
});
|
||||
|
||||
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),
|
||||
ai: many(ai),
|
||||
}));
|
||||
|
||||
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(),
|
||||
});
|
||||
@@ -44,6 +44,7 @@ export const buildType = pgEnum("buildType", [
|
||||
"static",
|
||||
]);
|
||||
|
||||
// TODO: refactor this types
|
||||
export interface HealthCheckSwarm {
|
||||
Test?: string[] | undefined;
|
||||
Interval?: number | undefined;
|
||||
|
||||
130
packages/server/src/db/schema/auth.ts
Normal file
130
packages/server/src/db/schema/auth.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
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();
|
||||
@@ -61,5 +61,5 @@ export const apiUpdateBitbucket = createSchema.extend({
|
||||
name: z.string().min(1),
|
||||
bitbucketUsername: z.string().optional(),
|
||||
bitbucketWorkspaceName: z.string().optional(),
|
||||
organizationId: z.string().optional(),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { boolean, 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 { server } from "./server";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
@@ -20,24 +20,27 @@ export const certificates = pgTable("certificate", {
|
||||
.$defaultFn(() => generateAppName("certificate"))
|
||||
.unique(),
|
||||
autoRenew: boolean("autoRenew"),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
adminId: text("adminId").references(() => admins.adminId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const certificatesRelations = relations(certificates, ({ one }) => ({
|
||||
server: one(server, {
|
||||
fields: [certificates.serverId],
|
||||
references: [server.serverId],
|
||||
export const certificatesRelations = relations(
|
||||
certificates,
|
||||
({ one, many }) => ({
|
||||
server: one(server, {
|
||||
fields: [certificates.serverId],
|
||||
references: [server.serverId],
|
||||
}),
|
||||
admin: one(admins, {
|
||||
fields: [certificates.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}),
|
||||
organization: one(organization, {
|
||||
fields: [certificates.organizationId],
|
||||
references: [organization.id],
|
||||
}),
|
||||
}));
|
||||
);
|
||||
|
||||
export const apiCreateCertificate = createInsertSchema(certificates, {
|
||||
name: z.string().min(1),
|
||||
|
||||
@@ -69,7 +69,6 @@ export const compose = pgTable("compose", {
|
||||
composePath: text("composePath").notNull().default("./docker-compose.yml"),
|
||||
suffix: text("suffix").notNull().default(""),
|
||||
randomize: boolean("randomize").notNull().default(false),
|
||||
isolatedDeployment: boolean("isolatedDeployment").notNull().default(false),
|
||||
composeStatus: applicationStatus("composeStatus").notNull().default("idle"),
|
||||
projectId: text("projectId")
|
||||
.notNull()
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
import { pgGenerate } from "drizzle-dbml-generator"; // Using Postgres for this example
|
||||
import * as schema from "./index";
|
||||
|
||||
const out = "./schema.dbml";
|
||||
const relational = true;
|
||||
|
||||
pgGenerate({ schema, out, relational });
|
||||
@@ -1,4 +1,4 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import { is, relations } from "drizzle-orm";
|
||||
import {
|
||||
type AnyPgColumn,
|
||||
boolean,
|
||||
@@ -47,7 +47,6 @@ export const deployments = pgTable("deployment", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
errorMessage: text("errorMessage"),
|
||||
});
|
||||
|
||||
export const deploymentsRelations = relations(deployments, ({ one }) => ({
|
||||
|
||||
@@ -3,7 +3,7 @@ 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 { backups } from "./backups";
|
||||
|
||||
export const destinations = pgTable("destination", {
|
||||
@@ -17,19 +17,20 @@ 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")
|
||||
adminId: text("adminId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
.references(() => admins.adminId, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const destinationsRelations = relations(
|
||||
destinations,
|
||||
({ many, one }) => ({
|
||||
backups: many(backups),
|
||||
organization: one(organization, {
|
||||
fields: [destinations.organizationId],
|
||||
references: [organization.id],
|
||||
admin: one(admins, {
|
||||
fields: [destinations.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { pgEnum, 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 { bitbucket } from "./bitbucket";
|
||||
import { github } from "./github";
|
||||
import { gitlab } from "./gitlab";
|
||||
@@ -24,12 +24,12 @@ export const gitProvider = pgTable("git_provider", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
adminId: text("adminId").references(() => admins.adminId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const gitProviderRelations = relations(gitProvider, ({ one }) => ({
|
||||
export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({
|
||||
github: one(github, {
|
||||
fields: [gitProvider.gitProviderId],
|
||||
references: [github.gitProviderId],
|
||||
@@ -42,9 +42,9 @@ export const gitProviderRelations = relations(gitProvider, ({ one }) => ({
|
||||
fields: [gitProvider.gitProviderId],
|
||||
references: [bitbucket.gitProviderId],
|
||||
}),
|
||||
organization: one(organization, {
|
||||
fields: [gitProvider.organizationId],
|
||||
references: [organization.id],
|
||||
admin: one(admins, {
|
||||
fields: [gitProvider.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}));
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
export * from "./application";
|
||||
export * from "./postgres";
|
||||
export * from "./user";
|
||||
export * from "./admin";
|
||||
export * from "./auth";
|
||||
export * from "./project";
|
||||
export * from "./domain";
|
||||
export * from "./mariadb";
|
||||
@@ -29,4 +31,3 @@ export * from "./server";
|
||||
export * from "./utils";
|
||||
export * from "./preview-deployments";
|
||||
export * from "./ai";
|
||||
export * from "./account";
|
||||
|
||||
@@ -3,7 +3,7 @@ import { boolean, integer, pgEnum, 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";
|
||||
|
||||
export const notificationType = pgEnum("notificationType", [
|
||||
"slack",
|
||||
@@ -24,7 +24,6 @@ export const notifications = pgTable("notification", {
|
||||
databaseBackup: boolean("databaseBackup").notNull().default(false),
|
||||
dokployRestart: boolean("dokployRestart").notNull().default(false),
|
||||
dockerCleanup: boolean("dockerCleanup").notNull().default(false),
|
||||
serverThreshold: boolean("serverThreshold").notNull().default(false),
|
||||
notificationType: notificationType("notificationType").notNull(),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
@@ -44,9 +43,9 @@ export const notifications = pgTable("notification", {
|
||||
gotifyId: text("gotifyId").references(() => gotify.gotifyId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
adminId: text("adminId").references(() => admins.adminId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const slack = pgTable("slack", {
|
||||
@@ -65,7 +64,6 @@ export const telegram = pgTable("telegram", {
|
||||
.$defaultFn(() => nanoid()),
|
||||
botToken: text("botToken").notNull(),
|
||||
chatId: text("chatId").notNull(),
|
||||
messageThreadId: text("messageThreadId"),
|
||||
});
|
||||
|
||||
export const discord = pgTable("discord", {
|
||||
@@ -122,9 +120,9 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({
|
||||
fields: [notifications.gotifyId],
|
||||
references: [gotify.gotifyId],
|
||||
}),
|
||||
organization: one(organization, {
|
||||
fields: [notifications.organizationId],
|
||||
references: [organization.id],
|
||||
admin: one(admins, {
|
||||
fields: [notifications.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -138,7 +136,6 @@ export const apiCreateSlack = notificationsSchema
|
||||
name: true,
|
||||
appDeploy: true,
|
||||
dockerCleanup: true,
|
||||
serverThreshold: true,
|
||||
})
|
||||
.extend({
|
||||
webhookUrl: z.string().min(1),
|
||||
@@ -149,7 +146,7 @@ export const apiCreateSlack = notificationsSchema
|
||||
export const apiUpdateSlack = apiCreateSlack.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
slackId: z.string(),
|
||||
organizationId: z.string().optional(),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestSlackConnection = apiCreateSlack.pick({
|
||||
@@ -165,25 +162,22 @@ export const apiCreateTelegram = notificationsSchema
|
||||
name: true,
|
||||
appDeploy: true,
|
||||
dockerCleanup: true,
|
||||
serverThreshold: true,
|
||||
})
|
||||
.extend({
|
||||
botToken: z.string().min(1),
|
||||
chatId: z.string().min(1),
|
||||
messageThreadId: z.string(),
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiUpdateTelegram = apiCreateTelegram.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
telegramId: z.string().min(1),
|
||||
organizationId: z.string().optional(),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestTelegramConnection = apiCreateTelegram.pick({
|
||||
botToken: true,
|
||||
chatId: true,
|
||||
messageThreadId: true,
|
||||
});
|
||||
|
||||
export const apiCreateDiscord = notificationsSchema
|
||||
@@ -194,7 +188,6 @@ export const apiCreateDiscord = notificationsSchema
|
||||
name: true,
|
||||
appDeploy: true,
|
||||
dockerCleanup: true,
|
||||
serverThreshold: true,
|
||||
})
|
||||
.extend({
|
||||
webhookUrl: z.string().min(1),
|
||||
@@ -205,7 +198,7 @@ export const apiCreateDiscord = notificationsSchema
|
||||
export const apiUpdateDiscord = apiCreateDiscord.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
discordId: z.string().min(1),
|
||||
organizationId: z.string().optional(),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestDiscordConnection = apiCreateDiscord
|
||||
@@ -224,7 +217,6 @@ export const apiCreateEmail = notificationsSchema
|
||||
name: true,
|
||||
appDeploy: true,
|
||||
dockerCleanup: true,
|
||||
serverThreshold: true,
|
||||
})
|
||||
.extend({
|
||||
smtpServer: z.string().min(1),
|
||||
@@ -239,7 +231,7 @@ export const apiCreateEmail = notificationsSchema
|
||||
export const apiUpdateEmail = apiCreateEmail.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
emailId: z.string().min(1),
|
||||
organizationId: z.string().optional(),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestEmailConnection = apiCreateEmail.pick({
|
||||
@@ -271,7 +263,7 @@ export const apiCreateGotify = notificationsSchema
|
||||
export const apiUpdateGotify = apiCreateGotify.partial().extend({
|
||||
notificationId: z.string().min(1),
|
||||
gotifyId: z.string().min(1),
|
||||
organizationId: z.string().optional(),
|
||||
adminId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiTestGotifyConnection = apiCreateGotify
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
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 { applications } from "./application";
|
||||
import { compose } from "./compose";
|
||||
import { mariadb } from "./mariadb";
|
||||
@@ -22,10 +23,9 @@ export const projects = pgTable("project", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
|
||||
organizationId: text("organizationId")
|
||||
adminId: text("adminId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
.references(() => admins.adminId, { onDelete: "cascade" }),
|
||||
env: text("env").notNull().default(""),
|
||||
});
|
||||
|
||||
@@ -37,9 +37,9 @@ export const projectRelations = relations(projects, ({ many, one }) => ({
|
||||
mongo: many(mongo),
|
||||
redis: many(redis),
|
||||
compose: many(compose),
|
||||
organization: one(organization, {
|
||||
fields: [projects.organizationId],
|
||||
references: [organization.id],
|
||||
admin: one(admins, {
|
||||
fields: [projects.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import { pgEnum, 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 { applications } from "./application";
|
||||
/**
|
||||
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
||||
@@ -27,12 +27,16 @@ export const registry = pgTable("registry", {
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
registryType: registryType("selfHosted").notNull().default("cloud"),
|
||||
organizationId: text("organizationId")
|
||||
adminId: text("adminId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
.references(() => admins.adminId, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const registryRelations = relations(registry, ({ many }) => ({
|
||||
export const registryRelations = relations(registry, ({ one, many }) => ({
|
||||
admin: one(admins, {
|
||||
fields: [registry.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
applications: many(applications),
|
||||
}));
|
||||
|
||||
@@ -41,7 +45,7 @@ const createSchema = createInsertSchema(registry, {
|
||||
username: z.string().min(1),
|
||||
password: z.string().min(1),
|
||||
registryUrl: z.string(),
|
||||
organizationId: z.string().min(1),
|
||||
adminId: z.string().min(1),
|
||||
registryId: z.string().min(1),
|
||||
registryType: z.enum(["cloud"]),
|
||||
imagePrefix: z.string().nullable().optional(),
|
||||
|
||||
@@ -1,872 +0,0 @@
|
||||
enum applicationStatus {
|
||||
idle
|
||||
running
|
||||
done
|
||||
error
|
||||
}
|
||||
|
||||
enum buildType {
|
||||
dockerfile
|
||||
heroku_buildpacks
|
||||
paketo_buildpacks
|
||||
nixpacks
|
||||
static
|
||||
}
|
||||
|
||||
enum certificateType {
|
||||
letsencrypt
|
||||
none
|
||||
}
|
||||
|
||||
enum composeType {
|
||||
"docker-compose"
|
||||
stack
|
||||
}
|
||||
|
||||
enum databaseType {
|
||||
postgres
|
||||
mariadb
|
||||
mysql
|
||||
mongo
|
||||
}
|
||||
|
||||
enum deploymentStatus {
|
||||
running
|
||||
done
|
||||
error
|
||||
}
|
||||
|
||||
enum domainType {
|
||||
compose
|
||||
application
|
||||
preview
|
||||
}
|
||||
|
||||
enum gitProviderType {
|
||||
github
|
||||
gitlab
|
||||
bitbucket
|
||||
}
|
||||
|
||||
enum mountType {
|
||||
bind
|
||||
volume
|
||||
file
|
||||
}
|
||||
|
||||
enum notificationType {
|
||||
slack
|
||||
telegram
|
||||
discord
|
||||
email
|
||||
gotify
|
||||
}
|
||||
|
||||
enum protocolType {
|
||||
tcp
|
||||
udp
|
||||
}
|
||||
|
||||
enum RegistryType {
|
||||
selfHosted
|
||||
cloud
|
||||
}
|
||||
|
||||
enum Roles {
|
||||
admin
|
||||
user
|
||||
}
|
||||
|
||||
enum serverStatus {
|
||||
active
|
||||
inactive
|
||||
}
|
||||
|
||||
enum serviceType {
|
||||
application
|
||||
postgres
|
||||
mysql
|
||||
mariadb
|
||||
mongo
|
||||
redis
|
||||
compose
|
||||
}
|
||||
|
||||
enum sourceType {
|
||||
docker
|
||||
git
|
||||
github
|
||||
gitlab
|
||||
bitbucket
|
||||
drop
|
||||
}
|
||||
|
||||
enum sourceTypeCompose {
|
||||
git
|
||||
github
|
||||
gitlab
|
||||
bitbucket
|
||||
raw
|
||||
}
|
||||
|
||||
table account {
|
||||
id text [pk, not null]
|
||||
account_id text [not null]
|
||||
provider_id text [not null]
|
||||
user_id text [not null]
|
||||
access_token text
|
||||
refresh_token text
|
||||
id_token text
|
||||
access_token_expires_at timestamp
|
||||
refresh_token_expires_at timestamp
|
||||
scope text
|
||||
password text
|
||||
is2FAEnabled boolean [not null, default: false]
|
||||
created_at timestamp [not null]
|
||||
updated_at timestamp [not null]
|
||||
resetPasswordToken text
|
||||
resetPasswordExpiresAt text
|
||||
confirmationToken text
|
||||
confirmationExpiresAt text
|
||||
}
|
||||
|
||||
table admin {
|
||||
}
|
||||
|
||||
table application {
|
||||
applicationId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null, unique]
|
||||
description text
|
||||
env text
|
||||
previewEnv text
|
||||
previewBuildArgs text
|
||||
previewWildcard text
|
||||
previewPort integer [default: 3000]
|
||||
previewHttps boolean [not null, default: false]
|
||||
previewPath text [default: '/']
|
||||
certificateType certificateType [not null, default: 'none']
|
||||
previewLimit integer [default: 3]
|
||||
isPreviewDeploymentsActive boolean [default: false]
|
||||
buildArgs text
|
||||
memoryReservation text
|
||||
memoryLimit text
|
||||
cpuReservation text
|
||||
cpuLimit text
|
||||
title text
|
||||
enabled boolean
|
||||
subtitle text
|
||||
command text
|
||||
refreshToken text
|
||||
sourceType sourceType [not null, default: 'github']
|
||||
repository text
|
||||
owner text
|
||||
branch text
|
||||
buildPath text [default: '/']
|
||||
autoDeploy boolean
|
||||
gitlabProjectId integer
|
||||
gitlabRepository text
|
||||
gitlabOwner text
|
||||
gitlabBranch text
|
||||
gitlabBuildPath text [default: '/']
|
||||
gitlabPathNamespace text
|
||||
bitbucketRepository text
|
||||
bitbucketOwner text
|
||||
bitbucketBranch text
|
||||
bitbucketBuildPath text [default: '/']
|
||||
username text
|
||||
password text
|
||||
dockerImage text
|
||||
registryUrl text
|
||||
customGitUrl text
|
||||
customGitBranch text
|
||||
customGitBuildPath text
|
||||
customGitSSHKeyId text
|
||||
dockerfile text
|
||||
dockerContextPath text
|
||||
dockerBuildStage text
|
||||
dropBuildPath text
|
||||
healthCheckSwarm json
|
||||
restartPolicySwarm json
|
||||
placementSwarm json
|
||||
updateConfigSwarm json
|
||||
rollbackConfigSwarm json
|
||||
modeSwarm json
|
||||
labelsSwarm json
|
||||
networkSwarm json
|
||||
replicas integer [not null, default: 1]
|
||||
applicationStatus applicationStatus [not null, default: 'idle']
|
||||
buildType buildType [not null, default: 'nixpacks']
|
||||
herokuVersion text [default: '24']
|
||||
publishDirectory text
|
||||
createdAt text [not null]
|
||||
registryId text
|
||||
projectId text [not null]
|
||||
githubId text
|
||||
gitlabId text
|
||||
bitbucketId text
|
||||
serverId text
|
||||
}
|
||||
|
||||
table auth {
|
||||
id text [pk, not null]
|
||||
email text [not null, unique]
|
||||
password text [not null]
|
||||
rol Roles [not null]
|
||||
image text
|
||||
secret text
|
||||
token text
|
||||
is2FAEnabled boolean [not null, default: false]
|
||||
createdAt text [not null]
|
||||
resetPasswordToken text
|
||||
resetPasswordExpiresAt text
|
||||
confirmationToken text
|
||||
confirmationExpiresAt text
|
||||
}
|
||||
|
||||
table backup {
|
||||
backupId text [pk, not null]
|
||||
schedule text [not null]
|
||||
enabled boolean
|
||||
database text [not null]
|
||||
prefix text [not null]
|
||||
destinationId text [not null]
|
||||
databaseType databaseType [not null]
|
||||
postgresId text
|
||||
mariadbId text
|
||||
mysqlId text
|
||||
mongoId text
|
||||
}
|
||||
|
||||
table bitbucket {
|
||||
bitbucketId text [pk, not null]
|
||||
bitbucketUsername text
|
||||
appPassword text
|
||||
bitbucketWorkspaceName text
|
||||
gitProviderId text [not null]
|
||||
}
|
||||
|
||||
table certificate {
|
||||
certificateId text [pk, not null]
|
||||
name text [not null]
|
||||
certificateData text [not null]
|
||||
privateKey text [not null]
|
||||
certificatePath text [not null, unique]
|
||||
autoRenew boolean
|
||||
userId text
|
||||
serverId text
|
||||
}
|
||||
|
||||
table compose {
|
||||
composeId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null]
|
||||
description text
|
||||
env text
|
||||
composeFile text [not null, default: '']
|
||||
refreshToken text
|
||||
sourceType sourceTypeCompose [not null, default: 'github']
|
||||
composeType composeType [not null, default: 'docker-compose']
|
||||
repository text
|
||||
owner text
|
||||
branch text
|
||||
autoDeploy boolean
|
||||
gitlabProjectId integer
|
||||
gitlabRepository text
|
||||
gitlabOwner text
|
||||
gitlabBranch text
|
||||
gitlabPathNamespace text
|
||||
bitbucketRepository text
|
||||
bitbucketOwner text
|
||||
bitbucketBranch text
|
||||
customGitUrl text
|
||||
customGitBranch text
|
||||
customGitSSHKeyId text
|
||||
command text [not null, default: '']
|
||||
composePath text [not null, default: './docker-compose.yml']
|
||||
suffix text [not null, default: '']
|
||||
randomize boolean [not null, default: false]
|
||||
isolatedDeployment boolean [not null, default: false]
|
||||
composeStatus applicationStatus [not null, default: 'idle']
|
||||
projectId text [not null]
|
||||
createdAt text [not null]
|
||||
githubId text
|
||||
gitlabId text
|
||||
bitbucketId text
|
||||
serverId text
|
||||
}
|
||||
|
||||
table deployment {
|
||||
deploymentId text [pk, not null]
|
||||
title text [not null]
|
||||
description text
|
||||
status deploymentStatus [default: 'running']
|
||||
logPath text [not null]
|
||||
applicationId text
|
||||
composeId text
|
||||
serverId text
|
||||
isPreviewDeployment boolean [default: false]
|
||||
previewDeploymentId text
|
||||
createdAt text [not null]
|
||||
errorMessage text
|
||||
}
|
||||
|
||||
table destination {
|
||||
destinationId text [pk, not null]
|
||||
name text [not null]
|
||||
provider text
|
||||
accessKey text [not null]
|
||||
secretAccessKey text [not null]
|
||||
bucket text [not null]
|
||||
region text [not null]
|
||||
endpoint text [not null]
|
||||
userId text [not null]
|
||||
}
|
||||
|
||||
table discord {
|
||||
discordId text [pk, not null]
|
||||
webhookUrl text [not null]
|
||||
decoration boolean
|
||||
}
|
||||
|
||||
table domain {
|
||||
domainId text [pk, not null]
|
||||
host text [not null]
|
||||
https boolean [not null, default: false]
|
||||
port integer [default: 3000]
|
||||
path text [default: '/']
|
||||
serviceName text
|
||||
domainType domainType [default: 'application']
|
||||
uniqueConfigKey serial [not null, increment]
|
||||
createdAt text [not null]
|
||||
composeId text
|
||||
applicationId text
|
||||
previewDeploymentId text
|
||||
certificateType certificateType [not null, default: 'none']
|
||||
}
|
||||
|
||||
table email {
|
||||
emailId text [pk, not null]
|
||||
smtpServer text [not null]
|
||||
smtpPort integer [not null]
|
||||
username text [not null]
|
||||
password text [not null]
|
||||
fromAddress text [not null]
|
||||
toAddress text[] [not null]
|
||||
}
|
||||
|
||||
table git_provider {
|
||||
gitProviderId text [pk, not null]
|
||||
name text [not null]
|
||||
providerType gitProviderType [not null, default: 'github']
|
||||
createdAt text [not null]
|
||||
userId text
|
||||
}
|
||||
|
||||
table github {
|
||||
githubId text [pk, not null]
|
||||
githubAppName text
|
||||
githubAppId integer
|
||||
githubClientId text
|
||||
githubClientSecret text
|
||||
githubInstallationId text
|
||||
githubPrivateKey text
|
||||
githubWebhookSecret text
|
||||
gitProviderId text [not null]
|
||||
}
|
||||
|
||||
table gitlab {
|
||||
gitlabId text [pk, not null]
|
||||
gitlabUrl text [not null, default: 'https://gitlab.com']
|
||||
application_id text
|
||||
redirect_uri text
|
||||
secret text
|
||||
access_token text
|
||||
refresh_token text
|
||||
group_name text
|
||||
expires_at integer
|
||||
gitProviderId text [not null]
|
||||
}
|
||||
|
||||
table gotify {
|
||||
gotifyId text [pk, not null]
|
||||
serverUrl text [not null]
|
||||
appToken text [not null]
|
||||
priority integer [not null, default: 5]
|
||||
decoration boolean
|
||||
}
|
||||
|
||||
table invitation {
|
||||
id text [pk, not null]
|
||||
organization_id text [not null]
|
||||
email text [not null]
|
||||
role text
|
||||
status text [not null]
|
||||
expires_at timestamp [not null]
|
||||
inviter_id text [not null]
|
||||
}
|
||||
|
||||
table mariadb {
|
||||
mariadbId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null, unique]
|
||||
description text
|
||||
databaseName text [not null]
|
||||
databaseUser text [not null]
|
||||
databasePassword text [not null]
|
||||
rootPassword text [not null]
|
||||
dockerImage text [not null]
|
||||
command text
|
||||
env text
|
||||
memoryReservation text
|
||||
memoryLimit text
|
||||
cpuReservation text
|
||||
cpuLimit text
|
||||
externalPort integer
|
||||
applicationStatus applicationStatus [not null, default: 'idle']
|
||||
createdAt text [not null]
|
||||
projectId text [not null]
|
||||
serverId text
|
||||
}
|
||||
|
||||
table member {
|
||||
id text [pk, not null]
|
||||
organization_id text [not null]
|
||||
user_id text [not null]
|
||||
role text [not null]
|
||||
created_at timestamp [not null]
|
||||
}
|
||||
|
||||
table mongo {
|
||||
mongoId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null, unique]
|
||||
description text
|
||||
databaseUser text [not null]
|
||||
databasePassword text [not null]
|
||||
dockerImage text [not null]
|
||||
command text
|
||||
env text
|
||||
memoryReservation text
|
||||
memoryLimit text
|
||||
cpuReservation text
|
||||
cpuLimit text
|
||||
externalPort integer
|
||||
applicationStatus applicationStatus [not null, default: 'idle']
|
||||
createdAt text [not null]
|
||||
projectId text [not null]
|
||||
serverId text
|
||||
replicaSets boolean [default: false]
|
||||
}
|
||||
|
||||
table mount {
|
||||
mountId text [pk, not null]
|
||||
type mountType [not null]
|
||||
hostPath text
|
||||
volumeName text
|
||||
filePath text
|
||||
content text
|
||||
serviceType serviceType [not null, default: 'application']
|
||||
mountPath text [not null]
|
||||
applicationId text
|
||||
postgresId text
|
||||
mariadbId text
|
||||
mongoId text
|
||||
mysqlId text
|
||||
redisId text
|
||||
composeId text
|
||||
}
|
||||
|
||||
table mysql {
|
||||
mysqlId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null, unique]
|
||||
description text
|
||||
databaseName text [not null]
|
||||
databaseUser text [not null]
|
||||
databasePassword text [not null]
|
||||
rootPassword text [not null]
|
||||
dockerImage text [not null]
|
||||
command text
|
||||
env text
|
||||
memoryReservation text
|
||||
memoryLimit text
|
||||
cpuReservation text
|
||||
cpuLimit text
|
||||
externalPort integer
|
||||
applicationStatus applicationStatus [not null, default: 'idle']
|
||||
createdAt text [not null]
|
||||
projectId text [not null]
|
||||
serverId text
|
||||
}
|
||||
|
||||
table notification {
|
||||
notificationId text [pk, not null]
|
||||
name text [not null]
|
||||
appDeploy boolean [not null, default: false]
|
||||
appBuildError boolean [not null, default: false]
|
||||
databaseBackup boolean [not null, default: false]
|
||||
dokployRestart boolean [not null, default: false]
|
||||
dockerCleanup boolean [not null, default: false]
|
||||
serverThreshold boolean [not null, default: false]
|
||||
notificationType notificationType [not null]
|
||||
createdAt text [not null]
|
||||
slackId text
|
||||
telegramId text
|
||||
discordId text
|
||||
emailId text
|
||||
gotifyId text
|
||||
userId text
|
||||
}
|
||||
|
||||
table organization {
|
||||
id text [pk, not null]
|
||||
name text [not null]
|
||||
slug text [unique]
|
||||
logo text
|
||||
created_at timestamp [not null]
|
||||
metadata text
|
||||
owner_id text [not null]
|
||||
}
|
||||
|
||||
table port {
|
||||
portId text [pk, not null]
|
||||
publishedPort integer [not null]
|
||||
targetPort integer [not null]
|
||||
protocol protocolType [not null]
|
||||
applicationId text [not null]
|
||||
}
|
||||
|
||||
table postgres {
|
||||
postgresId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null, unique]
|
||||
databaseName text [not null]
|
||||
databaseUser text [not null]
|
||||
databasePassword text [not null]
|
||||
description text
|
||||
dockerImage text [not null]
|
||||
command text
|
||||
env text
|
||||
memoryReservation text
|
||||
externalPort integer
|
||||
memoryLimit text
|
||||
cpuReservation text
|
||||
cpuLimit text
|
||||
applicationStatus applicationStatus [not null, default: 'idle']
|
||||
createdAt text [not null]
|
||||
projectId text [not null]
|
||||
serverId text
|
||||
}
|
||||
|
||||
table preview_deployments {
|
||||
previewDeploymentId text [pk, not null]
|
||||
branch text [not null]
|
||||
pullRequestId text [not null]
|
||||
pullRequestNumber text [not null]
|
||||
pullRequestURL text [not null]
|
||||
pullRequestTitle text [not null]
|
||||
pullRequestCommentId text [not null]
|
||||
previewStatus applicationStatus [not null, default: 'idle']
|
||||
appName text [not null, unique]
|
||||
applicationId text [not null]
|
||||
domainId text
|
||||
createdAt text [not null]
|
||||
expiresAt text
|
||||
}
|
||||
|
||||
table project {
|
||||
projectId text [pk, not null]
|
||||
name text [not null]
|
||||
description text
|
||||
createdAt text [not null]
|
||||
userId text [not null]
|
||||
env text [not null, default: '']
|
||||
}
|
||||
|
||||
table redirect {
|
||||
redirectId text [pk, not null]
|
||||
regex text [not null]
|
||||
replacement text [not null]
|
||||
permanent boolean [not null, default: false]
|
||||
uniqueConfigKey serial [not null, increment]
|
||||
createdAt text [not null]
|
||||
applicationId text [not null]
|
||||
}
|
||||
|
||||
table redis {
|
||||
redisId text [pk, not null]
|
||||
name text [not null]
|
||||
appName text [not null, unique]
|
||||
description text
|
||||
password text [not null]
|
||||
dockerImage text [not null]
|
||||
command text
|
||||
env text
|
||||
memoryReservation text
|
||||
memoryLimit text
|
||||
cpuReservation text
|
||||
cpuLimit text
|
||||
externalPort integer
|
||||
createdAt text [not null]
|
||||
applicationStatus applicationStatus [not null, default: 'idle']
|
||||
projectId text [not null]
|
||||
serverId text
|
||||
}
|
||||
|
||||
table registry {
|
||||
registryId text [pk, not null]
|
||||
registryName text [not null]
|
||||
imagePrefix text
|
||||
username text [not null]
|
||||
password text [not null]
|
||||
registryUrl text [not null, default: '']
|
||||
createdAt text [not null]
|
||||
selfHosted RegistryType [not null, default: 'cloud']
|
||||
userId text [not null]
|
||||
}
|
||||
|
||||
table security {
|
||||
securityId text [pk, not null]
|
||||
username text [not null]
|
||||
password text [not null]
|
||||
createdAt text [not null]
|
||||
applicationId text [not null]
|
||||
|
||||
indexes {
|
||||
(username, applicationId) [name: 'security_username_applicationId_unique', unique]
|
||||
}
|
||||
}
|
||||
|
||||
table server {
|
||||
serverId text [pk, not null]
|
||||
name text [not null]
|
||||
description text
|
||||
ipAddress text [not null]
|
||||
port integer [not null]
|
||||
username text [not null, default: 'root']
|
||||
appName text [not null]
|
||||
enableDockerCleanup boolean [not null, default: false]
|
||||
createdAt text [not null]
|
||||
userId text [not null]
|
||||
serverStatus serverStatus [not null, default: 'active']
|
||||
command text [not null, default: '']
|
||||
sshKeyId text
|
||||
metricsConfig jsonb [not null, default: `{"server":{"type":"Remote","refreshRate":60,"port":4500,"token":"","urlCallback":"","cronJob":"","retentionDays":2,"thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`]
|
||||
}
|
||||
|
||||
table session {
|
||||
id text [pk, not null]
|
||||
expires_at timestamp [not null]
|
||||
token text [not null, unique]
|
||||
created_at timestamp [not null]
|
||||
updated_at timestamp [not null]
|
||||
ip_address text
|
||||
user_agent text
|
||||
user_id text [not null]
|
||||
impersonated_by text
|
||||
active_organization_id text
|
||||
}
|
||||
|
||||
table slack {
|
||||
slackId text [pk, not null]
|
||||
webhookUrl text [not null]
|
||||
channel text
|
||||
}
|
||||
|
||||
table "ssh-key" {
|
||||
sshKeyId text [pk, not null]
|
||||
privateKey text [not null, default: '']
|
||||
publicKey text [not null]
|
||||
name text [not null]
|
||||
description text
|
||||
createdAt text [not null]
|
||||
lastUsedAt text
|
||||
userId text
|
||||
}
|
||||
|
||||
table telegram {
|
||||
telegramId text [pk, not null]
|
||||
botToken text [not null]
|
||||
chatId text [not null]
|
||||
}
|
||||
|
||||
table user {
|
||||
id text [pk, not null]
|
||||
name text [not null, default: '']
|
||||
token text [not null]
|
||||
isRegistered boolean [not null, default: false]
|
||||
expirationDate text [not null]
|
||||
createdAt text [not null]
|
||||
canCreateProjects boolean [not null, default: false]
|
||||
canAccessToSSHKeys boolean [not null, default: false]
|
||||
canCreateServices boolean [not null, default: false]
|
||||
canDeleteProjects boolean [not null, default: false]
|
||||
canDeleteServices boolean [not null, default: false]
|
||||
canAccessToDocker boolean [not null, default: false]
|
||||
canAccessToAPI boolean [not null, default: false]
|
||||
canAccessToGitProviders boolean [not null, default: false]
|
||||
canAccessToTraefikFiles boolean [not null, default: false]
|
||||
accesedProjects text[] [not null, default: `ARRAY[]::text[]`]
|
||||
accesedServices text[] [not null, default: `ARRAY[]::text[]`]
|
||||
email text [not null, unique]
|
||||
email_verified boolean [not null]
|
||||
image text
|
||||
role text
|
||||
banned boolean
|
||||
ban_reason text
|
||||
ban_expires timestamp
|
||||
updated_at timestamp [not null]
|
||||
serverIp text
|
||||
certificateType certificateType [not null, default: 'none']
|
||||
host text
|
||||
letsEncryptEmail text
|
||||
sshPrivateKey text
|
||||
enableDockerCleanup boolean [not null, default: false]
|
||||
enableLogRotation boolean [not null, default: false]
|
||||
enablePaidFeatures boolean [not null, default: false]
|
||||
metricsConfig jsonb [not null, 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 [not null, default: false]
|
||||
cleanupCacheOnPreviews boolean [not null, default: false]
|
||||
cleanupCacheOnCompose boolean [not null, default: false]
|
||||
stripeCustomerId text
|
||||
stripeSubscriptionId text
|
||||
serversQuantity integer [not null, default: 0]
|
||||
}
|
||||
|
||||
table verification {
|
||||
id text [pk, not null]
|
||||
identifier text [not null]
|
||||
value text [not null]
|
||||
expires_at timestamp [not null]
|
||||
created_at timestamp
|
||||
updated_at timestamp
|
||||
}
|
||||
|
||||
ref: mount.applicationId > application.applicationId
|
||||
|
||||
ref: mount.postgresId > postgres.postgresId
|
||||
|
||||
ref: mount.mariadbId > mariadb.mariadbId
|
||||
|
||||
ref: mount.mongoId > mongo.mongoId
|
||||
|
||||
ref: mount.mysqlId > mysql.mysqlId
|
||||
|
||||
ref: mount.redisId > redis.redisId
|
||||
|
||||
ref: mount.composeId > compose.composeId
|
||||
|
||||
ref: application.projectId > project.projectId
|
||||
|
||||
ref: application.customGitSSHKeyId > "ssh-key".sshKeyId
|
||||
|
||||
ref: application.registryId > registry.registryId
|
||||
|
||||
ref: application.githubId - github.githubId
|
||||
|
||||
ref: application.gitlabId - gitlab.gitlabId
|
||||
|
||||
ref: application.bitbucketId - bitbucket.bitbucketId
|
||||
|
||||
ref: application.serverId > server.serverId
|
||||
|
||||
ref: backup.destinationId > destination.destinationId
|
||||
|
||||
ref: backup.postgresId > postgres.postgresId
|
||||
|
||||
ref: backup.mariadbId > mariadb.mariadbId
|
||||
|
||||
ref: backup.mysqlId > mysql.mysqlId
|
||||
|
||||
ref: backup.mongoId > mongo.mongoId
|
||||
|
||||
ref: git_provider.gitProviderId - bitbucket.gitProviderId
|
||||
|
||||
ref: certificate.serverId > server.serverId
|
||||
|
||||
ref: certificate.userId - user.id
|
||||
|
||||
ref: compose.projectId > project.projectId
|
||||
|
||||
ref: compose.customGitSSHKeyId > "ssh-key".sshKeyId
|
||||
|
||||
ref: compose.githubId - github.githubId
|
||||
|
||||
ref: compose.gitlabId - gitlab.gitlabId
|
||||
|
||||
ref: compose.bitbucketId - bitbucket.bitbucketId
|
||||
|
||||
ref: compose.serverId > server.serverId
|
||||
|
||||
ref: deployment.applicationId > application.applicationId
|
||||
|
||||
ref: deployment.composeId > compose.composeId
|
||||
|
||||
ref: deployment.serverId > server.serverId
|
||||
|
||||
ref: deployment.previewDeploymentId > preview_deployments.previewDeploymentId
|
||||
|
||||
ref: destination.userId - user.id
|
||||
|
||||
ref: domain.applicationId > application.applicationId
|
||||
|
||||
ref: domain.composeId > compose.composeId
|
||||
|
||||
ref: preview_deployments.domainId - domain.domainId
|
||||
|
||||
ref: github.gitProviderId - git_provider.gitProviderId
|
||||
|
||||
ref: gitlab.gitProviderId - git_provider.gitProviderId
|
||||
|
||||
ref: git_provider.userId - user.id
|
||||
|
||||
ref: mariadb.projectId > project.projectId
|
||||
|
||||
ref: mariadb.serverId > server.serverId
|
||||
|
||||
ref: mongo.projectId > project.projectId
|
||||
|
||||
ref: mongo.serverId > server.serverId
|
||||
|
||||
ref: mysql.projectId > project.projectId
|
||||
|
||||
ref: mysql.serverId > server.serverId
|
||||
|
||||
ref: notification.slackId - slack.slackId
|
||||
|
||||
ref: notification.telegramId - telegram.telegramId
|
||||
|
||||
ref: notification.discordId - discord.discordId
|
||||
|
||||
ref: notification.emailId - email.emailId
|
||||
|
||||
ref: notification.gotifyId - gotify.gotifyId
|
||||
|
||||
ref: notification.userId - user.id
|
||||
|
||||
ref: port.applicationId > application.applicationId
|
||||
|
||||
ref: postgres.projectId > project.projectId
|
||||
|
||||
ref: postgres.serverId > server.serverId
|
||||
|
||||
ref: preview_deployments.applicationId > application.applicationId
|
||||
|
||||
ref: project.userId - user.id
|
||||
|
||||
ref: redirect.applicationId > application.applicationId
|
||||
|
||||
ref: redis.projectId > project.projectId
|
||||
|
||||
ref: redis.serverId > server.serverId
|
||||
|
||||
ref: registry.userId - user.id
|
||||
|
||||
ref: security.applicationId > application.applicationId
|
||||
|
||||
ref: server.userId - user.id
|
||||
|
||||
ref: server.sshKeyId > "ssh-key".sshKeyId
|
||||
|
||||
ref: "ssh-key".userId - user.id
|
||||
@@ -1,16 +1,10 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
integer,
|
||||
jsonb,
|
||||
pgEnum,
|
||||
pgTable,
|
||||
text,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { boolean, integer, pgEnum, 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 { applications } from "./application";
|
||||
import { certificates } from "./certificate";
|
||||
import { compose } from "./compose";
|
||||
@@ -39,64 +33,24 @@ export const server = pgTable("server", {
|
||||
.notNull()
|
||||
.$defaultFn(() => generateAppName("server")),
|
||||
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
|
||||
createdAt: text("createdAt").notNull(),
|
||||
organizationId: text("organizationId")
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
adminId: text("adminId")
|
||||
.notNull()
|
||||
.references(() => admins.adminId, { onDelete: "cascade" }),
|
||||
serverStatus: serverStatus("serverStatus").notNull().default("active"),
|
||||
command: text("command").notNull().default(""),
|
||||
sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
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: "Remote",
|
||||
refreshRate: 60,
|
||||
port: 4500,
|
||||
token: "",
|
||||
urlCallback: "",
|
||||
cronJob: "",
|
||||
retentionDays: 2,
|
||||
thresholds: {
|
||||
cpu: 0,
|
||||
memory: 0,
|
||||
},
|
||||
},
|
||||
containers: {
|
||||
refreshRate: 60,
|
||||
services: {
|
||||
include: [],
|
||||
exclude: [],
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
export const serverRelations = relations(server, ({ one, many }) => ({
|
||||
admin: one(admins, {
|
||||
fields: [server.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
deployments: many(deployments),
|
||||
sshKey: one(sshKeys, {
|
||||
fields: [server.sshKeyId],
|
||||
@@ -110,10 +64,6 @@ export const serverRelations = relations(server, ({ one, many }) => ({
|
||||
mysql: many(mysql),
|
||||
postgres: many(postgres),
|
||||
certificates: many(certificates),
|
||||
organization: one(organization, {
|
||||
fields: [server.organizationId],
|
||||
references: [organization.id],
|
||||
}),
|
||||
}));
|
||||
|
||||
const createSchema = createInsertSchema(server, {
|
||||
@@ -159,34 +109,3 @@ export const apiUpdateServer = createSchema
|
||||
.extend({
|
||||
command: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiUpdateServerMonitoring = createSchema
|
||||
.pick({
|
||||
serverId: true,
|
||||
})
|
||||
.required()
|
||||
.extend({
|
||||
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(),
|
||||
});
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { users_temp } from "./user";
|
||||
import { auth } from "./auth";
|
||||
|
||||
// OLD TABLE
|
||||
export const session = pgTable("session_temp", {
|
||||
export const sessionTable = pgTable("session", {
|
||||
id: text("id").primaryKey(),
|
||||
expiresAt: timestamp("expires_at").notNull(),
|
||||
token: text("token").notNull().unique(),
|
||||
createdAt: timestamp("created_at").notNull(),
|
||||
updatedAt: timestamp("updated_at").notNull(),
|
||||
ipAddress: text("ip_address"),
|
||||
userAgent: text("user_agent"),
|
||||
userId: text("user_id")
|
||||
.notNull()
|
||||
.references(() => users_temp.id, { onDelete: "cascade" }),
|
||||
impersonatedBy: text("impersonated_by"),
|
||||
activeOrganizationId: text("active_organization_id"),
|
||||
.references(() => auth.id, { onDelete: "cascade" }),
|
||||
expiresAt: timestamp("expires_at", {
|
||||
withTimezone: true,
|
||||
mode: "date",
|
||||
}).notNull(),
|
||||
});
|
||||
|
||||
27
packages/server/src/db/schema/source.ts
Normal file
27
packages/server/src/db/schema/source.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
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,
|
||||
});
|
||||
@@ -3,7 +3,7 @@ import { pgTable, text } from "drizzle-orm/pg-core";
|
||||
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";
|
||||
@@ -21,18 +21,18 @@ export const sshKeys = pgTable("ssh-key", {
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
lastUsedAt: text("lastUsedAt"),
|
||||
organizationId: text("organizationId")
|
||||
.notNull()
|
||||
.references(() => organization.id, { onDelete: "cascade" }),
|
||||
adminId: text("adminId").references(() => admins.adminId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const sshKeysRelations = relations(sshKeys, ({ many, one }) => ({
|
||||
applications: many(applications),
|
||||
compose: many(compose),
|
||||
servers: many(server),
|
||||
organization: one(organization, {
|
||||
fields: [sshKeys.organizationId],
|
||||
references: [organization.id],
|
||||
admin: one(admins, {
|
||||
fields: [sshKeys.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
}));
|
||||
|
||||
@@ -48,7 +48,7 @@ export const apiCreateSshKey = createSchema
|
||||
description: true,
|
||||
privateKey: true,
|
||||
publicKey: true,
|
||||
organizationId: true,
|
||||
adminId: true,
|
||||
})
|
||||
.merge(sshKeyCreate.pick({ privateKey: true }));
|
||||
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import { relations } from "drizzle-orm";
|
||||
import {
|
||||
boolean,
|
||||
integer,
|
||||
jsonb,
|
||||
pgTable,
|
||||
text,
|
||||
timestamp,
|
||||
} from "drizzle-orm/pg-core";
|
||||
import { relations, sql } from "drizzle-orm";
|
||||
import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { account, organization, apikey } from "./account";
|
||||
import { projects } from "./project";
|
||||
import { certificateType } from "./shared";
|
||||
import { admins } from "./admin";
|
||||
import { auth } from "./auth";
|
||||
/**
|
||||
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
|
||||
* database instance for multiple projects.
|
||||
@@ -20,115 +12,75 @@ import { certificateType } from "./shared";
|
||||
* @see https://orm.drizzle.team/docs/goodies#multi-project-schema
|
||||
*/
|
||||
|
||||
// OLD TABLE
|
||||
|
||||
// TEMP
|
||||
export const users_temp = pgTable("user_temp", {
|
||||
id: text("id")
|
||||
export const users = pgTable("user", {
|
||||
userId: text("userId")
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
name: text("name").notNull().default(""),
|
||||
|
||||
token: text("token").notNull(),
|
||||
isRegistered: boolean("isRegistered").notNull().default(false),
|
||||
expirationDate: text("expirationDate")
|
||||
expirationDate: timestamp("expirationDate", {
|
||||
precision: 3,
|
||||
mode: "string",
|
||||
}).notNull(),
|
||||
createdAt: text("createdAt")
|
||||
.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
|
||||
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),
|
||||
// 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")
|
||||
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),
|
||||
cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews")
|
||||
canAccessToTraefikFiles: boolean("canAccessToTraefikFiles")
|
||||
.notNull()
|
||||
.default(false),
|
||||
cleanupCacheOnCompose: boolean("cleanupCacheOnCompose")
|
||||
accessedProjects: text("accesedProjects")
|
||||
.array()
|
||||
.notNull()
|
||||
.default(false),
|
||||
stripeCustomerId: text("stripeCustomerId"),
|
||||
stripeSubscriptionId: text("stripeSubscriptionId"),
|
||||
serversQuantity: integer("serversQuantity").notNull().default(0),
|
||||
.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" }),
|
||||
});
|
||||
|
||||
export const usersRelations = relations(users_temp, ({ one, many }) => ({
|
||||
account: one(account, {
|
||||
fields: [users_temp.id],
|
||||
references: [account.userId],
|
||||
export const usersRelations = relations(users, ({ one }) => ({
|
||||
auth: one(auth, {
|
||||
fields: [users.authId],
|
||||
references: [auth.id],
|
||||
}),
|
||||
admin: one(admins, {
|
||||
fields: [users.adminId],
|
||||
references: [admins.adminId],
|
||||
}),
|
||||
organizations: many(organization),
|
||||
projects: many(projects),
|
||||
apiKeys: many(apikey),
|
||||
}));
|
||||
|
||||
const createSchema = createInsertSchema(users_temp, {
|
||||
id: z.string().min(1),
|
||||
const createSchema = createInsertSchema(users, {
|
||||
userId: z.string().min(1),
|
||||
authId: z.string().min(1),
|
||||
token: z.string().min(1),
|
||||
isRegistered: z.boolean().optional(),
|
||||
adminId: z.string(),
|
||||
accessedProjects: z.array(z.string()).optional(),
|
||||
accessedServices: 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(),
|
||||
});
|
||||
|
||||
export const apiCreateUserInvitation = createSchema.pick({}).extend({
|
||||
@@ -137,172 +89,41 @@ export const apiCreateUserInvitation = createSchema.pick({}).extend({
|
||||
|
||||
export const apiRemoveUser = createSchema
|
||||
.pick({
|
||||
id: true,
|
||||
authId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiFindOneToken = createSchema
|
||||
.pick({})
|
||||
.required()
|
||||
.extend({
|
||||
token: z.string().min(1),
|
||||
});
|
||||
.pick({
|
||||
token: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
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(),
|
||||
accessedServices: 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(),
|
||||
userId: true,
|
||||
canCreateProjects: true,
|
||||
canCreateServices: true,
|
||||
canDeleteProjects: true,
|
||||
canDeleteServices: true,
|
||||
accessedProjects: true,
|
||||
accessedServices: true,
|
||||
canAccessToTraefikFiles: true,
|
||||
canAccessToDocker: true,
|
||||
canAccessToAPI: true,
|
||||
canAccessToSSHKeys: true,
|
||||
canAccessToGitProviders: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiFindOneUser = createSchema
|
||||
.pick({
|
||||
id: true,
|
||||
userId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiFindOneUserByAuth = createSchema
|
||||
.pick({
|
||||
// authId: true,
|
||||
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(),
|
||||
});
|
||||
|
||||
export const apiUpdateUser = createSchema.partial().extend({
|
||||
password: z.string().optional(),
|
||||
currentPassword: z.string().optional(),
|
||||
metricsConfig: z
|
||||
.object({
|
||||
server: z.object({
|
||||
type: z.enum(["Dokploy", "Remote"]),
|
||||
refreshRate: z.number(),
|
||||
port: z.number(),
|
||||
token: z.string(),
|
||||
urlCallback: z.string(),
|
||||
retentionDays: z.number(),
|
||||
cronJob: z.string(),
|
||||
thresholds: z.object({
|
||||
cpu: z.number(),
|
||||
memory: z.number(),
|
||||
}),
|
||||
}),
|
||||
containers: z.object({
|
||||
refreshRate: z.number(),
|
||||
services: z.object({
|
||||
include: z.array(z.string()),
|
||||
exclude: z.array(z.string()),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
.optional(),
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
projectName: string;
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
projectName: string;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
projectName: string;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
Body,
|
||||
Button,
|
||||
Container,
|
||||
Head,
|
||||
Heading,
|
||||
@@ -10,6 +11,7 @@ import {
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
message: string;
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
export type TemplateProps = {
|
||||
date: string;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Preview,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface NotionMagicLinkEmailProps {
|
||||
loginCode?: string;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface PlaidVerifyIdentityEmailProps {
|
||||
validationCode?: string;
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
Section,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
const baseUrl = process.env.VERCEL_URL!;
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
Tailwind,
|
||||
Text,
|
||||
} from "@react-email/components";
|
||||
import * as React from "react";
|
||||
|
||||
interface VercelInviteUserEmailProps {
|
||||
username?: string;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
export * from "./auth/auth";
|
||||
export * from "./auth/token";
|
||||
export * from "./auth/random-password";
|
||||
// export * from "./db";
|
||||
export * from "./services/admin";
|
||||
export * from "./services/user";
|
||||
export * from "./services/project";
|
||||
@@ -27,6 +30,7 @@ export * from "./services/ssh-key";
|
||||
export * from "./services/git-provider";
|
||||
export * from "./services/bitbucket";
|
||||
export * from "./services/github";
|
||||
export * from "./services/auth";
|
||||
export * from "./services/gitlab";
|
||||
export * from "./services/server";
|
||||
export * from "./services/application";
|
||||
@@ -35,7 +39,6 @@ export * from "./setup/config-paths";
|
||||
export * from "./setup/postgres-setup";
|
||||
export * from "./setup/redis-setup";
|
||||
export * from "./setup/server-setup";
|
||||
export * from "./setup/monitoring-setup";
|
||||
export * from "./setup/setup";
|
||||
export * from "./setup/traefik-setup";
|
||||
export * from "./setup/server-validate";
|
||||
@@ -54,7 +57,6 @@ export * from "./utils/notifications/database-backup";
|
||||
export * from "./utils/notifications/dokploy-restart";
|
||||
export * from "./utils/notifications/utils";
|
||||
export * from "./utils/notifications/docker-cleanup";
|
||||
export * from "./utils/notifications/server-threshold";
|
||||
|
||||
export * from "./utils/builders/index";
|
||||
export * from "./utils/builders/compose";
|
||||
@@ -69,7 +71,6 @@ export * from "./utils/builders/utils";
|
||||
export * from "./utils/cluster/upload";
|
||||
|
||||
export * from "./utils/docker/compose";
|
||||
export * from "./utils/docker/collision";
|
||||
export * from "./utils/docker/domain";
|
||||
export * from "./utils/docker/utils";
|
||||
export * from "./utils/docker/types";
|
||||
@@ -109,10 +110,8 @@ export * from "./utils/access-log/types";
|
||||
export * from "./utils/access-log/utils";
|
||||
export * from "./constants/index";
|
||||
|
||||
export * from "./monitoring/utils";
|
||||
export * from "./monitoring/utilts";
|
||||
|
||||
export * from "./db/validations/domain";
|
||||
export * from "./db/validations/index";
|
||||
export * from "./utils/gpu-setup";
|
||||
|
||||
export * from "./lib/auth";
|
||||
|
||||
@@ -1,304 +0,0 @@
|
||||
import type { IncomingMessage } from "node:http";
|
||||
import * as bcrypt from "bcrypt";
|
||||
import { betterAuth } from "better-auth";
|
||||
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
||||
import { organization, twoFactor, apiKey } from "better-auth/plugins";
|
||||
import { and, desc, eq } from "drizzle-orm";
|
||||
import { db } from "../db";
|
||||
import * as schema from "../db/schema";
|
||||
import { sendEmail } from "../verification/send-verification-email";
|
||||
import { IS_CLOUD } from "../constants";
|
||||
|
||||
const { handler, api } = betterAuth({
|
||||
database: drizzleAdapter(db, {
|
||||
provider: "pg",
|
||||
schema: schema,
|
||||
}),
|
||||
logger: {
|
||||
disabled: process.env.NODE_ENV === "production",
|
||||
},
|
||||
appName: "Dokploy",
|
||||
socialProviders: {
|
||||
github: {
|
||||
clientId: process.env.GITHUB_CLIENT_ID as string,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
|
||||
},
|
||||
google: {
|
||||
clientId: process.env.GOOGLE_CLIENT_ID as string,
|
||||
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
|
||||
},
|
||||
},
|
||||
emailVerification: {
|
||||
sendOnSignUp: true,
|
||||
autoSignInAfterVerification: true,
|
||||
sendVerificationEmail: async ({ user, url }) => {
|
||||
if (IS_CLOUD) {
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
subject: "Verify your email",
|
||||
text: `
|
||||
<p>Click the link to verify your email: <a href="${url}">Verify Email</a></p>
|
||||
`,
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
autoSignIn: !IS_CLOUD,
|
||||
requireEmailVerification: IS_CLOUD,
|
||||
password: {
|
||||
async hash(password) {
|
||||
return bcrypt.hashSync(password, 10);
|
||||
},
|
||||
async verify({ hash, password }) {
|
||||
return bcrypt.compareSync(password, hash);
|
||||
},
|
||||
},
|
||||
sendResetPassword: async ({ user, url }) => {
|
||||
await sendEmail({
|
||||
email: user.email,
|
||||
subject: "Reset your password",
|
||||
text: `
|
||||
<p>Click the link to reset your password: <a href="${url}">Reset Password</a></p>
|
||||
`,
|
||||
});
|
||||
},
|
||||
},
|
||||
databaseHooks: {
|
||||
user: {
|
||||
create: {
|
||||
after: async (user) => {
|
||||
const isAdminPresent = await db.query.member.findFirst({
|
||||
where: eq(schema.member.role, "owner"),
|
||||
});
|
||||
|
||||
if (IS_CLOUD || !isAdminPresent) {
|
||||
await db.transaction(async (tx) => {
|
||||
const organization = await tx
|
||||
.insert(schema.organization)
|
||||
.values({
|
||||
name: "My Organization",
|
||||
ownerId: user.id,
|
||||
createdAt: new Date(),
|
||||
})
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
|
||||
await tx.insert(schema.member).values({
|
||||
userId: user.id,
|
||||
organizationId: organization?.id || "",
|
||||
role: "owner",
|
||||
createdAt: new Date(),
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
session: {
|
||||
create: {
|
||||
before: async (session) => {
|
||||
const member = await db.query.member.findFirst({
|
||||
where: eq(schema.member.userId, session.userId),
|
||||
orderBy: desc(schema.member.createdAt),
|
||||
with: {
|
||||
organization: true,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: {
|
||||
...session,
|
||||
activeOrganizationId: member?.organization.id,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
user: {
|
||||
modelName: "users_temp",
|
||||
additionalFields: {
|
||||
role: {
|
||||
type: "string",
|
||||
// required: true,
|
||||
input: false,
|
||||
},
|
||||
ownerId: {
|
||||
type: "string",
|
||||
// required: true,
|
||||
input: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
plugins: [
|
||||
apiKey({
|
||||
enableMetadata: true,
|
||||
}),
|
||||
twoFactor(),
|
||||
organization({
|
||||
async sendInvitationEmail(data, _request) {
|
||||
if (IS_CLOUD) {
|
||||
const host =
|
||||
process.env.NODE_ENV === "development"
|
||||
? "http://localhost:3000"
|
||||
: "https://dokploy.com";
|
||||
const inviteLink = `${host}/invitation?token=${data.id}`;
|
||||
|
||||
await sendEmail({
|
||||
email: data.email,
|
||||
subject: "Invitation to join organization",
|
||||
text: `
|
||||
<p>You are invited to join ${data.organization.name} on Dokploy. Click the link to accept the invitation: <a href="${inviteLink}">Accept Invitation</a></p>
|
||||
`,
|
||||
});
|
||||
}
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const auth = {
|
||||
handler,
|
||||
createApiKey: api.createApiKey,
|
||||
};
|
||||
|
||||
export const validateRequest = async (request: IncomingMessage) => {
|
||||
const apiKey = request.headers["x-api-key"] as string;
|
||||
if (apiKey) {
|
||||
try {
|
||||
const { valid, key, error } = await api.verifyApiKey({
|
||||
body: {
|
||||
key: apiKey,
|
||||
},
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw new Error(error.message || "Error verifying API key");
|
||||
}
|
||||
if (!valid || !key) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
const apiKeyRecord = await db.query.apikey.findFirst({
|
||||
where: eq(schema.apikey.id, key.id),
|
||||
with: {
|
||||
user: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!apiKeyRecord) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
const organizationId = JSON.parse(
|
||||
apiKeyRecord.metadata || "{}",
|
||||
).organizationId;
|
||||
|
||||
if (!organizationId) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
const member = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(schema.member.userId, apiKeyRecord.user.id),
|
||||
eq(schema.member.organizationId, organizationId),
|
||||
),
|
||||
with: {
|
||||
organization: true,
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
emailVerified,
|
||||
image,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
twoFactorEnabled,
|
||||
} = apiKeyRecord.user;
|
||||
|
||||
const mockSession = {
|
||||
session: {
|
||||
user: {
|
||||
id: apiKeyRecord.user.id,
|
||||
email: apiKeyRecord.user.email,
|
||||
name: apiKeyRecord.user.name,
|
||||
},
|
||||
activeOrganizationId: organizationId || "",
|
||||
},
|
||||
user: {
|
||||
id,
|
||||
name,
|
||||
email,
|
||||
emailVerified,
|
||||
image,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
twoFactorEnabled,
|
||||
role: member?.role || "member",
|
||||
ownerId: member?.organization.ownerId || apiKeyRecord.user.id,
|
||||
},
|
||||
};
|
||||
|
||||
return mockSession;
|
||||
} catch (error) {
|
||||
console.error("Error verifying API key", error);
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// If no API key, proceed with normal session validation
|
||||
const session = await api.getSession({
|
||||
headers: new Headers({
|
||||
cookie: request.headers.cookie || "",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!session?.session || !session.user) {
|
||||
return {
|
||||
session: null,
|
||||
user: null,
|
||||
};
|
||||
}
|
||||
|
||||
if (session?.user) {
|
||||
const member = await db.query.member.findFirst({
|
||||
where: and(
|
||||
eq(schema.member.userId, session.user.id),
|
||||
eq(
|
||||
schema.member.organizationId,
|
||||
session.session.activeOrganizationId || "",
|
||||
),
|
||||
),
|
||||
with: {
|
||||
organization: true,
|
||||
},
|
||||
});
|
||||
|
||||
session.user.role = member?.role || "member";
|
||||
if (member) {
|
||||
session.user.ownerId = member.organization.ownerId;
|
||||
} else {
|
||||
session.user.ownerId = session.user.id;
|
||||
}
|
||||
}
|
||||
|
||||
return session;
|
||||
};
|
||||
@@ -1,19 +1,10 @@
|
||||
import { promises } from "node:fs";
|
||||
import type Dockerode from "dockerode";
|
||||
import osUtils from "node-os-utils";
|
||||
import { paths } from "../constants";
|
||||
|
||||
export interface Container {
|
||||
BlockIO: string;
|
||||
CPUPerc: string;
|
||||
Container: string;
|
||||
ID: string;
|
||||
MemPerc: string;
|
||||
MemUsage: string;
|
||||
Name: string;
|
||||
NetIO: string;
|
||||
}
|
||||
export const recordAdvancedStats = async (
|
||||
stats: Container,
|
||||
stats: Dockerode.ContainerStats,
|
||||
appName: string,
|
||||
) => {
|
||||
const { MONITORING_PATH } = paths();
|
||||
@@ -21,20 +12,29 @@ export const recordAdvancedStats = async (
|
||||
|
||||
await promises.mkdir(path, { recursive: true });
|
||||
|
||||
await updateStatsFile(appName, "cpu", stats.CPUPerc);
|
||||
await updateStatsFile(appName, "memory", {
|
||||
used: stats.MemUsage.split(" ")[0],
|
||||
total: stats.MemUsage.split(" ")[2],
|
||||
});
|
||||
const cpuPercent = calculateCpuUsagePercent(
|
||||
stats.cpu_stats,
|
||||
stats.precpu_stats,
|
||||
);
|
||||
const memoryStats = calculateMemoryStats(stats.memory_stats);
|
||||
const blockIO = calculateBlockIO(stats.blkio_stats);
|
||||
const networkUsage = calculateNetworkUsage(stats.networks);
|
||||
|
||||
await updateStatsFile(appName, "cpu", cpuPercent);
|
||||
await updateStatsFile(appName, "memory", {
|
||||
used: memoryStats.used,
|
||||
free: memoryStats.free,
|
||||
usedPercentage: memoryStats.usedPercentage,
|
||||
total: memoryStats.total,
|
||||
});
|
||||
await updateStatsFile(appName, "block", {
|
||||
readMb: stats.BlockIO.split(" ")[0],
|
||||
writeMb: stats.BlockIO.split(" ")[2],
|
||||
readMb: blockIO.readMb,
|
||||
writeMb: blockIO.writeMb,
|
||||
});
|
||||
|
||||
await updateStatsFile(appName, "network", {
|
||||
inputMb: stats.NetIO.split(" ")[0],
|
||||
outputMb: stats.NetIO.split(" ")[2],
|
||||
inputMb: networkUsage.inputMb,
|
||||
outputMb: networkUsage.outputMb,
|
||||
});
|
||||
|
||||
if (appName === "dokploy") {
|
||||
@@ -73,7 +73,7 @@ export const readStatsFile = async (
|
||||
const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`;
|
||||
const data = await promises.readFile(filePath, "utf-8");
|
||||
return JSON.parse(data);
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
@@ -108,7 +108,7 @@ export const readLastValueStatsFile = async (
|
||||
const data = await promises.readFile(filePath, "utf-8");
|
||||
const stats = JSON.parse(data);
|
||||
return stats[stats.length - 1] || null;
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -122,3 +122,77 @@ export const getLastAdvancedStatsFile = async (appName: string) => {
|
||||
block: await readLastValueStatsFile(appName, "block"),
|
||||
};
|
||||
};
|
||||
|
||||
const calculateCpuUsagePercent = (
|
||||
cpu_stats: Dockerode.ContainerStats["cpu_stats"],
|
||||
precpu_stats: Dockerode.ContainerStats["precpu_stats"],
|
||||
) => {
|
||||
const cpuDelta =
|
||||
cpu_stats.cpu_usage.total_usage - precpu_stats.cpu_usage.total_usage;
|
||||
const systemDelta =
|
||||
cpu_stats.system_cpu_usage - precpu_stats.system_cpu_usage;
|
||||
|
||||
const numberCpus =
|
||||
cpu_stats.online_cpus ||
|
||||
(cpu_stats.cpu_usage.percpu_usage
|
||||
? cpu_stats.cpu_usage.percpu_usage.length
|
||||
: 1);
|
||||
|
||||
if (systemDelta > 0 && cpuDelta > 0) {
|
||||
return (cpuDelta / systemDelta) * numberCpus * 100.0;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
const calculateMemoryStats = (
|
||||
memory_stats: Dockerode.ContainerStats["memory_stats"],
|
||||
) => {
|
||||
const usedMemory = memory_stats.usage - (memory_stats.stats.cache || 0);
|
||||
const availableMemory = memory_stats.limit;
|
||||
const memoryUsedPercentage = (usedMemory / availableMemory) * 100.0;
|
||||
|
||||
return {
|
||||
used: usedMemory,
|
||||
free: availableMemory - usedMemory,
|
||||
usedPercentage: memoryUsedPercentage,
|
||||
total: availableMemory,
|
||||
};
|
||||
};
|
||||
const calculateBlockIO = (
|
||||
blkio_stats: Dockerode.ContainerStats["blkio_stats"],
|
||||
) => {
|
||||
let readIO = 0;
|
||||
let writeIO = 0;
|
||||
if (blkio_stats?.io_service_bytes_recursive) {
|
||||
for (const io of blkio_stats.io_service_bytes_recursive) {
|
||||
if (io.op === "read") {
|
||||
readIO += io.value;
|
||||
} else if (io.op === "write") {
|
||||
writeIO += io.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
readMb: readIO / (1024 * 1024),
|
||||
writeMb: writeIO / (1024 * 1024),
|
||||
};
|
||||
};
|
||||
|
||||
const calculateNetworkUsage = (
|
||||
networks: Dockerode.ContainerStats["networks"],
|
||||
) => {
|
||||
let totalRx = 0;
|
||||
let totalTx = 0;
|
||||
|
||||
const stats = Object.keys(networks);
|
||||
|
||||
for (const interfaceName of stats) {
|
||||
const net = networks[interfaceName];
|
||||
totalRx += net?.rx_bytes || 0;
|
||||
totalTx += net?.tx_bytes || 0;
|
||||
}
|
||||
return {
|
||||
inputMb: totalRx / (1024 * 1024),
|
||||
outputMb: totalTx / (1024 * 1024),
|
||||
};
|
||||
};
|
||||
@@ -1,56 +1,108 @@
|
||||
import { randomBytes } from "node:crypto";
|
||||
import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
invitation,
|
||||
member,
|
||||
organization,
|
||||
users_temp,
|
||||
admins,
|
||||
type apiCreateUserInvitation,
|
||||
auth,
|
||||
users,
|
||||
} 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 const findUserById = async (userId: string) => {
|
||||
const user = await db.query.users_temp.findFirst({
|
||||
where: eq(users_temp.id, userId),
|
||||
// with: {
|
||||
// account: true,
|
||||
// },
|
||||
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();
|
||||
});
|
||||
if (!user) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "User not found",
|
||||
});
|
||||
}
|
||||
return user;
|
||||
};
|
||||
|
||||
export const findOrganizationById = async (organizationId: string) => {
|
||||
const organizationResult = await db.query.organization.findFirst({
|
||||
where: eq(organization.id, organizationId),
|
||||
export const findAdminById = async (adminId: string) => {
|
||||
const admin = await db.query.admins.findFirst({
|
||||
where: eq(admins.adminId, adminId),
|
||||
});
|
||||
return organizationResult;
|
||||
if (!admin) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Admin not found",
|
||||
});
|
||||
}
|
||||
return admin;
|
||||
};
|
||||
|
||||
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 isAdminPresent = async () => {
|
||||
const admin = await db.query.member.findFirst({
|
||||
where: eq(member.role, "owner"),
|
||||
});
|
||||
|
||||
const admin = await db.query.admins.findFirst();
|
||||
if (!admin) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
export const findAdmin = async () => {
|
||||
const admin = await db.query.member.findFirst({
|
||||
where: eq(member.role, "owner"),
|
||||
export const findAdminByAuthId = async (authId: string) => {
|
||||
const admin = await db.query.admins.findFirst({
|
||||
where: eq(admins.authId, authId),
|
||||
with: {
|
||||
user: true,
|
||||
users: 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",
|
||||
@@ -61,15 +113,14 @@ export const findAdmin = async () => {
|
||||
};
|
||||
|
||||
export const getUserByToken = async (token: string) => {
|
||||
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,
|
||||
const user = await db.query.users.findFirst({
|
||||
where: eq(users.token, token),
|
||||
with: {
|
||||
auth: {
|
||||
columns: {
|
||||
password: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -79,23 +130,34 @@ 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 {
|
||||
...rest,
|
||||
isExpired: user.expiresAt < new Date(),
|
||||
userAlreadyExists: !!userAlreadyExists,
|
||||
...user,
|
||||
isExpired: user.isRegistered,
|
||||
};
|
||||
};
|
||||
|
||||
export const removeUserById = async (userId: string) => {
|
||||
export const removeUserByAuthId = async (authId: string) => {
|
||||
await db
|
||||
.delete(users_temp)
|
||||
.where(eq(users_temp.id, userId))
|
||||
.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))
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
};
|
||||
@@ -106,8 +168,8 @@ export const getDokployUrl = async () => {
|
||||
}
|
||||
const admin = await findAdmin();
|
||||
|
||||
if (admin.user.host) {
|
||||
return `https://${admin.user.host}`;
|
||||
if (admin.host) {
|
||||
return `https://${admin.host}`;
|
||||
}
|
||||
return `http://${admin.user.serverIp}:${process.env.PORT}`;
|
||||
return `http://${admin.serverIp}:${process.env.PORT}`;
|
||||
};
|
||||
|
||||
@@ -4,8 +4,9 @@ import {
|
||||
type apiCreateApplication,
|
||||
applications,
|
||||
buildAppName,
|
||||
cleanAppName,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { getAdvancedStats } from "@dokploy/server/monitoring/utils";
|
||||
import { getAdvancedStats } from "@dokploy/server/monitoring/utilts";
|
||||
import {
|
||||
buildApplication,
|
||||
getBuildCommand,
|
||||
@@ -27,6 +28,7 @@ import {
|
||||
getCustomGitCloneCommand,
|
||||
} from "@dokploy/server/utils/providers/git";
|
||||
import {
|
||||
authGithub,
|
||||
cloneGithubRepository,
|
||||
getGithubCloneCommand,
|
||||
} from "@dokploy/server/utils/providers/github";
|
||||
@@ -173,7 +175,6 @@ 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,
|
||||
@@ -182,12 +183,6 @@ 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,
|
||||
@@ -217,7 +212,7 @@ export const deployApplication = async ({
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
adminId: application.project.adminId,
|
||||
domains: application.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -230,7 +225,7 @@ export const deployApplication = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
adminId: application.project.adminId,
|
||||
});
|
||||
|
||||
throw error;
|
||||
@@ -249,7 +244,6 @@ export const rebuildApplication = async ({
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
const deployment = await createDeployment({
|
||||
applicationId: applicationId,
|
||||
title: titleLog,
|
||||
@@ -257,11 +251,6 @@ 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") {
|
||||
@@ -296,7 +285,6 @@ 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,
|
||||
@@ -306,11 +294,6 @@ 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({
|
||||
@@ -349,7 +332,7 @@ export const deployRemoteApplication = async ({
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
adminId: application.project.adminId,
|
||||
domains: application.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -373,9 +356,17 @@ export const deployRemoteApplication = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
adminId: application.project.adminId,
|
||||
});
|
||||
|
||||
console.log(
|
||||
"Error on ",
|
||||
application.buildType,
|
||||
"/",
|
||||
application.sourceType,
|
||||
error,
|
||||
);
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -394,7 +385,6 @@ export const deployPreviewApplication = async ({
|
||||
previewDeploymentId: string;
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
const deployment = await createDeploymentPreview({
|
||||
title: titleLog,
|
||||
description: descriptionLog,
|
||||
@@ -448,15 +438,9 @@ export const deployPreviewApplication = async ({
|
||||
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
|
||||
});
|
||||
application.appName = previewDeployment.appName;
|
||||
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
|
||||
application.env = application.previewEnv;
|
||||
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,
|
||||
@@ -466,6 +450,7 @@ export const deployPreviewApplication = async ({
|
||||
});
|
||||
await buildApplication(application, deployment.logPath);
|
||||
}
|
||||
// 4eef09efc46009187d668cf1c25f768d0bde4f91
|
||||
const successComment = getIssueComment(
|
||||
application.name,
|
||||
"success",
|
||||
@@ -507,7 +492,6 @@ export const deployRemotePreviewApplication = async ({
|
||||
previewDeploymentId: string;
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
const deployment = await createDeploymentPreview({
|
||||
title: titleLog,
|
||||
description: descriptionLog,
|
||||
@@ -561,21 +545,14 @@ export const deployRemotePreviewApplication = async ({
|
||||
body: `### Dokploy Preview Deployment\n\n${buildingComment}`,
|
||||
});
|
||||
application.appName = previewDeployment.appName;
|
||||
application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`;
|
||||
application.env = application.previewEnv;
|
||||
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,
|
||||
});
|
||||
@@ -625,7 +602,6 @@ export const rebuildRemoteApplication = async ({
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
const deployment = await createDeployment({
|
||||
applicationId: applicationId,
|
||||
title: titleLog,
|
||||
@@ -634,11 +610,6 @@ 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);
|
||||
|
||||
184
packages/server/src/services/auth.ts
Normal file
184
packages/server/src/services/auth.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
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;
|
||||
};
|
||||
@@ -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,20 +69,3 @@ 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 || [];
|
||||
};
|
||||
|
||||
@@ -12,14 +12,14 @@ export type Bitbucket = typeof bitbucket.$inferSelect;
|
||||
|
||||
export const createBitbucket = async (
|
||||
input: typeof apiCreateBitbucket._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
return await db.transaction(async (tx) => {
|
||||
const newGitProvider = await tx
|
||||
.insert(gitProvider)
|
||||
.values({
|
||||
providerType: "bitbucket",
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
name: input.name,
|
||||
})
|
||||
.returning()
|
||||
@@ -74,12 +74,12 @@ export const updateBitbucket = async (
|
||||
.where(eq(bitbucket.bitbucketId, bitbucketId))
|
||||
.returning();
|
||||
|
||||
if (input.name || input.organizationId) {
|
||||
if (input.name || input.adminId) {
|
||||
await tx
|
||||
.update(gitProvider)
|
||||
.set({
|
||||
name: input.name,
|
||||
organizationId: input.organizationId,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.where(eq(gitProvider.gitProviderId, input.gitProviderId))
|
||||
.returning();
|
||||
|
||||
@@ -33,13 +33,13 @@ export const findCertificateById = async (certificateId: string) => {
|
||||
|
||||
export const createCertificate = async (
|
||||
certificateData: z.infer<typeof apiCreateCertificate>,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
const certificate = await db
|
||||
.insert(certificates)
|
||||
.values({
|
||||
...certificateData,
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning();
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ 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,
|
||||
@@ -205,7 +206,6 @@ 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,10 +216,6 @@ 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,
|
||||
@@ -246,7 +242,7 @@ export const deployCompose = async ({
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
adminId: compose.project.adminId,
|
||||
domains: compose.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -261,7 +257,7 @@ export const deployCompose = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
adminId: compose.project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
@@ -277,7 +273,6 @@ export const rebuildCompose = async ({
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
|
||||
const deployment = await createDeploymentCompose({
|
||||
composeId: composeId,
|
||||
title: titleLog,
|
||||
@@ -285,10 +280,6 @@ 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 {
|
||||
@@ -320,7 +311,6 @@ 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`;
|
||||
@@ -331,10 +321,6 @@ 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") {
|
||||
@@ -380,7 +366,7 @@ export const deployRemoteCompose = async ({
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
adminId: compose.project.adminId,
|
||||
domains: compose.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -405,7 +391,7 @@ export const deployRemoteCompose = async ({
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
adminId: compose.project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
@@ -421,7 +407,6 @@ export const rebuildRemoteCompose = async ({
|
||||
descriptionLog: string;
|
||||
}) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
|
||||
const deployment = await createDeploymentCompose({
|
||||
composeId: composeId,
|
||||
title: titleLog,
|
||||
@@ -429,10 +414,6 @@ 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);
|
||||
}
|
||||
@@ -557,17 +538,6 @@ 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",
|
||||
});
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { format } from "date-fns";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import { and, desc, eq, isNull } from "drizzle-orm";
|
||||
import {
|
||||
type Application,
|
||||
findApplicationById,
|
||||
@@ -98,17 +98,6 @@ 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({
|
||||
@@ -175,17 +164,6 @@ 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",
|
||||
});
|
||||
@@ -248,17 +226,6 @@ 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",
|
||||
});
|
||||
@@ -278,11 +245,9 @@ 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,
|
||||
message: "Error deleting this deployment",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -537,11 +502,9 @@ 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,
|
||||
message: "Error creating the deployment",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -10,13 +10,13 @@ export type Destination = typeof destinations.$inferSelect;
|
||||
|
||||
export const createDestintation = async (
|
||||
input: typeof apiCreateDestination._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
const newDestination = await db
|
||||
.insert(destinations)
|
||||
.values({
|
||||
...input,
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -46,14 +46,14 @@ export const findDestinationById = async (destinationId: string) => {
|
||||
|
||||
export const removeDestinationById = async (
|
||||
destinationId: string,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
const result = await db
|
||||
.delete(destinations)
|
||||
.where(
|
||||
and(
|
||||
eq(destinations.destinationId, destinationId),
|
||||
eq(destinations.organizationId, organizationId),
|
||||
eq(destinations.adminId, adminId),
|
||||
),
|
||||
)
|
||||
.returning();
|
||||
@@ -73,7 +73,7 @@ export const updateDestinationById = async (
|
||||
.where(
|
||||
and(
|
||||
eq(destinations.destinationId, destinationId),
|
||||
eq(destinations.organizationId, destinationData.organizationId || ""),
|
||||
eq(destinations.adminId, destinationData.adminId || ""),
|
||||
),
|
||||
)
|
||||
.returning();
|
||||
|
||||
@@ -58,11 +58,7 @@ export const getContainers = async (serverId?: string | null) => {
|
||||
serverId,
|
||||
};
|
||||
})
|
||||
.filter(
|
||||
(container) =>
|
||||
!container.name.includes("dokploy") ||
|
||||
container.name.includes("dokploy-monitoring"),
|
||||
);
|
||||
.filter((container) => !container.name.includes("dokploy"));
|
||||
|
||||
return containers;
|
||||
} catch (error) {
|
||||
@@ -98,7 +94,7 @@ export const getConfig = async (
|
||||
const config = JSON.parse(stdout);
|
||||
|
||||
return config;
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const getContainersByAppNameMatch = async (
|
||||
@@ -156,7 +152,7 @@ export const getContainersByAppNameMatch = async (
|
||||
});
|
||||
|
||||
return containers || [];
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -214,7 +210,7 @@ export const getStackContainersByAppName = async (
|
||||
});
|
||||
|
||||
return containers || [];
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -274,7 +270,7 @@ export const getServiceContainersByAppName = async (
|
||||
});
|
||||
|
||||
return containers || [];
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -325,7 +321,7 @@ export const getContainersByAppLabel = async (
|
||||
});
|
||||
|
||||
return containers || [];
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
|
||||
return [];
|
||||
};
|
||||
@@ -344,7 +340,7 @@ export const containerRestart = async (containerId: string) => {
|
||||
const config = JSON.parse(stdout);
|
||||
|
||||
return config;
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const getSwarmNodes = async (serverId?: string) => {
|
||||
@@ -373,7 +369,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) => {
|
||||
@@ -399,7 +395,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) => {
|
||||
@@ -431,7 +427,7 @@ export const getNodeApplications = async (serverId?: string) => {
|
||||
.filter((service) => !service.Name.startsWith("dokploy-"));
|
||||
|
||||
return appArray;
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const getApplicationInfo = async (
|
||||
@@ -464,5 +460,5 @@ export const getApplicationInfo = async (
|
||||
.map((line) => JSON.parse(line));
|
||||
|
||||
return appArray;
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
@@ -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 { findUserById } from "./admin";
|
||||
import { findAdmin, findAdminById } 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,
|
||||
userId: string,
|
||||
adminId: string,
|
||||
serverId?: string,
|
||||
) => {
|
||||
if (serverId) {
|
||||
@@ -57,7 +57,7 @@ export const generateTraefikMeDomain = async (
|
||||
projectName: appName,
|
||||
});
|
||||
}
|
||||
const admin = await findUserById(userId);
|
||||
const admin = await findAdminById(adminId);
|
||||
return generateRandomDomain({
|
||||
serverIp: admin?.serverIp || "",
|
||||
projectName: appName,
|
||||
@@ -126,6 +126,7 @@ 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))
|
||||
|
||||
@@ -12,14 +12,14 @@ import { updatePreviewDeployment } from "./preview-deployment";
|
||||
export type Github = typeof github.$inferSelect;
|
||||
export const createGithub = async (
|
||||
input: typeof apiCreateGithub._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
return await db.transaction(async (tx) => {
|
||||
const newGitProvider = await tx
|
||||
.insert(gitProvider)
|
||||
.values({
|
||||
providerType: "github",
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
name: input.name,
|
||||
})
|
||||
.returning()
|
||||
@@ -119,7 +119,7 @@ export const issueCommentExists = async ({
|
||||
comment_id: comment_id,
|
||||
});
|
||||
return true;
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
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";
|
||||
@@ -11,14 +13,14 @@ export type Gitlab = typeof gitlab.$inferSelect;
|
||||
|
||||
export const createGitlab = async (
|
||||
input: typeof apiCreateGitlab._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
return await db.transaction(async (tx) => {
|
||||
const newGitProvider = await tx
|
||||
.insert(gitProvider)
|
||||
.values({
|
||||
providerType: "gitlab",
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
name: input.name,
|
||||
})
|
||||
.returning()
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
backups,
|
||||
mariadb,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { buildAppName } from "@dokploy/server/db/schema";
|
||||
import { buildAppName, cleanAppName } 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";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { type apiCreateMongo, backups, mongo } from "@dokploy/server/db/schema";
|
||||
import { buildAppName } from "@dokploy/server/db/schema";
|
||||
import { buildAppName, cleanAppName } 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";
|
||||
|
||||
@@ -64,7 +64,7 @@ export const createMount = async (input: typeof apiCreateMount._type) => {
|
||||
console.log(error);
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: `Error ${error instanceof Error ? error.message : error}`,
|
||||
message: "Error creating the mount",
|
||||
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 ${error instanceof Error ? error.message : error}`,
|
||||
message: "Error creating the mount",
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
@@ -123,8 +123,8 @@ export const updateMount = async (
|
||||
mountId: string,
|
||||
mountData: Partial<Mount>,
|
||||
) => {
|
||||
return await db.transaction(async (tx) => {
|
||||
const mount = await tx
|
||||
return await db.transaction(async (transaction) => {
|
||||
const mount = await db
|
||||
.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) => {
|
||||
|
||||
@@ -24,7 +24,7 @@ export type Notification = typeof notifications.$inferSelect;
|
||||
|
||||
export const createSlackNotification = async (
|
||||
input: typeof apiCreateSlack._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newSlack = await tx
|
||||
@@ -54,8 +54,7 @@ export const createSlackNotification = async (
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "slack",
|
||||
organizationId: organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -84,8 +83,7 @@ export const updateSlackNotification = async (
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
organizationId: input.organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.where(eq(notifications.notificationId, input.notificationId))
|
||||
.returning()
|
||||
@@ -114,7 +112,7 @@ export const updateSlackNotification = async (
|
||||
|
||||
export const createTelegramNotification = async (
|
||||
input: typeof apiCreateTelegram._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newTelegram = await tx
|
||||
@@ -122,7 +120,6 @@ export const createTelegramNotification = async (
|
||||
.values({
|
||||
botToken: input.botToken,
|
||||
chatId: input.chatId,
|
||||
messageThreadId: input.messageThreadId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -145,8 +142,7 @@ export const createTelegramNotification = async (
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "telegram",
|
||||
organizationId: organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -175,8 +171,7 @@ export const updateTelegramNotification = async (
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
organizationId: input.organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.where(eq(notifications.notificationId, input.notificationId))
|
||||
.returning()
|
||||
@@ -194,7 +189,6 @@ export const updateTelegramNotification = async (
|
||||
.set({
|
||||
botToken: input.botToken,
|
||||
chatId: input.chatId,
|
||||
messageThreadId: input.messageThreadId,
|
||||
})
|
||||
.where(eq(telegram.telegramId, input.telegramId))
|
||||
.returning()
|
||||
@@ -206,7 +200,7 @@ export const updateTelegramNotification = async (
|
||||
|
||||
export const createDiscordNotification = async (
|
||||
input: typeof apiCreateDiscord._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newDiscord = await tx
|
||||
@@ -236,8 +230,7 @@ export const createDiscordNotification = async (
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "discord",
|
||||
organizationId: organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -266,8 +259,7 @@ export const updateDiscordNotification = async (
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
organizationId: input.organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.where(eq(notifications.notificationId, input.notificationId))
|
||||
.returning()
|
||||
@@ -296,7 +288,7 @@ export const updateDiscordNotification = async (
|
||||
|
||||
export const createEmailNotification = async (
|
||||
input: typeof apiCreateEmail._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newEmail = await tx
|
||||
@@ -330,8 +322,7 @@ export const createEmailNotification = async (
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "email",
|
||||
organizationId: organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -360,8 +351,7 @@ export const updateEmailNotification = async (
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
organizationId: input.organizationId,
|
||||
serverThreshold: input.serverThreshold,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.where(eq(notifications.notificationId, input.notificationId))
|
||||
.returning()
|
||||
@@ -394,7 +384,7 @@ export const updateEmailNotification = async (
|
||||
|
||||
export const createGotifyNotification = async (
|
||||
input: typeof apiCreateGotify._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
await db.transaction(async (tx) => {
|
||||
const newGotify = await tx
|
||||
@@ -426,7 +416,7 @@ export const createGotifyNotification = async (
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
notificationType: "gotify",
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -455,7 +445,7 @@ export const updateGotifyNotification = async (
|
||||
databaseBackup: input.databaseBackup,
|
||||
dokployRestart: input.dokployRestart,
|
||||
dockerCleanup: input.dockerCleanup,
|
||||
organizationId: input.organizationId,
|
||||
adminId: input.adminId,
|
||||
})
|
||||
.where(eq(notifications.notificationId, input.notificationId))
|
||||
.returning()
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
backups,
|
||||
postgres,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { buildAppName } from "@dokploy/server/db/schema";
|
||||
import { buildAppName, cleanAppName } 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";
|
||||
|
||||
@@ -2,20 +2,23 @@ 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 { generatePassword } from "../templates/utils";
|
||||
import { slugify } from "../setup/server-setup";
|
||||
import { generatePassword, generateRandomDomain } 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 { findUserById } from "./admin";
|
||||
import { findAdminById } from "./admin";
|
||||
import { findApplicationById } from "./application";
|
||||
import { removeDeploymentsByPreviewDeploymentId } from "./deployment";
|
||||
import {
|
||||
removeDeployments,
|
||||
removeDeploymentsByPreviewDeploymentId,
|
||||
} from "./deployment";
|
||||
import { createDomain } from "./domain";
|
||||
import { type Github, getIssueComment } from "./github";
|
||||
|
||||
@@ -103,17 +106,13 @@ 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,
|
||||
message: "Error deleting this preview deployment",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -155,14 +154,11 @@ 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 || "",
|
||||
org?.ownerId || "",
|
||||
application.project.adminId,
|
||||
);
|
||||
|
||||
const octokit = authGithub(application?.github as Github);
|
||||
@@ -254,7 +250,7 @@ const generateWildcardDomain = async (
|
||||
baseDomain: string,
|
||||
appName: string,
|
||||
serverIp: string,
|
||||
userId: string,
|
||||
adminId: string,
|
||||
): Promise<string> => {
|
||||
if (!baseDomain.startsWith("*.")) {
|
||||
throw new Error('The base domain must start with "*."');
|
||||
@@ -272,7 +268,7 @@ const generateWildcardDomain = async (
|
||||
}
|
||||
|
||||
if (!ip) {
|
||||
const admin = await findUserById(userId);
|
||||
const admin = await findAdminById(adminId);
|
||||
ip = admin?.serverIp || "";
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,13 @@ export type Project = typeof projects.$inferSelect;
|
||||
|
||||
export const createProject = async (
|
||||
input: typeof apiCreateProject._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
const newProject = await db
|
||||
.insert(projects)
|
||||
.values({
|
||||
...input,
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
updateRedirectMiddleware,
|
||||
} from "@dokploy/server/utils/traefik/redirect";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
import type { z } from "zod";
|
||||
import { findApplicationById } from "./application";
|
||||
export type Redirect = typeof redirects.$inferSelect;
|
||||
@@ -114,11 +114,9 @@ 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,
|
||||
message: "Error updating this redirect",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { type apiCreateRedis, redis } from "@dokploy/server/db/schema";
|
||||
import { buildAppName } from "@dokploy/server/db/schema";
|
||||
import { buildAppName, cleanAppName } 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";
|
||||
|
||||
@@ -12,14 +12,14 @@ export type Registry = typeof registry.$inferSelect;
|
||||
|
||||
export const createRegistry = async (
|
||||
input: typeof apiCreateRegistry._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
return await db.transaction(async (tx) => {
|
||||
const newRegistry = await tx
|
||||
.insert(registry)
|
||||
.values({
|
||||
...input,
|
||||
organizationId: organizationId,
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -112,11 +112,9 @@ 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,
|
||||
message: "Error updating this registry",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -137,11 +135,9 @@ export const findRegistryById = async (registryId: string) => {
|
||||
return registryResponse;
|
||||
};
|
||||
|
||||
export const findAllRegistryByOrganizationId = async (
|
||||
organizationId: string,
|
||||
) => {
|
||||
export const findAllRegistryByAdminId = async (adminId: string) => {
|
||||
const registryResponse = await db.query.registry.findMany({
|
||||
where: eq(registry.organizationId, organizationId),
|
||||
where: eq(registry.adminId, adminId),
|
||||
});
|
||||
return registryResponse;
|
||||
};
|
||||
|
||||
@@ -76,11 +76,9 @@ 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,
|
||||
message: "Error removing this security",
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -100,11 +98,9 @@ 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,
|
||||
message: "Error updating this security",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,24 +1,19 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
type apiCreateServer,
|
||||
organization,
|
||||
server,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { type apiCreateServer, server } from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { desc, eq } from "drizzle-orm";
|
||||
|
||||
export type Server = typeof server.$inferSelect;
|
||||
|
||||
export const createServer = async (
|
||||
input: typeof apiCreateServer._type,
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
) => {
|
||||
const newServer = await db
|
||||
.insert(server)
|
||||
.values({
|
||||
...input,
|
||||
organizationId: organizationId,
|
||||
createdAt: new Date().toISOString(),
|
||||
adminId: adminId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
@@ -50,16 +45,12 @@ export const findServerById = async (serverId: string) => {
|
||||
return currentServer;
|
||||
};
|
||||
|
||||
export const findServersByUserId = async (userId: string) => {
|
||||
const orgs = await db.query.organization.findMany({
|
||||
where: eq(organization.ownerId, userId),
|
||||
with: {
|
||||
servers: true,
|
||||
},
|
||||
export const findServersByAdminId = async (adminId: string) => {
|
||||
const servers = await db.query.server.findMany({
|
||||
where: eq(server.adminId, adminId),
|
||||
orderBy: desc(server.createdAt),
|
||||
});
|
||||
|
||||
const servers = orgs.flatMap((org) => org.servers);
|
||||
|
||||
return servers;
|
||||
};
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
execAsync,
|
||||
execAsyncRemote,
|
||||
} from "@dokploy/server/utils/process/execAsync";
|
||||
// import packageInfo from "../../../package.json";
|
||||
|
||||
export interface IUpdateData {
|
||||
latestVersion: string | null;
|
||||
@@ -169,6 +170,7 @@ echo "$json_output"
|
||||
const result = JSON.parse(stdout);
|
||||
return result;
|
||||
}
|
||||
const items = readdirSync(dirPath, { withFileTypes: true });
|
||||
|
||||
const stack = [dirPath];
|
||||
const result: TreeDataItem[] = [];
|
||||
@@ -211,35 +213,3 @@ 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);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,53 +1,80 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { apikey, member, users_temp } from "@dokploy/server/db/schema";
|
||||
import { users } from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { auth } from "../lib/auth";
|
||||
import { eq } from "drizzle-orm";
|
||||
|
||||
export type User = typeof users_temp.$inferSelect;
|
||||
export type User = typeof users.$inferSelect;
|
||||
|
||||
export const addNewProject = async (
|
||||
userId: string,
|
||||
projectId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const userR = await findMemberById(userId, organizationId);
|
||||
|
||||
await db
|
||||
.update(member)
|
||||
.set({
|
||||
accessedProjects: [...userR.accessedProjects, projectId],
|
||||
})
|
||||
.where(
|
||||
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
|
||||
);
|
||||
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 addNewService = async (
|
||||
userId: string,
|
||||
serviceId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const userR = await findMemberById(userId, organizationId);
|
||||
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);
|
||||
|
||||
await db
|
||||
.update(member)
|
||||
.update(users)
|
||||
.set({
|
||||
accessedServices: [...userR.accessedServices, serviceId],
|
||||
accessedProjects: [...user.accessedProjects, projectId],
|
||||
})
|
||||
.where(
|
||||
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
|
||||
);
|
||||
.where(eq(users.authId, authId));
|
||||
};
|
||||
|
||||
export const addNewService = async (authId: string, serviceId: string) => {
|
||||
const user = await findUserByAuthId(authId);
|
||||
await db
|
||||
.update(users)
|
||||
.set({
|
||||
accessedServices: [...user.accessedServices, serviceId],
|
||||
})
|
||||
.where(eq(users.authId, authId));
|
||||
};
|
||||
|
||||
export const canPerformCreationService = async (
|
||||
userId: string,
|
||||
projectId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { accessedProjects, canCreateServices } = await findMemberById(
|
||||
userId,
|
||||
organizationId,
|
||||
);
|
||||
const { accessedProjects, canCreateServices } =
|
||||
await findUserByAuthId(userId);
|
||||
const haveAccessToProject = accessedProjects.includes(projectId);
|
||||
|
||||
if (canCreateServices && haveAccessToProject) {
|
||||
@@ -60,9 +87,8 @@ export const canPerformCreationService = async (
|
||||
export const canPerformAccessService = async (
|
||||
userId: string,
|
||||
serviceId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { accessedServices } = await findMemberById(userId, organizationId);
|
||||
const { accessedServices } = await findUserByAuthId(userId);
|
||||
const haveAccessToService = accessedServices.includes(serviceId);
|
||||
|
||||
if (haveAccessToService) {
|
||||
@@ -73,14 +99,11 @@ export const canPerformAccessService = async (
|
||||
};
|
||||
|
||||
export const canPeformDeleteService = async (
|
||||
userId: string,
|
||||
authId: string,
|
||||
serviceId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { accessedServices, canDeleteServices } = await findMemberById(
|
||||
userId,
|
||||
organizationId,
|
||||
);
|
||||
const { accessedServices, canDeleteServices } =
|
||||
await findUserByAuthId(authId);
|
||||
const haveAccessToService = accessedServices.includes(serviceId);
|
||||
|
||||
if (canDeleteServices && haveAccessToService) {
|
||||
@@ -90,11 +113,8 @@ export const canPeformDeleteService = async (
|
||||
return false;
|
||||
};
|
||||
|
||||
export const canPerformCreationProject = async (
|
||||
userId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { canCreateProjects } = await findMemberById(userId, organizationId);
|
||||
export const canPerformCreationProject = async (authId: string) => {
|
||||
const { canCreateProjects } = await findUserByAuthId(authId);
|
||||
|
||||
if (canCreateProjects) {
|
||||
return true;
|
||||
@@ -103,11 +123,8 @@ export const canPerformCreationProject = async (
|
||||
return false;
|
||||
};
|
||||
|
||||
export const canPerformDeleteProject = async (
|
||||
userId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { canDeleteProjects } = await findMemberById(userId, organizationId);
|
||||
export const canPerformDeleteProject = async (authId: string) => {
|
||||
const { canDeleteProjects } = await findUserByAuthId(authId);
|
||||
|
||||
if (canDeleteProjects) {
|
||||
return true;
|
||||
@@ -117,11 +134,10 @@ export const canPerformDeleteProject = async (
|
||||
};
|
||||
|
||||
export const canPerformAccessProject = async (
|
||||
userId: string,
|
||||
authId: string,
|
||||
projectId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { accessedProjects } = await findMemberById(userId, organizationId);
|
||||
const { accessedProjects } = await findUserByAuthId(authId);
|
||||
|
||||
const haveAccessToProject = accessedProjects.includes(projectId);
|
||||
|
||||
@@ -131,45 +147,26 @@ export const canPerformAccessProject = async (
|
||||
return false;
|
||||
};
|
||||
|
||||
export const canAccessToTraefikFiles = async (
|
||||
userId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { canAccessToTraefikFiles } = await findMemberById(
|
||||
userId,
|
||||
organizationId,
|
||||
);
|
||||
export const canAccessToTraefikFiles = async (authId: string) => {
|
||||
const { canAccessToTraefikFiles } = await findUserByAuthId(authId);
|
||||
return canAccessToTraefikFiles;
|
||||
};
|
||||
|
||||
export const checkServiceAccess = async (
|
||||
userId: string,
|
||||
authId: string,
|
||||
serviceId: string,
|
||||
organizationId: string,
|
||||
action = "access" as "access" | "create" | "delete",
|
||||
) => {
|
||||
let hasPermission = false;
|
||||
switch (action) {
|
||||
case "create":
|
||||
hasPermission = await canPerformCreationService(
|
||||
userId,
|
||||
serviceId,
|
||||
organizationId,
|
||||
);
|
||||
hasPermission = await canPerformCreationService(authId, serviceId);
|
||||
break;
|
||||
case "access":
|
||||
hasPermission = await canPerformAccessService(
|
||||
userId,
|
||||
serviceId,
|
||||
organizationId,
|
||||
);
|
||||
hasPermission = await canPerformAccessService(authId, serviceId);
|
||||
break;
|
||||
case "delete":
|
||||
hasPermission = await canPeformDeleteService(
|
||||
userId,
|
||||
serviceId,
|
||||
organizationId,
|
||||
);
|
||||
hasPermission = await canPeformDeleteService(authId, serviceId);
|
||||
break;
|
||||
default:
|
||||
hasPermission = false;
|
||||
@@ -185,7 +182,6 @@ export const checkServiceAccess = async (
|
||||
export const checkProjectAccess = async (
|
||||
authId: string,
|
||||
action: "create" | "delete" | "access",
|
||||
organizationId: string,
|
||||
projectId?: string,
|
||||
) => {
|
||||
let hasPermission = false;
|
||||
@@ -194,14 +190,13 @@ export const checkProjectAccess = async (
|
||||
hasPermission = await canPerformAccessProject(
|
||||
authId,
|
||||
projectId as string,
|
||||
organizationId,
|
||||
);
|
||||
break;
|
||||
case "create":
|
||||
hasPermission = await canPerformCreationProject(authId, organizationId);
|
||||
hasPermission = await canPerformCreationProject(authId);
|
||||
break;
|
||||
case "delete":
|
||||
hasPermission = await canPerformDeleteProject(authId, organizationId);
|
||||
hasPermission = await canPerformDeleteProject(authId);
|
||||
break;
|
||||
default:
|
||||
hasPermission = false;
|
||||
@@ -213,82 +208,3 @@ 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;
|
||||
};
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
import { findServerById } from "@dokploy/server/services/server";
|
||||
import type { ContainerCreateOptions } from "dockerode";
|
||||
import { IS_CLOUD } from "../constants";
|
||||
import { findUserById } from "../services/admin";
|
||||
import { getDokployImageTag } from "../services/settings";
|
||||
import { pullImage, pullRemoteImage } from "../utils/docker/utils";
|
||||
import { execAsync, execAsyncRemote } from "../utils/process/execAsync";
|
||||
import { getRemoteDocker } from "../utils/servers/remote-docker";
|
||||
|
||||
export const setupMonitoring = async (serverId: string) => {
|
||||
const server = await findServerById(serverId);
|
||||
|
||||
const containerName = "dokploy-monitoring";
|
||||
let imageName = "dokploy/monitoring:latest";
|
||||
|
||||
if (
|
||||
(getDokployImageTag() !== "latest" ||
|
||||
process.env.NODE_ENV === "development") &&
|
||||
!IS_CLOUD
|
||||
) {
|
||||
imageName = "dokploy/monitoring:canary";
|
||||
}
|
||||
|
||||
const settings: ContainerCreateOptions = {
|
||||
name: containerName,
|
||||
Env: [`METRICS_CONFIG=${JSON.stringify(server?.metricsConfig)}`],
|
||||
Image: imageName,
|
||||
HostConfig: {
|
||||
// Memory: 100 * 1024 * 1024, // 100MB en bytes
|
||||
// PidMode: "host",
|
||||
// CapAdd: ["NET_ADMIN", "SYS_ADMIN"],
|
||||
// Privileged: true,
|
||||
PortBindings: {
|
||||
[`${server.metricsConfig.server.port}/tcp`]: [
|
||||
{
|
||||
HostPort: server.metricsConfig.server.port.toString(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Binds: [
|
||||
"/var/run/docker.sock:/var/run/docker.sock:ro",
|
||||
"/sys:/host/sys:ro",
|
||||
"/etc/os-release:/etc/os-release:ro",
|
||||
"/proc:/host/proc:ro",
|
||||
"/etc/dokploy/monitoring/monitoring.db:/app/monitoring.db",
|
||||
],
|
||||
NetworkMode: "host",
|
||||
},
|
||||
ExposedPorts: {
|
||||
[`${server.metricsConfig.server.port}/tcp`]: {},
|
||||
},
|
||||
};
|
||||
const docker = await getRemoteDocker(serverId);
|
||||
try {
|
||||
await execAsyncRemote(
|
||||
serverId,
|
||||
"mkdir -p /etc/dokploy/monitoring && touch /etc/dokploy/monitoring/monitoring.db",
|
||||
);
|
||||
if (serverId) {
|
||||
await pullRemoteImage(imageName, serverId);
|
||||
}
|
||||
|
||||
// Check if container exists
|
||||
const container = docker.getContainer(containerName);
|
||||
try {
|
||||
await container.inspect();
|
||||
await container.remove({ force: true });
|
||||
console.log("Removed existing container");
|
||||
} catch (_error) {
|
||||
// Container doesn't exist, continue
|
||||
}
|
||||
|
||||
await docker.createContainer(settings);
|
||||
const newContainer = docker.getContainer(containerName);
|
||||
await newContainer.start();
|
||||
|
||||
console.log("Monitoring Started ");
|
||||
} catch (error) {
|
||||
console.log("Monitoring Not Found: Starting ", error);
|
||||
}
|
||||
};
|
||||
|
||||
export const setupWebMonitoring = async (userId: string) => {
|
||||
const user = await findUserById(userId);
|
||||
|
||||
const containerName = "dokploy-monitoring";
|
||||
let imageName = "dokploy/monitoring:latest";
|
||||
|
||||
if (
|
||||
(getDokployImageTag() !== "latest" ||
|
||||
process.env.NODE_ENV === "development") &&
|
||||
!IS_CLOUD
|
||||
) {
|
||||
imageName = "dokploy/monitoring:canary";
|
||||
}
|
||||
|
||||
const settings: ContainerCreateOptions = {
|
||||
name: containerName,
|
||||
Env: [`METRICS_CONFIG=${JSON.stringify(user?.metricsConfig)}`],
|
||||
Image: imageName,
|
||||
HostConfig: {
|
||||
// Memory: 100 * 1024 * 1024, // 100MB en bytes
|
||||
// PidMode: "host",
|
||||
// CapAdd: ["NET_ADMIN", "SYS_ADMIN"],
|
||||
// Privileged: true,
|
||||
PortBindings: {
|
||||
[`${user?.metricsConfig?.server?.port}/tcp`]: [
|
||||
{
|
||||
HostPort: user?.metricsConfig?.server?.port.toString(),
|
||||
},
|
||||
],
|
||||
},
|
||||
Binds: [
|
||||
"/var/run/docker.sock:/var/run/docker.sock:ro",
|
||||
"/sys:/host/sys:ro",
|
||||
"/etc/os-release:/etc/os-release:ro",
|
||||
"/proc:/host/proc:ro",
|
||||
"/etc/dokploy/monitoring/monitoring.db:/app/monitoring.db",
|
||||
],
|
||||
// NetworkMode: "host",
|
||||
},
|
||||
ExposedPorts: {
|
||||
[`${user?.metricsConfig?.server?.port}/tcp`]: {},
|
||||
},
|
||||
};
|
||||
const docker = await getRemoteDocker();
|
||||
try {
|
||||
await execAsync(
|
||||
"mkdir -p /etc/dokploy/monitoring && touch /etc/dokploy/monitoring/monitoring.db",
|
||||
);
|
||||
await pullImage(imageName);
|
||||
|
||||
const container = docker.getContainer(containerName);
|
||||
try {
|
||||
await container.inspect();
|
||||
await container.remove({ force: true });
|
||||
console.log("Removed existing container");
|
||||
} catch (_error) {}
|
||||
|
||||
await docker.createContainer(settings);
|
||||
const newContainer = docker.getContainer(containerName);
|
||||
await newContainer.start();
|
||||
|
||||
console.log("Monitoring Started ");
|
||||
} catch (error) {
|
||||
console.log("Monitoring Not Found: Starting ", error);
|
||||
}
|
||||
};
|
||||
@@ -54,16 +54,10 @@ export const initializePostgres = async () => {
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
});
|
||||
|
||||
console.log("Postgres Started ✅");
|
||||
} catch (_) {
|
||||
try {
|
||||
await docker.createService(settings);
|
||||
} catch (error: any) {
|
||||
if (error?.statusCode !== 409) {
|
||||
throw error;
|
||||
}
|
||||
console.log("Postgres service already exists, continuing...");
|
||||
}
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
console.log("Postgres Not Found: Starting ✅");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -52,15 +52,8 @@ export const initializeRedis = async () => {
|
||||
...settings,
|
||||
});
|
||||
console.log("Redis Started ✅");
|
||||
} catch (_) {
|
||||
try {
|
||||
await docker.createService(settings);
|
||||
} catch (error: any) {
|
||||
if (error?.statusCode !== 409) {
|
||||
throw error;
|
||||
}
|
||||
console.log("Redis service already exists, continuing...");
|
||||
}
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
console.log("Redis Not Found: Starting ✅");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -89,7 +89,7 @@ export const serverAudit = async (serverId: string) => {
|
||||
.on("data", (data: string) => {
|
||||
output += data;
|
||||
})
|
||||
.stderr.on("data", (_data) => {});
|
||||
.stderr.on("data", (data) => {});
|
||||
});
|
||||
})
|
||||
.on("error", (err) => {
|
||||
|
||||
@@ -128,7 +128,7 @@ export const serverValidate = async (serverId: string) => {
|
||||
.on("data", (data: string) => {
|
||||
output += data;
|
||||
})
|
||||
.stderr.on("data", (_data) => {});
|
||||
.stderr.on("data", (data) => {});
|
||||
});
|
||||
})
|
||||
.on("error", (err) => {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const dockerSwarmInitialized = async () => {
|
||||
await docker.swarmInspect();
|
||||
|
||||
return true;
|
||||
} catch (_e) {
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -41,7 +41,7 @@ export const dockerNetworkInitialized = async () => {
|
||||
try {
|
||||
await docker.getNetwork("dokploy-network").inspect();
|
||||
return true;
|
||||
} catch (_e) {
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -68,6 +68,9 @@ export const initializeTraefik = async ({
|
||||
Replicas: 1,
|
||||
},
|
||||
},
|
||||
Labels: {
|
||||
"traefik.enable": "true",
|
||||
},
|
||||
EndpointSpec: {
|
||||
Ports: [
|
||||
{
|
||||
@@ -127,15 +130,8 @@ export const initializeTraefik = async ({
|
||||
});
|
||||
|
||||
console.log("Traefik Started ✅");
|
||||
} catch (_) {
|
||||
try {
|
||||
await docker.createService(settings);
|
||||
} catch (error: any) {
|
||||
if (error?.statusCode !== 409) {
|
||||
throw error;
|
||||
}
|
||||
console.log("Traefik service already exists, continuing...");
|
||||
}
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
console.log("Traefik Not Found: Starting ✅");
|
||||
}
|
||||
};
|
||||
@@ -193,12 +189,10 @@ export const getDefaultTraefikConfig = () => {
|
||||
: {
|
||||
swarm: {
|
||||
exposedByDefault: false,
|
||||
watch: true,
|
||||
watch: false,
|
||||
},
|
||||
docker: {
|
||||
exposedByDefault: false,
|
||||
watch: true,
|
||||
network: "dokploy-network",
|
||||
},
|
||||
}),
|
||||
file: {
|
||||
@@ -249,12 +243,10 @@ export const getDefaultServerTraefikConfig = () => {
|
||||
providers: {
|
||||
swarm: {
|
||||
exposedByDefault: false,
|
||||
watch: true,
|
||||
watch: false,
|
||||
},
|
||||
docker: {
|
||||
exposedByDefault: false,
|
||||
watch: true,
|
||||
network: "dokploy-network",
|
||||
},
|
||||
file: {
|
||||
directory: "/etc/dokploy/traefik/dynamic",
|
||||
|
||||
@@ -36,7 +36,7 @@ type AnyObj = Record<PropertyKey, unknown>;
|
||||
type ZodObj<T extends AnyObj> = {
|
||||
[key in keyof T]: z.ZodType<T[key]>;
|
||||
};
|
||||
const _zObject = <T extends AnyObj>(arg: ZodObj<T>) => z.object(arg);
|
||||
const zObject = <T extends AnyObj>(arg: ZodObj<T>) => z.object(arg);
|
||||
|
||||
// const goodDogScheme = zObject<UserWithPosts>({
|
||||
// // prueba: schema.selectDatabaseSchema,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { IS_CLOUD, paths } from "@dokploy/server/constants";
|
||||
import { updateAdmin } from "@dokploy/server/services/admin";
|
||||
import { type RotatingFileStream, createStream } from "rotating-file-stream";
|
||||
import { db } from "../../db";
|
||||
import { execAsync } from "../process/execAsync";
|
||||
import { findAdmin } from "@dokploy/server/services/admin";
|
||||
import { updateUser } from "@dokploy/server/services/user";
|
||||
|
||||
class LogRotationManager {
|
||||
private static instance: LogRotationManager;
|
||||
@@ -30,16 +30,17 @@ class LogRotationManager {
|
||||
}
|
||||
|
||||
private async getStateFromDB(): Promise<boolean> {
|
||||
const admin = await findAdmin();
|
||||
return admin?.user.enableLogRotation ?? false;
|
||||
const setting = await db.query.admins.findFirst({});
|
||||
return setting?.enableLogRotation ?? false;
|
||||
}
|
||||
|
||||
private async setStateInDB(active: boolean): Promise<void> {
|
||||
const admin = await findAdmin();
|
||||
const admin = await db.query.admins.findFirst({});
|
||||
|
||||
if (!admin) {
|
||||
return;
|
||||
}
|
||||
await updateUser(admin.user.id, {
|
||||
await updateAdmin(admin?.authId, {
|
||||
enableLogRotation: active,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { findAdmin } from "@dokploy/server/services/admin";
|
||||
import { getAllServers } from "@dokploy/server/services/server";
|
||||
import { scheduleJob } from "node-schedule";
|
||||
import { db } from "../../db/index";
|
||||
@@ -11,14 +12,13 @@ import { runMariadbBackup } from "./mariadb";
|
||||
import { runMongoBackup } from "./mongo";
|
||||
import { runMySqlBackup } from "./mysql";
|
||||
import { runPostgresBackup } from "./postgres";
|
||||
import { findAdmin } from "../../services/admin";
|
||||
|
||||
export const initCronJobs = async () => {
|
||||
console.log("Setting up cron jobs....");
|
||||
|
||||
const admin = await findAdmin();
|
||||
|
||||
if (admin?.user.enableDockerCleanup) {
|
||||
if (admin?.enableDockerCleanup) {
|
||||
scheduleJob("docker-cleanup", "0 0 * * *", async () => {
|
||||
console.log(
|
||||
`Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`,
|
||||
@@ -26,7 +26,7 @@ export const initCronJobs = async () => {
|
||||
await cleanUpUnusedImages();
|
||||
await cleanUpDockerBuilder();
|
||||
await cleanUpSystemPrune();
|
||||
await sendDockerCleanupNotifications(admin.user.id);
|
||||
await sendDockerCleanupNotifications(admin.adminId);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ export const initCronJobs = async () => {
|
||||
await cleanUpDockerBuilder(serverId);
|
||||
await cleanUpSystemPrune(serverId);
|
||||
await sendDockerCleanupNotifications(
|
||||
admin.user.id,
|
||||
admin.adminId,
|
||||
`Docker cleanup for Server ${name} (${serverId})`,
|
||||
);
|
||||
});
|
||||
|
||||
@@ -49,7 +49,7 @@ export const runMariadbBackup = async (
|
||||
projectName: project.name,
|
||||
databaseType: "mariadb",
|
||||
type: "success",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -60,7 +60,7 @@ export const runMariadbBackup = async (
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
projectName: project.name,
|
||||
databaseType: "mongodb",
|
||||
type: "success",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -57,7 +57,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { unlink } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import type { BackupSchedule } from "@dokploy/server/services/backup";
|
||||
import type { MySql } from "@dokploy/server/services/mysql";
|
||||
@@ -45,7 +46,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
projectName: project.name,
|
||||
databaseType: "mysql",
|
||||
type: "success",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -56,7 +57,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ export const runPostgresBackup = async (
|
||||
projectName: project.name,
|
||||
databaseType: "postgres",
|
||||
type: "success",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
} catch (error) {
|
||||
await sendDatabaseBackupNotifications({
|
||||
@@ -59,7 +59,7 @@ export const runPostgresBackup = async (
|
||||
type: "error",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error message not provided",
|
||||
organizationId: project.organizationId,
|
||||
adminId: project.adminId,
|
||||
});
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -28,7 +28,7 @@ export const removeScheduleBackup = (backupId: string) => {
|
||||
};
|
||||
|
||||
export const getS3Credentials = (destination: Destination) => {
|
||||
const { accessKey, secretAccessKey, region, endpoint, provider } =
|
||||
const { accessKey, secretAccessKey, bucket, region, endpoint, provider } =
|
||||
destination;
|
||||
const rcloneFlags = [
|
||||
`--s3-access-key-id=${accessKey}`,
|
||||
|
||||
@@ -12,12 +12,8 @@ import {
|
||||
writeDomainsToCompose,
|
||||
writeDomainsToComposeRemote,
|
||||
} from "../docker/domain";
|
||||
import {
|
||||
encodeBase64,
|
||||
getEnviromentVariablesObject,
|
||||
prepareEnvironmentVariables,
|
||||
} from "../docker/utils";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { encodeBase64, prepareEnvironmentVariables } from "../docker/utils";
|
||||
import { execAsyncRemote } from "../process/execAsync";
|
||||
import { spawnAsync } from "../process/spawnAsync";
|
||||
|
||||
export type ComposeNested = InferResultType<
|
||||
@@ -33,19 +29,13 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||
await writeDomainsToCompose(compose, domains);
|
||||
createEnvFile(compose);
|
||||
|
||||
if (compose.isolatedDeployment) {
|
||||
await execAsync(
|
||||
`docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}`,
|
||||
);
|
||||
}
|
||||
|
||||
const logContent = `
|
||||
App Name: ${appName}
|
||||
Build Compose 🐳
|
||||
Detected: ${mounts.length} mounts 📂
|
||||
Command: docker ${command}
|
||||
Source Type: docker ${sourceType} ✅
|
||||
Compose Type: ${composeType} ✅`;
|
||||
App Name: ${appName}
|
||||
Build Compose 🐳
|
||||
Detected: ${mounts.length} mounts 📂
|
||||
Command: docker ${command}
|
||||
Source Type: docker ${sourceType} ✅
|
||||
Compose Type: ${composeType} ✅`;
|
||||
const logBox = boxen(logContent, {
|
||||
padding: {
|
||||
left: 1,
|
||||
@@ -56,6 +46,7 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||
borderStyle: "double",
|
||||
});
|
||||
writeStream.write(`\n${logBox}\n`);
|
||||
|
||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||
|
||||
await spawnAsync(
|
||||
@@ -71,19 +62,10 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||
env: {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
PATH: process.env.PATH,
|
||||
...(composeType === "stack" && {
|
||||
...getEnviromentVariablesObject(compose.env, compose.project.env),
|
||||
}),
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (compose.isolatedDeployment) {
|
||||
await execAsync(
|
||||
`docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1`,
|
||||
).catch(() => {});
|
||||
}
|
||||
|
||||
writeStream.write("Docker Compose Deployed: ✅");
|
||||
} catch (error) {
|
||||
writeStream.write(`Error ❌ ${(error as Error).message}`);
|
||||
@@ -98,11 +80,11 @@ export const getBuildComposeCommand = async (
|
||||
logPath: string,
|
||||
) => {
|
||||
const { COMPOSE_PATH } = paths(true);
|
||||
const { sourceType, appName, mounts, composeType, domains } = compose;
|
||||
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
||||
compose;
|
||||
const command = createCommand(compose);
|
||||
const envCommand = getCreateEnvFileCommand(compose);
|
||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||
const exportEnvCommand = getExportEnvCommand(compose);
|
||||
|
||||
const newCompose = await writeDomainsToComposeRemote(
|
||||
compose,
|
||||
@@ -138,10 +120,7 @@ Compose Type: ${composeType} ✅`;
|
||||
|
||||
cd "${projectPath}";
|
||||
|
||||
${exportEnvCommand}
|
||||
${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""}
|
||||
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
||||
${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""}
|
||||
|
||||
echo "Docker Compose Deployed: ✅" >> "${logPath}"
|
||||
} || {
|
||||
@@ -165,6 +144,7 @@ const sanitizeCommand = (command: string) => {
|
||||
|
||||
export const createCommand = (compose: ComposeNested) => {
|
||||
const { composeType, appName, sourceType } = compose;
|
||||
|
||||
if (compose.command) {
|
||||
return `${sanitizeCommand(compose.command)}`;
|
||||
}
|
||||
@@ -239,17 +219,3 @@ touch ${envFilePath};
|
||||
echo "${encodedContent}" | base64 -d > "${envFilePath}";
|
||||
`;
|
||||
};
|
||||
|
||||
const getExportEnvCommand = (compose: ComposeNested) => {
|
||||
if (compose.composeType !== "stack") return "";
|
||||
|
||||
const envVars = getEnviromentVariablesObject(
|
||||
compose.env,
|
||||
compose.project.env,
|
||||
);
|
||||
const exports = Object.entries(envVars)
|
||||
.map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
|
||||
.join("\n");
|
||||
|
||||
return exports ? `\n# Export environment variables\n${exports}\n` : "";
|
||||
};
|
||||
|
||||
@@ -197,7 +197,7 @@ export const mechanizeDockerContainer = async (
|
||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||
},
|
||||
});
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -91,7 +91,7 @@ export const getNixpacksCommand = (
|
||||
application: ApplicationNested,
|
||||
logPath: string,
|
||||
) => {
|
||||
const { env, appName, publishDirectory } = application;
|
||||
const { env, appName, publishDirectory, serverId } = application;
|
||||
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const buildContainerId = `${appName}-${nanoid(10)}`;
|
||||
|
||||
@@ -98,7 +98,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
});
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -152,7 +152,7 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
});
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -104,7 +104,7 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
});
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -95,7 +95,7 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
version: Number.parseInt(inspect.Version.Index),
|
||||
...settings,
|
||||
});
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
await docker.createService(settings);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { findComposeById } from "@dokploy/server/services/compose";
|
||||
import { dump, load } from "js-yaml";
|
||||
import { addAppNameToAllServiceNames } from "./collision/root-network";
|
||||
import { generateRandomHash } from "./compose";
|
||||
import { addSuffixToAllVolumes } from "./compose/volume";
|
||||
import type { ComposeSpecification } from "./types";
|
||||
|
||||
export const addAppNameToPreventCollision = (
|
||||
composeData: ComposeSpecification,
|
||||
appName: string,
|
||||
): ComposeSpecification => {
|
||||
let updatedComposeData = { ...composeData };
|
||||
|
||||
updatedComposeData = addAppNameToAllServiceNames(updatedComposeData, appName);
|
||||
updatedComposeData = addSuffixToAllVolumes(updatedComposeData, appName);
|
||||
return updatedComposeData;
|
||||
};
|
||||
|
||||
export const randomizeIsolatedDeploymentComposeFile = async (
|
||||
composeId: string,
|
||||
suffix?: string,
|
||||
) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
const composeFile = compose.composeFile;
|
||||
const composeData = load(composeFile) as ComposeSpecification;
|
||||
|
||||
const randomSuffix = suffix || compose.appName || generateRandomHash();
|
||||
|
||||
const newComposeFile = addAppNameToPreventCollision(
|
||||
composeData,
|
||||
randomSuffix,
|
||||
);
|
||||
|
||||
return dump(newComposeFile);
|
||||
};
|
||||
|
||||
export const randomizeDeployableSpecificationFile = (
|
||||
composeSpec: ComposeSpecification,
|
||||
suffix?: string,
|
||||
) => {
|
||||
if (!suffix) {
|
||||
return composeSpec;
|
||||
}
|
||||
const newComposeFile = addAppNameToPreventCollision(composeSpec, suffix);
|
||||
return newComposeFile;
|
||||
};
|
||||
@@ -1,62 +0,0 @@
|
||||
import _ from "lodash";
|
||||
import type { ComposeSpecification, DefinitionsService } from "../types";
|
||||
|
||||
export const addAppNameToRootNetwork = (
|
||||
composeData: ComposeSpecification,
|
||||
appName: string,
|
||||
): ComposeSpecification => {
|
||||
const updatedComposeData = { ...composeData };
|
||||
|
||||
// Initialize networks if it doesn't exist
|
||||
if (!updatedComposeData.networks) {
|
||||
updatedComposeData.networks = {};
|
||||
}
|
||||
|
||||
// Add the new network with the app name
|
||||
updatedComposeData.networks[appName] = {
|
||||
name: appName,
|
||||
external: true,
|
||||
};
|
||||
|
||||
return updatedComposeData;
|
||||
};
|
||||
|
||||
export const addAppNameToServiceNetworks = (
|
||||
services: { [key: string]: DefinitionsService },
|
||||
appName: string,
|
||||
): { [key: string]: DefinitionsService } => {
|
||||
return _.mapValues(services, (service) => {
|
||||
if (!service.networks) {
|
||||
service.networks = [appName];
|
||||
return service;
|
||||
}
|
||||
|
||||
if (Array.isArray(service.networks)) {
|
||||
if (!service.networks.includes(appName)) {
|
||||
service.networks.push(appName);
|
||||
}
|
||||
} else {
|
||||
service.networks[appName] = {};
|
||||
}
|
||||
|
||||
return service;
|
||||
});
|
||||
};
|
||||
|
||||
export const addAppNameToAllServiceNames = (
|
||||
composeData: ComposeSpecification,
|
||||
appName: string,
|
||||
): ComposeSpecification => {
|
||||
let updatedComposeData = { ...composeData };
|
||||
|
||||
updatedComposeData = addAppNameToRootNetwork(updatedComposeData, appName);
|
||||
|
||||
if (updatedComposeData.services) {
|
||||
updatedComposeData.services = addAppNameToServiceNetworks(
|
||||
updatedComposeData.services,
|
||||
appName,
|
||||
);
|
||||
}
|
||||
|
||||
return updatedComposeData;
|
||||
};
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
createComposeFileRaw,
|
||||
createComposeFileRawRemote,
|
||||
} from "../providers/raw";
|
||||
import { randomizeDeployableSpecificationFile } from "./collision";
|
||||
import { randomizeSpecificationFile } from "./compose";
|
||||
import type {
|
||||
ComposeSpecification,
|
||||
@@ -109,7 +108,7 @@ export const loadDockerComposeRemote = async (
|
||||
if (!stdout) return null;
|
||||
const parsedConfig = load(stdout) as ComposeSpecification;
|
||||
return parsedConfig;
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
@@ -191,13 +190,7 @@ export const addDomainToCompose = async (
|
||||
return null;
|
||||
}
|
||||
|
||||
if (compose.isolatedDeployment) {
|
||||
const randomized = randomizeDeployableSpecificationFile(
|
||||
result,
|
||||
compose.suffix || compose.appName,
|
||||
);
|
||||
result = randomized;
|
||||
} else if (compose.randomize) {
|
||||
if (compose.randomize) {
|
||||
const randomized = randomizeSpecificationFile(result, compose.suffix);
|
||||
result = randomized;
|
||||
}
|
||||
@@ -210,6 +203,9 @@ export const addDomainToCompose = async (
|
||||
if (!result?.services?.[serviceName]) {
|
||||
throw new Error(`The service ${serviceName} not found in the compose`);
|
||||
}
|
||||
if (!result.services[serviceName].labels) {
|
||||
result.services[serviceName].labels = [];
|
||||
}
|
||||
|
||||
const httpLabels = await createDomainLabels(appName, domain, "web");
|
||||
if (https) {
|
||||
@@ -221,24 +217,7 @@ export const addDomainToCompose = async (
|
||||
httpLabels.push(...httpsLabels);
|
||||
}
|
||||
|
||||
let labels: DefinitionsService["labels"] = [];
|
||||
if (compose.composeType === "docker-compose") {
|
||||
if (!result.services[serviceName].labels) {
|
||||
result.services[serviceName].labels = [];
|
||||
}
|
||||
|
||||
labels = result.services[serviceName].labels;
|
||||
} else {
|
||||
// Stack Case
|
||||
if (!result.services[serviceName].deploy) {
|
||||
result.services[serviceName].deploy = {};
|
||||
}
|
||||
if (!result.services[serviceName].deploy.labels) {
|
||||
result.services[serviceName].deploy.labels = [];
|
||||
}
|
||||
|
||||
labels = result.services[serviceName].deploy.labels;
|
||||
}
|
||||
const labels = result.services[serviceName].labels;
|
||||
|
||||
if (Array.isArray(labels)) {
|
||||
if (!labels.includes("traefik.enable=true")) {
|
||||
@@ -247,18 +226,14 @@ export const addDomainToCompose = async (
|
||||
labels.push(...httpLabels);
|
||||
}
|
||||
|
||||
if (!compose.isolatedDeployment) {
|
||||
// Add the dokploy-network to the service
|
||||
result.services[serviceName].networks = addDokployNetworkToService(
|
||||
result.services[serviceName].networks,
|
||||
);
|
||||
}
|
||||
// Add the dokploy-network to the service
|
||||
result.services[serviceName].networks = addDokployNetworkToService(
|
||||
result.services[serviceName].networks,
|
||||
);
|
||||
}
|
||||
|
||||
// Add dokploy-network to the root of the compose file
|
||||
if (!compose.isolatedDeployment) {
|
||||
result.networks = addDokployNetworkToRoot(result.networks);
|
||||
}
|
||||
result.networks = addDokployNetworkToRoot(result.networks);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -100,7 +100,7 @@ export const containerExists = async (containerName: string) => {
|
||||
try {
|
||||
await container.inspect();
|
||||
return true;
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -144,11 +144,10 @@ export const getContainerByName = (name: string): Promise<ContainerInfo> => {
|
||||
};
|
||||
export const cleanUpUnusedImages = async (serverId?: string) => {
|
||||
try {
|
||||
const command = "docker image prune --force";
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
await execAsyncRemote(serverId, "docker image prune --all --force");
|
||||
} else {
|
||||
await execAsync(command);
|
||||
await execAsync("docker image prune --all --force");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -158,11 +157,10 @@ export const cleanUpUnusedImages = async (serverId?: string) => {
|
||||
|
||||
export const cleanStoppedContainers = async (serverId?: string) => {
|
||||
try {
|
||||
const command = "docker container prune --force";
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
await execAsyncRemote(serverId, "docker container prune --force");
|
||||
} else {
|
||||
await execAsync(command);
|
||||
await execAsync("docker container prune --force");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -172,11 +170,10 @@ export const cleanStoppedContainers = async (serverId?: string) => {
|
||||
|
||||
export const cleanUpUnusedVolumes = async (serverId?: string) => {
|
||||
try {
|
||||
const command = "docker volume prune --force";
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
await execAsyncRemote(serverId, "docker volume prune --all --force");
|
||||
} else {
|
||||
await execAsync(command);
|
||||
await execAsync("docker volume prune --all --force");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
@@ -202,20 +199,21 @@ export const cleanUpInactiveContainers = async () => {
|
||||
};
|
||||
|
||||
export const cleanUpDockerBuilder = async (serverId?: string) => {
|
||||
const command = "docker builder prune --all --force";
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
await execAsyncRemote(serverId, "docker builder prune --all --force");
|
||||
} else {
|
||||
await execAsync(command);
|
||||
await execAsync("docker builder prune --all --force");
|
||||
}
|
||||
};
|
||||
|
||||
export const cleanUpSystemPrune = async (serverId?: string) => {
|
||||
const command = "docker system prune --all --force --volumes";
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
await execAsyncRemote(
|
||||
serverId,
|
||||
"docker system prune --all --force --volumes",
|
||||
);
|
||||
} else {
|
||||
await execAsync(command);
|
||||
await execAsync("docker system prune --all --force --volumes");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -240,7 +238,7 @@ export const startServiceRemote = async (serverId: string, appName: string) => {
|
||||
export const removeService = async (
|
||||
appName: string,
|
||||
serverId?: string | null,
|
||||
_deleteVolumes = false,
|
||||
deleteVolumes = false,
|
||||
) => {
|
||||
try {
|
||||
const command = `docker service rm ${appName}`;
|
||||
@@ -278,15 +276,12 @@ export const prepareEnvironmentVariables = (
|
||||
return resolvedVars;
|
||||
};
|
||||
|
||||
export const getEnviromentVariablesObject = (
|
||||
input: string | null,
|
||||
projectEnv?: string | null,
|
||||
) => {
|
||||
const envs = prepareEnvironmentVariables(input, projectEnv);
|
||||
export const prepareBuildArgs = (input: string | null) => {
|
||||
const pairs = (input ?? "").split("\n");
|
||||
|
||||
const jsonObject: Record<string, string> = {};
|
||||
|
||||
for (const pair of envs) {
|
||||
for (const pair of pairs) {
|
||||
const [key, value] = pair.split("=");
|
||||
if (key && value) {
|
||||
jsonObject[key] = value;
|
||||
|
||||
@@ -34,7 +34,7 @@ export async function checkGPUStatus(serverId?: string): Promise<GPUInfo> {
|
||||
...gpuInfo,
|
||||
...cudaInfo,
|
||||
};
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
return {
|
||||
driverInstalled: false,
|
||||
driverVersion: undefined,
|
||||
@@ -315,7 +315,7 @@ const setupLocalServer = async (daemonConfig: any) => {
|
||||
|
||||
try {
|
||||
await execAsync(setupCommands);
|
||||
} catch (_error) {
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
"Failed to configure GPU support. Please ensure you have sudo privileges and try again.",
|
||||
);
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Props {
|
||||
applicationType: string;
|
||||
errorMessage: string;
|
||||
buildLink: string;
|
||||
organizationId: string;
|
||||
adminId: string;
|
||||
}
|
||||
|
||||
export const sendBuildErrorNotifications = async ({
|
||||
@@ -27,14 +27,14 @@ export const sendBuildErrorNotifications = async ({
|
||||
applicationType,
|
||||
errorMessage,
|
||||
buildLink,
|
||||
organizationId,
|
||||
adminId,
|
||||
}: Props) => {
|
||||
const date = new Date();
|
||||
const unixDate = ~~(Number(date) / 1000);
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: and(
|
||||
eq(notifications.appBuildError, true),
|
||||
eq(notifications.organizationId, organizationId),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
|
||||
@@ -18,7 +18,7 @@ interface Props {
|
||||
applicationName: string;
|
||||
applicationType: string;
|
||||
buildLink: string;
|
||||
organizationId: string;
|
||||
adminId: string;
|
||||
domains: Domain[];
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ export const sendBuildSuccessNotifications = async ({
|
||||
applicationName,
|
||||
applicationType,
|
||||
buildLink,
|
||||
organizationId,
|
||||
adminId,
|
||||
domains,
|
||||
}: Props) => {
|
||||
const date = new Date();
|
||||
@@ -35,7 +35,7 @@ export const sendBuildSuccessNotifications = async ({
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: and(
|
||||
eq(notifications.appDeploy, true),
|
||||
eq(notifications.organizationId, organizationId),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { error } from "node:console";
|
||||
import { db } from "@dokploy/server/db";
|
||||
import { notifications } from "@dokploy/server/db/schema";
|
||||
import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup";
|
||||
@@ -18,13 +19,13 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
databaseType,
|
||||
type,
|
||||
errorMessage,
|
||||
organizationId,
|
||||
adminId,
|
||||
}: {
|
||||
projectName: string;
|
||||
applicationName: string;
|
||||
databaseType: "postgres" | "mysql" | "mongodb" | "mariadb";
|
||||
type: "error" | "success";
|
||||
organizationId: string;
|
||||
adminId: string;
|
||||
errorMessage?: string;
|
||||
}) => {
|
||||
const date = new Date();
|
||||
@@ -32,7 +33,7 @@ export const sendDatabaseBackupNotifications = async ({
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: and(
|
||||
eq(notifications.databaseBackup, true),
|
||||
eq(notifications.organizationId, organizationId),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "./utils";
|
||||
|
||||
export const sendDockerCleanupNotifications = async (
|
||||
organizationId: string,
|
||||
adminId: string,
|
||||
message = "Docker cleanup for dokploy",
|
||||
) => {
|
||||
const date = new Date();
|
||||
@@ -21,7 +21,7 @@ export const sendDockerCleanupNotifications = async (
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: and(
|
||||
eq(notifications.dockerCleanup, true),
|
||||
eq(notifications.organizationId, organizationId),
|
||||
eq(notifications.adminId, adminId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import { db } from "../../db";
|
||||
import { notifications } from "../../db/schema";
|
||||
import {
|
||||
sendDiscordNotification,
|
||||
sendSlackNotification,
|
||||
sendTelegramNotification,
|
||||
} from "./utils";
|
||||
|
||||
interface ServerThresholdPayload {
|
||||
Type: "CPU" | "Memory";
|
||||
Value: number;
|
||||
Threshold: number;
|
||||
Message: string;
|
||||
Timestamp: string;
|
||||
Token: string;
|
||||
ServerName: string;
|
||||
}
|
||||
|
||||
export const sendServerThresholdNotifications = async (
|
||||
organizationId: string,
|
||||
payload: ServerThresholdPayload,
|
||||
) => {
|
||||
const date = new Date(payload.Timestamp);
|
||||
const unixDate = ~~(Number(date) / 1000);
|
||||
|
||||
const notificationList = await db.query.notifications.findMany({
|
||||
where: and(
|
||||
eq(notifications.serverThreshold, true),
|
||||
eq(notifications.organizationId, organizationId),
|
||||
),
|
||||
with: {
|
||||
email: true,
|
||||
discord: true,
|
||||
telegram: true,
|
||||
slack: true,
|
||||
},
|
||||
});
|
||||
|
||||
const typeEmoji = payload.Type === "CPU" ? "🔲" : "💾";
|
||||
const typeColor = 0xff0000; // Rojo para indicar alerta
|
||||
|
||||
for (const notification of notificationList) {
|
||||
const { discord, telegram, slack } = notification;
|
||||
|
||||
if (discord) {
|
||||
const decorate = (decoration: string, text: string) =>
|
||||
`${discord.decoration ? decoration : ""} ${text}`.trim();
|
||||
|
||||
await sendDiscordNotification(discord, {
|
||||
title: decorate(">", `\`⚠️\` Server ${payload.Type} Alert`),
|
||||
color: typeColor,
|
||||
fields: [
|
||||
{
|
||||
name: decorate("`🏷️`", "Server Name"),
|
||||
value: payload.ServerName,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: decorate("`📅`", "Date"),
|
||||
value: `<t:${unixDate}:D>`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: decorate("`⌚`", "Time"),
|
||||
value: `<t:${unixDate}:t>`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: decorate(typeEmoji, "Type"),
|
||||
value: payload.Type,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: decorate("📊", "Current Value"),
|
||||
value: `${payload.Value.toFixed(2)}%`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: decorate("⚠️", "Threshold"),
|
||||
value: `${payload.Threshold.toFixed(2)}%`,
|
||||
inline: true,
|
||||
},
|
||||
{
|
||||
name: decorate("`📜`", "Message"),
|
||||
value: `\`\`\`${payload.Message}\`\`\``,
|
||||
},
|
||||
],
|
||||
timestamp: date.toISOString(),
|
||||
footer: {
|
||||
text: "Dokploy Server Monitoring Alert",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (telegram) {
|
||||
await sendTelegramNotification(
|
||||
telegram,
|
||||
`
|
||||
<b>⚠️ Server ${payload.Type} Alert</b>
|
||||
<b>Server Name:</b> ${payload.ServerName}
|
||||
<b>Type:</b> ${payload.Type}
|
||||
<b>Current Value:</b> ${payload.Value.toFixed(2)}%
|
||||
<b>Threshold:</b> ${payload.Threshold.toFixed(2)}%
|
||||
<b>Message:</b> ${payload.Message}
|
||||
<b>Time:</b> ${date.toLocaleString()}
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
if (slack) {
|
||||
const { channel } = slack;
|
||||
await sendSlackNotification(slack, {
|
||||
channel: channel,
|
||||
attachments: [
|
||||
{
|
||||
color: "#FF0000",
|
||||
pretext: `:warning: *Server ${payload.Type} Alert*`,
|
||||
fields: [
|
||||
{
|
||||
title: "Server Name",
|
||||
value: payload.ServerName,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Type",
|
||||
value: payload.Type,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Current Value",
|
||||
value: `${payload.Value.toFixed(2)}%`,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Threshold",
|
||||
value: `${payload.Threshold.toFixed(2)}%`,
|
||||
short: true,
|
||||
},
|
||||
{
|
||||
title: "Message",
|
||||
value: payload.Message,
|
||||
},
|
||||
{
|
||||
title: "Time",
|
||||
value: date.toLocaleString(),
|
||||
short: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -68,7 +68,6 @@ export const sendTelegramNotification = async (
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
chat_id: connection.chatId,
|
||||
message_thread_id: connection.messageThreadId,
|
||||
text: messageText,
|
||||
parse_mode: "HTML",
|
||||
disable_web_page_preview: true,
|
||||
|
||||
@@ -27,7 +27,7 @@ export const execAsyncRemote = async (
|
||||
throw err;
|
||||
}
|
||||
stream
|
||||
.on("close", (code: number, _signal: string) => {
|
||||
.on("close", (code: number, signal: string) => {
|
||||
conn.end();
|
||||
if (code === 0) {
|
||||
resolve({ stdout, stderr });
|
||||
|
||||
@@ -176,6 +176,7 @@ export const getBitbucketCloneCommand = async (
|
||||
bitbucketBranch,
|
||||
bitbucketId,
|
||||
serverId,
|
||||
bitbucket,
|
||||
} = entity;
|
||||
|
||||
if (!serverId) {
|
||||
|
||||
@@ -69,7 +69,6 @@ export const cloneGitRepository = async (
|
||||
});
|
||||
}
|
||||
|
||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
||||
await spawnAsync(
|
||||
"git",
|
||||
[
|
||||
@@ -92,7 +91,7 @@ export const cloneGitRepository = async (
|
||||
env: {
|
||||
...process.env,
|
||||
...(customGitSSHKeyId && {
|
||||
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`,
|
||||
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -169,8 +168,7 @@ export const getCustomGitCloneCommand = async (
|
||||
);
|
||||
if (customGitSSHKeyId) {
|
||||
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
||||
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
|
||||
const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`;
|
||||
command.push(
|
||||
`
|
||||
echo "${sshKey.privateKey}" > /tmp/id_rsa
|
||||
@@ -306,7 +304,6 @@ export const cloneGitRawRepository = async (entity: {
|
||||
});
|
||||
}
|
||||
|
||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
||||
await spawnAsync(
|
||||
"git",
|
||||
[
|
||||
@@ -320,12 +317,12 @@ export const cloneGitRawRepository = async (entity: {
|
||||
outputPath,
|
||||
"--progress",
|
||||
],
|
||||
(_data) => {},
|
||||
(data) => {},
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
...(customGitSSHKeyId && {
|
||||
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`,
|
||||
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath} -o UserKnownHostsFile=${knownHostsPath}`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -384,8 +381,7 @@ export const cloneRawGitRepositoryRemote = async (compose: Compose) => {
|
||||
command.push(`mkdir -p ${outputPath};`);
|
||||
if (customGitSSHKeyId) {
|
||||
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
||||
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
|
||||
const gitSshCommand = `ssh -i /tmp/id_rsa -o UserKnownHostsFile=${knownHostsPath}`;
|
||||
command.push(
|
||||
`
|
||||
echo "${sshKey.privateKey}" > /tmp/id_rsa
|
||||
|
||||
@@ -162,6 +162,8 @@ export const getGitlabCloneCommand = async (
|
||||
) => {
|
||||
const {
|
||||
appName,
|
||||
gitlabRepository,
|
||||
gitlabOwner,
|
||||
gitlabPathNamespace,
|
||||
gitlabBranch,
|
||||
gitlabId,
|
||||
@@ -266,7 +268,7 @@ export const getGitlabRepositories = async (gitlabId?: string) => {
|
||||
if (groupName) {
|
||||
return full_path.toLowerCase().includes(groupName) && kind === "group";
|
||||
}
|
||||
return kind === "member";
|
||||
return kind === "user";
|
||||
});
|
||||
const mappedRepositories = filteredRepos.map((repo: any) => {
|
||||
return {
|
||||
@@ -326,7 +328,14 @@ export const getGitlabBranches = async (input: {
|
||||
};
|
||||
|
||||
export const cloneRawGitlabRepository = async (entity: Compose) => {
|
||||
const { appName, gitlabBranch, gitlabId, gitlabPathNamespace } = entity;
|
||||
const {
|
||||
appName,
|
||||
gitlabRepository,
|
||||
gitlabOwner,
|
||||
gitlabBranch,
|
||||
gitlabId,
|
||||
gitlabPathNamespace,
|
||||
} = entity;
|
||||
|
||||
if (!gitlabId) {
|
||||
throw new TRPCError({
|
||||
@@ -433,7 +442,7 @@ export const testGitlabConnection = async (
|
||||
if (groupName) {
|
||||
return full_path.toLowerCase().includes(groupName) && kind === "group";
|
||||
}
|
||||
return kind === "member";
|
||||
return kind === "user";
|
||||
});
|
||||
|
||||
return filteredRepos.length;
|
||||
|
||||
@@ -67,7 +67,7 @@ export const removeTraefikConfig = async (
|
||||
if (fs.existsSync(configPath)) {
|
||||
await fs.promises.unlink(configPath);
|
||||
}
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const removeTraefikConfigRemote = async (
|
||||
@@ -78,7 +78,7 @@ export const removeTraefikConfigRemote = async (
|
||||
const { DYNAMIC_TRAEFIK_PATH } = paths(true);
|
||||
const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`);
|
||||
await execAsyncRemote(serverId, `rm ${configPath}`);
|
||||
} catch (_error) {}
|
||||
} catch (error) {}
|
||||
};
|
||||
|
||||
export const loadOrCreateConfig = (appName: string): FileConfig => {
|
||||
@@ -110,7 +110,7 @@ export const loadOrCreateConfigRemote = async (
|
||||
http: { routers: {}, services: {} },
|
||||
};
|
||||
return parsedConfig;
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
return fileConfig;
|
||||
}
|
||||
};
|
||||
@@ -132,7 +132,7 @@ export const readRemoteConfig = async (serverId: string, appName: string) => {
|
||||
const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`);
|
||||
if (!stdout) return null;
|
||||
return stdout;
|
||||
} catch (_err) {
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user