mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-08 07:25:22 +02:00
Merge branch 'canary' into feat/ntfy
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
import { defineConfig } from "drizzle-kit";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "./server/db/schema/index.ts",
|
||||
dialect: "postgresql",
|
||||
dbCredentials: {
|
||||
url: process.env.DATABASE_URL!,
|
||||
},
|
||||
out: "drizzle",
|
||||
migrations: {
|
||||
table: "migrations",
|
||||
schema: "public",
|
||||
},
|
||||
});
|
||||
@@ -1,21 +0,0 @@
|
||||
// import { drizzle } from "drizzle-orm/postgres-js";
|
||||
// import { migrate } from "drizzle-orm/postgres-js/migrator";
|
||||
// import postgres from "postgres";
|
||||
|
||||
// const connectionString = process.env.DATABASE_URL!;
|
||||
|
||||
// const sql = postgres(connectionString, { max: 1 });
|
||||
// const db = drizzle(sql);
|
||||
|
||||
// export const migration = async () =>
|
||||
// await migrate(db, { migrationsFolder: "drizzle" })
|
||||
// .then(() => {
|
||||
// console.log("Migration complete");
|
||||
// sql.end();
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// console.log("Migration failed", error);
|
||||
// })
|
||||
// .finally(() => {
|
||||
// sql.end();
|
||||
// });
|
||||
@@ -1,23 +0,0 @@
|
||||
import { sql } from "drizzle-orm";
|
||||
// Credits to Louistiti from Drizzle Discord: https://discord.com/channels/1043890932593987624/1130802621750448160/1143083373535973406
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import postgres from "postgres";
|
||||
|
||||
const connectionString = process.env.DATABASE_URL!;
|
||||
|
||||
const pg = postgres(connectionString, { max: 1 });
|
||||
const db = drizzle(pg);
|
||||
|
||||
const clearDb = async (): Promise<void> => {
|
||||
try {
|
||||
const tablesQuery = sql<string>`DROP SCHEMA public CASCADE; CREATE SCHEMA public; DROP schema drizzle CASCADE;`;
|
||||
const tables = await db.execute(tablesQuery);
|
||||
console.log(tables);
|
||||
await pg.end();
|
||||
} catch (error) {
|
||||
console.error("Error cleaning database", error);
|
||||
} finally {
|
||||
}
|
||||
};
|
||||
|
||||
clearDb();
|
||||
@@ -112,6 +112,10 @@ export const member = pgTable("member", {
|
||||
.array()
|
||||
.notNull()
|
||||
.default(sql`ARRAY[]::text[]`),
|
||||
accessedEnvironments: text("accessedEnvironments")
|
||||
.array()
|
||||
.notNull()
|
||||
.default(sql`ARRAY[]::text[]`),
|
||||
accessedServices: text("accesedServices")
|
||||
.array()
|
||||
.notNull()
|
||||
|
||||
@@ -55,7 +55,7 @@ export const apiUpdateAi = createSchema
|
||||
.omit({ organizationId: true });
|
||||
|
||||
export const deploySuggestionSchema = z.object({
|
||||
projectId: z.string().min(1),
|
||||
environmentId: z.string().min(1),
|
||||
id: z.string().min(1),
|
||||
dockerCompose: z.string().min(1),
|
||||
envVariables: z.string(),
|
||||
|
||||
@@ -13,6 +13,7 @@ import { z } from "zod";
|
||||
import { bitbucket } from "./bitbucket";
|
||||
import { deployments } from "./deployment";
|
||||
import { domains } from "./domain";
|
||||
import { environments } from "./environment";
|
||||
import { gitea } from "./gitea";
|
||||
import { github } from "./github";
|
||||
import { gitlab } from "./gitlab";
|
||||
@@ -179,9 +180,9 @@ export const applications = pgTable("application", {
|
||||
registryId: text("registryId").references(() => registry.registryId, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
projectId: text("projectId")
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
githubId: text("githubId").references(() => github.githubId, {
|
||||
onDelete: "set null",
|
||||
}),
|
||||
@@ -202,9 +203,9 @@ export const applications = pgTable("application", {
|
||||
export const applicationsRelations = relations(
|
||||
applications,
|
||||
({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [applications.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [applications.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
deployments: many(deployments),
|
||||
customGitSSHKey: one(sshKeys, {
|
||||
@@ -273,7 +274,7 @@ const createSchema = createInsertSchema(applications, {
|
||||
customGitBuildPath: z.string().optional(),
|
||||
customGitUrl: z.string().optional(),
|
||||
buildPath: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
sourceType: z
|
||||
.enum(["github", "docker", "git", "gitlab", "bitbucket", "gitea", "drop"])
|
||||
.optional(),
|
||||
@@ -317,7 +318,7 @@ export const apiCreateApplication = createSchema.pick({
|
||||
name: true,
|
||||
appName: true,
|
||||
description: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
serverId: true,
|
||||
});
|
||||
|
||||
@@ -327,6 +328,26 @@ export const apiFindOneApplication = createSchema
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiDeployApplication = createSchema
|
||||
.pick({
|
||||
applicationId: true,
|
||||
})
|
||||
.extend({
|
||||
applicationId: z.string().min(1),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiRedeployApplication = createSchema
|
||||
.pick({
|
||||
applicationId: true,
|
||||
})
|
||||
.extend({
|
||||
applicationId: z.string().min(1),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiReloadApplication = createSchema
|
||||
.pick({
|
||||
appName: true,
|
||||
|
||||
@@ -7,6 +7,7 @@ import { backups } from "./backups";
|
||||
import { bitbucket } from "./bitbucket";
|
||||
import { deployments } from "./deployment";
|
||||
import { domains } from "./domain";
|
||||
import { environments } from "./environment";
|
||||
import { gitea } from "./gitea";
|
||||
import { github } from "./github";
|
||||
import { gitlab } from "./gitlab";
|
||||
@@ -84,9 +85,9 @@ export const compose = pgTable("compose", {
|
||||
.default(false),
|
||||
triggerType: triggerType("triggerType").default("push"),
|
||||
composeStatus: applicationStatus("composeStatus").notNull().default("idle"),
|
||||
projectId: text("projectId")
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
@@ -109,9 +110,9 @@ export const compose = pgTable("compose", {
|
||||
});
|
||||
|
||||
export const composeRelations = relations(compose, ({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [compose.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [compose.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
deployments: many(deployments),
|
||||
mounts: many(mounts),
|
||||
@@ -149,7 +150,7 @@ const createSchema = createInsertSchema(compose, {
|
||||
description: z.string(),
|
||||
env: z.string().optional(),
|
||||
composeFile: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
customGitSSHKeyId: z.string().optional(),
|
||||
command: z.string().optional(),
|
||||
composePath: z.string().min(1),
|
||||
@@ -160,7 +161,7 @@ const createSchema = createInsertSchema(compose, {
|
||||
export const apiCreateCompose = createSchema.pick({
|
||||
name: true,
|
||||
description: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
composeType: true,
|
||||
appName: true,
|
||||
serverId: true,
|
||||
@@ -169,7 +170,7 @@ export const apiCreateCompose = createSchema.pick({
|
||||
|
||||
export const apiCreateComposeByTemplate = createSchema
|
||||
.pick({
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
})
|
||||
.extend({
|
||||
id: z.string().min(1),
|
||||
@@ -180,6 +181,18 @@ export const apiFindCompose = z.object({
|
||||
composeId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiDeployCompose = z.object({
|
||||
composeId: z.string().min(1),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiRedeployCompose = z.object({
|
||||
composeId: z.string().min(1),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiDeleteCompose = z.object({
|
||||
composeId: z.string().min(1),
|
||||
deleteVolumes: z.boolean(),
|
||||
|
||||
85
packages/server/src/db/schema/environment.ts
Normal file
85
packages/server/src/db/schema/environment.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
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 { applications } from "./application";
|
||||
import { compose } from "./compose";
|
||||
import { mariadb } from "./mariadb";
|
||||
import { mongo } from "./mongo";
|
||||
import { mysql } from "./mysql";
|
||||
import { postgres } from "./postgres";
|
||||
import { projects } from "./project";
|
||||
import { redis } from "./redis";
|
||||
|
||||
export const environments = pgTable("environment", {
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.primaryKey()
|
||||
.$defaultFn(() => nanoid()),
|
||||
name: text("name").notNull(),
|
||||
description: text("description"),
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
env: text("env").notNull().default(""),
|
||||
projectId: text("projectId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
});
|
||||
|
||||
export const environmentRelations = relations(
|
||||
environments,
|
||||
({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [environments.projectId],
|
||||
references: [projects.projectId],
|
||||
}),
|
||||
applications: many(applications),
|
||||
mariadb: many(mariadb),
|
||||
postgres: many(postgres),
|
||||
mysql: many(mysql),
|
||||
redis: many(redis),
|
||||
mongo: many(mongo),
|
||||
compose: many(compose),
|
||||
}),
|
||||
);
|
||||
|
||||
const createSchema = createInsertSchema(environments, {
|
||||
environmentId: z.string().min(1),
|
||||
name: z.string().min(1),
|
||||
description: z.string().optional(),
|
||||
});
|
||||
|
||||
export const apiCreateEnvironment = createSchema.pick({
|
||||
name: true,
|
||||
description: true,
|
||||
projectId: true,
|
||||
});
|
||||
|
||||
export const apiFindOneEnvironment = createSchema
|
||||
.pick({
|
||||
environmentId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiRemoveEnvironment = createSchema
|
||||
.pick({
|
||||
environmentId: true,
|
||||
})
|
||||
.required();
|
||||
|
||||
export const apiUpdateEnvironment = createSchema.partial().extend({
|
||||
environmentId: z.string().min(1),
|
||||
});
|
||||
|
||||
export const apiDuplicateEnvironment = createSchema
|
||||
.pick({
|
||||
environmentId: true,
|
||||
name: true,
|
||||
description: true,
|
||||
})
|
||||
.required({
|
||||
environmentId: true,
|
||||
name: true,
|
||||
});
|
||||
@@ -8,6 +8,7 @@ export * from "./compose";
|
||||
export * from "./deployment";
|
||||
export * from "./destination";
|
||||
export * from "./domain";
|
||||
export * from "./environment";
|
||||
export * from "./git-provider";
|
||||
export * from "./gitea";
|
||||
export * from "./github";
|
||||
|
||||
@@ -4,8 +4,8 @@ import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { backups } from "./backups";
|
||||
import { environments } from "./environment";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import {
|
||||
applicationStatus,
|
||||
@@ -66,18 +66,19 @@ export const mariadb = pgTable("mariadb", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
projectId: text("projectId")
|
||||
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const mariadbRelations = relations(mariadb, ({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [mariadb.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [mariadb.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
backups: many(backups),
|
||||
mounts: many(mounts),
|
||||
@@ -94,8 +95,19 @@ const createSchema = createInsertSchema(mariadb, {
|
||||
createdAt: z.string(),
|
||||
databaseName: z.string().min(1),
|
||||
databaseUser: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databaseRootPassword: z.string().optional(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseRootPassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
})
|
||||
.optional(),
|
||||
dockerImage: z.string().default("mariadb:6"),
|
||||
command: z.string().optional(),
|
||||
env: z.string().optional(),
|
||||
@@ -103,7 +115,7 @@ const createSchema = createInsertSchema(mariadb, {
|
||||
memoryLimit: z.string().optional(),
|
||||
cpuReservation: z.string().optional(),
|
||||
cpuLimit: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
applicationStatus: z.enum(["idle", "running", "done", "error"]),
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
@@ -124,7 +136,7 @@ export const apiCreateMariaDB = createSchema
|
||||
appName: true,
|
||||
dockerImage: true,
|
||||
databaseRootPassword: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
description: true,
|
||||
databaseName: true,
|
||||
databaseUser: true,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { backups } from "./backups";
|
||||
import { environments } from "./environment";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import {
|
||||
applicationStatus,
|
||||
@@ -62,9 +62,10 @@ export const mongo = pgTable("mongo", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
projectId: text("projectId")
|
||||
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
@@ -72,9 +73,9 @@ export const mongo = pgTable("mongo", {
|
||||
});
|
||||
|
||||
export const mongoRelations = relations(mongo, ({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [mongo.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [mongo.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
backups: many(backups),
|
||||
mounts: many(mounts),
|
||||
@@ -89,7 +90,12 @@ const createSchema = createInsertSchema(mongo, {
|
||||
createdAt: z.string(),
|
||||
mongoId: z.string(),
|
||||
name: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseUser: z.string().min(1),
|
||||
dockerImage: z.string().default("mongo:15"),
|
||||
command: z.string().optional(),
|
||||
@@ -98,7 +104,7 @@ const createSchema = createInsertSchema(mongo, {
|
||||
memoryLimit: z.string().optional(),
|
||||
cpuReservation: z.string().optional(),
|
||||
cpuLimit: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
applicationStatus: z.enum(["idle", "running", "done", "error"]),
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
@@ -119,7 +125,7 @@ export const apiCreateMongo = createSchema
|
||||
name: true,
|
||||
appName: true,
|
||||
dockerImage: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
description: true,
|
||||
databaseUser: true,
|
||||
databasePassword: true,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { backups } from "./backups";
|
||||
import { environments } from "./environment";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import {
|
||||
applicationStatus,
|
||||
@@ -64,18 +64,19 @@ export const mysql = pgTable("mysql", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
projectId: text("projectId")
|
||||
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const mysqlRelations = relations(mysql, ({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [mysql.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [mysql.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
backups: many(backups),
|
||||
mounts: many(mounts),
|
||||
@@ -92,8 +93,19 @@ const createSchema = createInsertSchema(mysql, {
|
||||
name: z.string().min(1),
|
||||
databaseName: z.string().min(1),
|
||||
databaseUser: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databaseRootPassword: z.string().optional(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseRootPassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
})
|
||||
.optional(),
|
||||
dockerImage: z.string().default("mysql:8"),
|
||||
command: z.string().optional(),
|
||||
env: z.string().optional(),
|
||||
@@ -101,7 +113,6 @@ const createSchema = createInsertSchema(mysql, {
|
||||
memoryLimit: z.string().optional(),
|
||||
cpuReservation: z.string().optional(),
|
||||
cpuLimit: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
applicationStatus: z.enum(["idle", "running", "done", "error"]),
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
@@ -121,7 +132,7 @@ export const apiCreateMySql = createSchema
|
||||
name: true,
|
||||
appName: true,
|
||||
dockerImage: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
description: true,
|
||||
databaseName: true,
|
||||
databaseUser: true,
|
||||
|
||||
@@ -4,8 +4,8 @@ import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { backups } from "./backups";
|
||||
import { environments } from "./environment";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import {
|
||||
applicationStatus,
|
||||
@@ -64,18 +64,19 @@ export const postgres = pgTable("postgres", {
|
||||
createdAt: text("createdAt")
|
||||
.notNull()
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
projectId: text("projectId")
|
||||
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const postgresRelations = relations(postgres, ({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [postgres.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [postgres.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
backups: many(backups),
|
||||
mounts: many(mounts),
|
||||
@@ -88,7 +89,12 @@ export const postgresRelations = relations(postgres, ({ one, many }) => ({
|
||||
const createSchema = createInsertSchema(postgres, {
|
||||
postgresId: z.string(),
|
||||
name: z.string().min(1),
|
||||
databasePassword: z.string(),
|
||||
databasePassword: z
|
||||
.string()
|
||||
.regex(/^[a-zA-Z0-9@#%^&*()_+\-=[\]{}|;:,.<>?~`]*$/, {
|
||||
message:
|
||||
"Password contains invalid characters. Please avoid: $ ! ' \" \\ / and space characters for database compatibility",
|
||||
}),
|
||||
databaseName: z.string().min(1),
|
||||
databaseUser: z.string().min(1),
|
||||
dockerImage: z.string().default("postgres:15"),
|
||||
@@ -98,7 +104,7 @@ const createSchema = createInsertSchema(postgres, {
|
||||
memoryLimit: z.string().optional(),
|
||||
cpuReservation: z.string().optional(),
|
||||
cpuLimit: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
applicationStatus: z.enum(["idle", "running", "done", "error"]),
|
||||
externalPort: z.number(),
|
||||
createdAt: z.string(),
|
||||
@@ -122,7 +128,7 @@ export const apiCreatePostgres = createSchema
|
||||
databaseUser: true,
|
||||
databasePassword: true,
|
||||
dockerImage: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
})
|
||||
|
||||
@@ -4,13 +4,7 @@ import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { organization } from "./account";
|
||||
import { applications } from "./application";
|
||||
import { compose } from "./compose";
|
||||
import { mariadb } from "./mariadb";
|
||||
import { mongo } from "./mongo";
|
||||
import { mysql } from "./mysql";
|
||||
import { postgres } from "./postgres";
|
||||
import { redis } from "./redis";
|
||||
import { environments } from "./environment";
|
||||
|
||||
export const projects = pgTable("project", {
|
||||
projectId: text("projectId")
|
||||
@@ -30,13 +24,7 @@ export const projects = pgTable("project", {
|
||||
});
|
||||
|
||||
export const projectRelations = relations(projects, ({ many, one }) => ({
|
||||
mysql: many(mysql),
|
||||
postgres: many(postgres),
|
||||
mariadb: many(mariadb),
|
||||
applications: many(applications),
|
||||
mongo: many(mongo),
|
||||
redis: many(redis),
|
||||
compose: many(compose),
|
||||
environments: many(environments),
|
||||
organization: one(organization, {
|
||||
fields: [projects.organizationId],
|
||||
references: [organization.id],
|
||||
|
||||
@@ -3,6 +3,7 @@ import { integer, json, pgTable, text } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { nanoid } from "nanoid";
|
||||
import { z } from "zod";
|
||||
import { environments } from "./environment";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
@@ -60,18 +61,19 @@ export const redis = pgTable("redis", {
|
||||
labelsSwarm: json("labelsSwarm").$type<LabelsSwarm>(),
|
||||
networkSwarm: json("networkSwarm").$type<NetworkSwarm[]>(),
|
||||
replicas: integer("replicas").default(1).notNull(),
|
||||
projectId: text("projectId")
|
||||
|
||||
environmentId: text("environmentId")
|
||||
.notNull()
|
||||
.references(() => projects.projectId, { onDelete: "cascade" }),
|
||||
.references(() => environments.environmentId, { onDelete: "cascade" }),
|
||||
serverId: text("serverId").references(() => server.serverId, {
|
||||
onDelete: "cascade",
|
||||
}),
|
||||
});
|
||||
|
||||
export const redisRelations = relations(redis, ({ one, many }) => ({
|
||||
project: one(projects, {
|
||||
fields: [redis.projectId],
|
||||
references: [projects.projectId],
|
||||
environment: one(environments, {
|
||||
fields: [redis.environmentId],
|
||||
references: [environments.environmentId],
|
||||
}),
|
||||
mounts: many(mounts),
|
||||
server: one(server, {
|
||||
@@ -93,7 +95,7 @@ const createSchema = createInsertSchema(redis, {
|
||||
memoryLimit: z.string().optional(),
|
||||
cpuReservation: z.string().optional(),
|
||||
cpuLimit: z.string().optional(),
|
||||
projectId: z.string(),
|
||||
environmentId: z.string(),
|
||||
applicationStatus: z.enum(["idle", "running", "done", "error"]),
|
||||
externalPort: z.number(),
|
||||
description: z.string().optional(),
|
||||
@@ -114,7 +116,7 @@ export const apiCreateRedis = createSchema
|
||||
appName: true,
|
||||
databasePassword: true,
|
||||
dockerImage: true,
|
||||
projectId: true,
|
||||
environmentId: true,
|
||||
description: true,
|
||||
serverId: true,
|
||||
})
|
||||
|
||||
@@ -27,7 +27,9 @@ export const rollbacks = pgTable("rollback", {
|
||||
.$defaultFn(() => new Date().toISOString()),
|
||||
fullContext: jsonb("fullContext").$type<
|
||||
Application & {
|
||||
project: Project;
|
||||
environment: {
|
||||
project: Project;
|
||||
};
|
||||
mounts: Mount[];
|
||||
ports: Port[];
|
||||
registry?: Registry | null;
|
||||
|
||||
@@ -175,6 +175,7 @@ export const apiAssignPermissions = createSchema
|
||||
})
|
||||
.extend({
|
||||
accessedProjects: z.array(z.string()).optional(),
|
||||
accessedEnvironments: z.array(z.string()).optional(),
|
||||
accessedServices: z.array(z.string()).optional(),
|
||||
canCreateProjects: z.boolean().optional(),
|
||||
canCreateServices: z.boolean().optional(),
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
// import bc from "bcrypt";
|
||||
// import { drizzle } from "drizzle-orm/postgres-js";
|
||||
// import postgres from "postgres";
|
||||
// import { users } from "./schema";
|
||||
|
||||
// const connectionString = process.env.DATABASE_URL!;
|
||||
|
||||
// const pg = postgres(connectionString, { max: 1 });
|
||||
// const db = drizzle(pg);
|
||||
|
||||
// function password(txt: string) {
|
||||
// return bc.hashSync(txt, 10);
|
||||
// }
|
||||
|
||||
// async function seed() {
|
||||
// console.log("> Seed:", process.env.DATABASE_PATH, "\n");
|
||||
|
||||
// // const authenticationR = await db
|
||||
// // .insert(users)
|
||||
// // .values([
|
||||
// // {
|
||||
// // email: "user1@hotmail.com",
|
||||
// // password: password("12345671"),
|
||||
// // },
|
||||
// // ])
|
||||
// // .onConflictDoNothing()
|
||||
// // .returning();
|
||||
|
||||
// // console.log("\nSemillas Update:", authenticationR.length);
|
||||
// }
|
||||
|
||||
// seed().catch((e) => {
|
||||
// console.error(e);
|
||||
// process.exit(1);
|
||||
// });
|
||||
@@ -16,6 +16,7 @@ export * from "./services/deployment";
|
||||
export * from "./services/destination";
|
||||
export * from "./services/docker";
|
||||
export * from "./services/domain";
|
||||
export * from "./services/environment";
|
||||
export * from "./services/git-provider";
|
||||
export * from "./services/gitea";
|
||||
export * from "./services/github";
|
||||
|
||||
@@ -105,7 +105,11 @@ export const findApplicationById = async (applicationId: string) => {
|
||||
const application = await db.query.applications.findFirst({
|
||||
where: eq(applications.applicationId, applicationId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
domains: true,
|
||||
deployments: true,
|
||||
mounts: true,
|
||||
@@ -180,7 +184,7 @@ export const deployApplication = async ({
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`;
|
||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.environment.projectId}/environment/${application.environmentId}/services/application/${application.applicationId}?tab=deployments`;
|
||||
const deployment = await createDeployment({
|
||||
applicationId: applicationId,
|
||||
title: titleLog,
|
||||
@@ -227,11 +231,11 @@ export const deployApplication = async ({
|
||||
}
|
||||
|
||||
await sendBuildSuccessNotifications({
|
||||
projectName: application.project.name,
|
||||
projectName: application.environment.project.name,
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
organizationId: application.environment.project.organizationId,
|
||||
domains: application.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -239,13 +243,13 @@ export const deployApplication = async ({
|
||||
await updateApplicationStatus(applicationId, "error");
|
||||
|
||||
await sendBuildErrorNotifications({
|
||||
projectName: application.project.name,
|
||||
projectName: application.environment.project.name,
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
organizationId: application.environment.project.organizationId,
|
||||
});
|
||||
|
||||
throw error;
|
||||
@@ -307,7 +311,7 @@ export const deployRemoteApplication = async ({
|
||||
}) => {
|
||||
const application = await findApplicationById(applicationId);
|
||||
|
||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.projectId}/services/application/${application.applicationId}?tab=deployments`;
|
||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.environment.projectId}/environment/${application.environmentId}/services/application/${application.applicationId}?tab=deployments`;
|
||||
const deployment = await createDeployment({
|
||||
applicationId: applicationId,
|
||||
title: titleLog,
|
||||
@@ -363,11 +367,11 @@ export const deployRemoteApplication = async ({
|
||||
}
|
||||
|
||||
await sendBuildSuccessNotifications({
|
||||
projectName: application.project.name,
|
||||
projectName: application.environment.project.name,
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
organizationId: application.environment.project.organizationId,
|
||||
domains: application.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -387,12 +391,12 @@ export const deployRemoteApplication = async ({
|
||||
await updateApplicationStatus(applicationId, "error");
|
||||
|
||||
await sendBuildErrorNotifications({
|
||||
projectName: application.project.name,
|
||||
projectName: application.environment.project.name,
|
||||
applicationName: application.name,
|
||||
applicationType: "application",
|
||||
errorMessage: `Please check the logs for details: ${errorMessage}`,
|
||||
buildLink,
|
||||
organizationId: application.project.organizationId,
|
||||
organizationId: application.environment.project.organizationId,
|
||||
});
|
||||
|
||||
throw error;
|
||||
|
||||
@@ -126,7 +126,11 @@ export const findComposeById = async (composeId: string) => {
|
||||
const result = await db.query.compose.findFirst({
|
||||
where: eq(compose.composeId, composeId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
deployments: true,
|
||||
mounts: true,
|
||||
domains: true,
|
||||
@@ -222,7 +226,7 @@ export const deployCompose = async ({
|
||||
const compose = await findComposeById(composeId);
|
||||
|
||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${
|
||||
compose.projectId
|
||||
compose.environment.projectId
|
||||
}/services/compose/${compose.composeId}?tab=deployments`;
|
||||
const deployment = await createDeploymentCompose({
|
||||
composeId: composeId,
|
||||
@@ -255,11 +259,11 @@ export const deployCompose = async ({
|
||||
});
|
||||
|
||||
await sendBuildSuccessNotifications({
|
||||
projectName: compose.project.name,
|
||||
projectName: compose.environment.project.name,
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
organizationId: compose.environment.project.organizationId,
|
||||
domains: compose.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -268,13 +272,13 @@ export const deployCompose = async ({
|
||||
composeStatus: "error",
|
||||
});
|
||||
await sendBuildErrorNotifications({
|
||||
projectName: compose.project.name,
|
||||
projectName: compose.environment.project.name,
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
organizationId: compose.environment.project.organizationId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
@@ -330,7 +334,7 @@ export const deployRemoteCompose = async ({
|
||||
const compose = await findComposeById(composeId);
|
||||
|
||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${
|
||||
compose.projectId
|
||||
compose.environment.projectId
|
||||
}/services/compose/${compose.composeId}?tab=deployments`;
|
||||
const deployment = await createDeploymentCompose({
|
||||
composeId: composeId,
|
||||
@@ -387,11 +391,11 @@ export const deployRemoteCompose = async ({
|
||||
});
|
||||
|
||||
await sendBuildSuccessNotifications({
|
||||
projectName: compose.project.name,
|
||||
projectName: compose.environment.project.name,
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
organizationId: compose.environment.project.organizationId,
|
||||
domains: compose.domains,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -410,13 +414,13 @@ export const deployRemoteCompose = async ({
|
||||
composeStatus: "error",
|
||||
});
|
||||
await sendBuildErrorNotifications({
|
||||
projectName: compose.project.name,
|
||||
projectName: compose.environment.project.name,
|
||||
applicationName: compose.name,
|
||||
applicationType: "compose",
|
||||
// @ts-ignore
|
||||
errorMessage: error?.message || "Error building",
|
||||
buildLink,
|
||||
organizationId: compose.project.organizationId,
|
||||
organizationId: compose.environment.project.organizationId,
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
|
||||
140
packages/server/src/services/environment.ts
Normal file
140
packages/server/src/services/environment.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { db } from "@dokploy/server/db";
|
||||
import {
|
||||
type apiCreateEnvironment,
|
||||
type apiDuplicateEnvironment,
|
||||
environments,
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { asc, eq } from "drizzle-orm";
|
||||
|
||||
export type Environment = typeof environments.$inferSelect;
|
||||
|
||||
export const createEnvironment = async (
|
||||
input: typeof apiCreateEnvironment._type,
|
||||
) => {
|
||||
const newEnvironment = await db
|
||||
.insert(environments)
|
||||
.values({
|
||||
...input,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newEnvironment) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error creating the environment",
|
||||
});
|
||||
}
|
||||
|
||||
return newEnvironment;
|
||||
};
|
||||
|
||||
export const findEnvironmentById = async (environmentId: string) => {
|
||||
const environment = await db.query.environments.findFirst({
|
||||
where: eq(environments.environmentId, environmentId),
|
||||
with: {
|
||||
applications: true,
|
||||
mariadb: true,
|
||||
mongo: true,
|
||||
mysql: true,
|
||||
postgres: true,
|
||||
redis: true,
|
||||
compose: true,
|
||||
project: true,
|
||||
},
|
||||
});
|
||||
if (!environment) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Environment not found",
|
||||
});
|
||||
}
|
||||
return environment;
|
||||
};
|
||||
|
||||
export const findEnvironmentsByProjectId = async (projectId: string) => {
|
||||
const projectEnvironments = await db.query.environments.findMany({
|
||||
where: eq(environments.projectId, projectId),
|
||||
orderBy: asc(environments.createdAt),
|
||||
with: {
|
||||
applications: true,
|
||||
mariadb: true,
|
||||
mongo: true,
|
||||
mysql: true,
|
||||
postgres: true,
|
||||
redis: true,
|
||||
compose: true,
|
||||
project: true,
|
||||
},
|
||||
});
|
||||
return projectEnvironments;
|
||||
};
|
||||
|
||||
export const deleteEnvironment = async (environmentId: string) => {
|
||||
const currentEnvironment = await findEnvironmentById(environmentId);
|
||||
if (currentEnvironment.name === "production") {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "You cannot delete the production environment",
|
||||
});
|
||||
}
|
||||
const deletedEnvironment = await db
|
||||
.delete(environments)
|
||||
.where(eq(environments.environmentId, environmentId))
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
return deletedEnvironment;
|
||||
};
|
||||
|
||||
export const updateEnvironmentById = async (
|
||||
environmentId: string,
|
||||
environmentData: Partial<Environment>,
|
||||
) => {
|
||||
const result = await db
|
||||
.update(environments)
|
||||
.set({
|
||||
...environmentData,
|
||||
})
|
||||
.where(eq(environments.environmentId, environmentId))
|
||||
.returning()
|
||||
.then((res) => res[0]);
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const duplicateEnvironment = async (
|
||||
input: typeof apiDuplicateEnvironment._type,
|
||||
) => {
|
||||
// Find the original environment
|
||||
const originalEnvironment = await findEnvironmentById(input.environmentId);
|
||||
|
||||
// Create a new environment with the provided name and description
|
||||
const newEnvironment = await db
|
||||
.insert(environments)
|
||||
.values({
|
||||
name: input.name,
|
||||
description: input.description || originalEnvironment.description,
|
||||
projectId: originalEnvironment.projectId,
|
||||
})
|
||||
.returning()
|
||||
.then((value) => value[0]);
|
||||
|
||||
if (!newEnvironment) {
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Error duplicating the environment",
|
||||
});
|
||||
}
|
||||
|
||||
return newEnvironment;
|
||||
};
|
||||
|
||||
export const createProductionEnvironment = async (projectId: string) => {
|
||||
return createEnvironment({
|
||||
name: "production",
|
||||
description: "Production environment",
|
||||
projectId,
|
||||
});
|
||||
};
|
||||
@@ -56,7 +56,11 @@ export const findMariadbById = async (mariadbId: string) => {
|
||||
const result = await db.query.mariadb.findFirst({
|
||||
where: eq(mariadb.mariadbId, mariadbId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
mounts: true,
|
||||
server: true,
|
||||
backups: {
|
||||
|
||||
@@ -53,7 +53,11 @@ export const findMongoById = async (mongoId: string) => {
|
||||
const result = await db.query.mongo.findFirst({
|
||||
where: eq(mongo.mongoId, mongoId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
mounts: true,
|
||||
server: true,
|
||||
backups: {
|
||||
|
||||
@@ -105,13 +105,69 @@ export const findMountById = async (mountId: string) => {
|
||||
const mount = await db.query.mounts.findFirst({
|
||||
where: eq(mounts.mountId, mountId),
|
||||
with: {
|
||||
application: true,
|
||||
postgres: true,
|
||||
mariadb: true,
|
||||
mongo: true,
|
||||
mysql: true,
|
||||
redis: true,
|
||||
compose: true,
|
||||
application: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
postgres: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mariadb: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mongo: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
mysql: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
redis: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
compose: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!mount) {
|
||||
@@ -123,6 +179,34 @@ export const findMountById = async (mountId: string) => {
|
||||
return mount;
|
||||
};
|
||||
|
||||
export const findMountOrganizationId = async (mountId: string) => {
|
||||
const mount = await findMountById(mountId);
|
||||
|
||||
if (mount.application) {
|
||||
return mount.application.environment.project.organizationId;
|
||||
}
|
||||
if (mount.postgres) {
|
||||
return mount.postgres.environment.project.organizationId;
|
||||
}
|
||||
if (mount.mariadb) {
|
||||
return mount.mariadb.environment.project.organizationId;
|
||||
}
|
||||
if (mount.mongo) {
|
||||
return mount.mongo.environment.project.organizationId;
|
||||
}
|
||||
if (mount.mysql) {
|
||||
return mount.mysql.environment.project.organizationId;
|
||||
}
|
||||
if (mount.redis) {
|
||||
return mount.redis.environment.project.organizationId;
|
||||
}
|
||||
|
||||
if (mount.compose) {
|
||||
return mount.compose.environment.project.organizationId;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const updateMount = async (
|
||||
mountId: string,
|
||||
mountData: Partial<Mount>,
|
||||
|
||||
@@ -56,7 +56,11 @@ export const findMySqlById = async (mysqlId: string) => {
|
||||
const result = await db.query.mysql.findFirst({
|
||||
where: eq(mysql.mysqlId, mysqlId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
mounts: true,
|
||||
server: true,
|
||||
backups: {
|
||||
|
||||
@@ -27,6 +27,17 @@ export const createPort = async (input: typeof apiCreatePort._type) => {
|
||||
export const finPortById = async (portId: string) => {
|
||||
const result = await db.query.ports.findFirst({
|
||||
where: eq(ports.portId, portId),
|
||||
with: {
|
||||
application: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!result) {
|
||||
throw new TRPCError({
|
||||
|
||||
@@ -51,7 +51,11 @@ export const findPostgresById = async (postgresId: string) => {
|
||||
const result = await db.query.postgres.findFirst({
|
||||
where: eq(postgres.postgresId, postgresId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
mounts: true,
|
||||
server: true,
|
||||
backups: {
|
||||
|
||||
@@ -31,7 +31,11 @@ export const findPreviewDeploymentById = async (
|
||||
application: {
|
||||
with: {
|
||||
server: true,
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -45,37 +49,6 @@ export const findPreviewDeploymentById = async (
|
||||
return application;
|
||||
};
|
||||
|
||||
export const findApplicationByPreview = async (applicationId: string) => {
|
||||
const application = await db.query.applications.findFirst({
|
||||
with: {
|
||||
previewDeployments: {
|
||||
where: eq(previewDeployments.applicationId, applicationId),
|
||||
},
|
||||
project: true,
|
||||
domains: true,
|
||||
deployments: true,
|
||||
mounts: true,
|
||||
redirects: true,
|
||||
security: true,
|
||||
ports: true,
|
||||
registry: true,
|
||||
gitlab: true,
|
||||
github: true,
|
||||
bitbucket: true,
|
||||
gitea: true,
|
||||
server: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!application) {
|
||||
throw new TRPCError({
|
||||
code: "NOT_FOUND",
|
||||
message: "Application not found",
|
||||
});
|
||||
}
|
||||
return application;
|
||||
};
|
||||
|
||||
export const removePreviewDeployment = async (previewDeploymentId: string) => {
|
||||
try {
|
||||
const previewDeployment =
|
||||
@@ -163,7 +136,7 @@ export const createPreviewDeployment = async (
|
||||
const appName = `preview-${application.appName}-${generatePassword(6)}`;
|
||||
|
||||
const org = await db.query.organization.findFirst({
|
||||
where: eq(organization.id, application.project.organizationId),
|
||||
where: eq(organization.id, application.environment.project.organizationId),
|
||||
});
|
||||
const generateDomain = await generateWildcardDomain(
|
||||
application.previewWildcard || "*.traefik.me",
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "@dokploy/server/db/schema";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { createProductionEnvironment } from "./environment";
|
||||
|
||||
export type Project = typeof projects.$inferSelect;
|
||||
|
||||
@@ -34,20 +35,31 @@ export const createProject = async (
|
||||
});
|
||||
}
|
||||
|
||||
return newProject;
|
||||
// Automatically create a production environment
|
||||
const newEnvironment = await createProductionEnvironment(
|
||||
newProject.projectId,
|
||||
);
|
||||
return {
|
||||
project: newProject,
|
||||
environment: newEnvironment,
|
||||
};
|
||||
};
|
||||
|
||||
export const findProjectById = async (projectId: string) => {
|
||||
const project = await db.query.projects.findFirst({
|
||||
where: eq(projects.projectId, projectId),
|
||||
with: {
|
||||
applications: true,
|
||||
mariadb: true,
|
||||
mongo: true,
|
||||
mysql: true,
|
||||
postgres: true,
|
||||
redis: true,
|
||||
compose: true,
|
||||
environments: {
|
||||
with: {
|
||||
applications: true,
|
||||
mariadb: true,
|
||||
mongo: true,
|
||||
mysql: true,
|
||||
postgres: true,
|
||||
redis: true,
|
||||
compose: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
if (!project) {
|
||||
@@ -86,7 +98,7 @@ export const updateProjectById = async (
|
||||
};
|
||||
|
||||
export const validUniqueServerAppName = async (appName: string) => {
|
||||
const query = await db.query.projects.findMany({
|
||||
const query = await db.query.environments.findMany({
|
||||
with: {
|
||||
applications: {
|
||||
where: eq(applications.appName, appName),
|
||||
|
||||
@@ -52,7 +52,11 @@ export const findRedisById = async (redisId: string) => {
|
||||
const result = await db.query.redis.findFirst({
|
||||
where: eq(redis.redisId, redisId),
|
||||
with: {
|
||||
project: true,
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
mounts: true,
|
||||
server: true,
|
||||
},
|
||||
|
||||
@@ -76,9 +76,24 @@ export const createRollback = async (
|
||||
});
|
||||
};
|
||||
|
||||
const findRollbackById = async (rollbackId: string) => {
|
||||
export const findRollbackById = async (rollbackId: string) => {
|
||||
const result = await db.query.rollbacks.findFirst({
|
||||
where: eq(rollbacks.rollbackId, rollbackId),
|
||||
with: {
|
||||
deployment: {
|
||||
with: {
|
||||
application: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!result) {
|
||||
@@ -179,7 +194,9 @@ const rollbackApplication = async (
|
||||
image: string,
|
||||
serverId?: string | null,
|
||||
fullContext?: Application & {
|
||||
project: Project;
|
||||
environment: {
|
||||
project: Project;
|
||||
};
|
||||
mounts: Mount[];
|
||||
ports: Port[];
|
||||
},
|
||||
@@ -225,7 +242,7 @@ const rollbackApplication = async (
|
||||
const bindsMount = generateBindMounts(mounts);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
fullContext.project.env,
|
||||
fullContext.environment.project.env,
|
||||
);
|
||||
|
||||
// For rollback, we use the provided image instead of calculating it
|
||||
|
||||
@@ -35,9 +35,29 @@ export const findScheduleById = async (scheduleId: string) => {
|
||||
const schedule = await db.query.schedules.findFirst({
|
||||
where: eq(schedules.scheduleId, scheduleId),
|
||||
with: {
|
||||
application: true,
|
||||
compose: true,
|
||||
server: true,
|
||||
application: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
compose: {
|
||||
with: {
|
||||
environment: {
|
||||
with: {
|
||||
project: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
server: {
|
||||
with: {
|
||||
organization: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -50,6 +70,21 @@ export const findScheduleById = async (scheduleId: string) => {
|
||||
return schedule;
|
||||
};
|
||||
|
||||
export const findScheduleOrganizationId = async (scheduleId: string) => {
|
||||
const schedule = await findScheduleById(scheduleId);
|
||||
|
||||
if (schedule?.application) {
|
||||
return schedule?.application?.environment?.project?.organizationId;
|
||||
}
|
||||
if (schedule?.compose) {
|
||||
return schedule?.compose?.environment?.project?.organizationId;
|
||||
}
|
||||
if (schedule?.server) {
|
||||
return schedule?.server?.organization?.id;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const deleteSchedule = async (scheduleId: string) => {
|
||||
const schedule = await findScheduleById(scheduleId);
|
||||
const serverId =
|
||||
|
||||
@@ -253,37 +253,36 @@ export const getDockerResourceType = async (
|
||||
resourceName: string,
|
||||
serverId?: string,
|
||||
) => {
|
||||
let result = "";
|
||||
const command = `
|
||||
RESOURCE_NAME="${resourceName}"
|
||||
if docker service inspect "$RESOURCE_NAME" &>/dev/null; then
|
||||
echo "service"
|
||||
exit 0
|
||||
fi
|
||||
try {
|
||||
let result = "";
|
||||
const command = `
|
||||
RESOURCE_NAME="${resourceName}"
|
||||
if docker service inspect "$RESOURCE_NAME" >/dev/null 2>&1; then
|
||||
echo "service"
|
||||
elif docker inspect "$RESOURCE_NAME" >/dev/null 2>&1; then
|
||||
echo "standalone"
|
||||
else
|
||||
echo "unknown"
|
||||
fi`;
|
||||
|
||||
if docker inspect "$RESOURCE_NAME" &>/dev/null; then
|
||||
echo "standalone"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "unknown"
|
||||
exit 0
|
||||
`;
|
||||
|
||||
if (serverId) {
|
||||
const { stdout } = await execAsyncRemote(serverId, command);
|
||||
result = stdout.trim();
|
||||
} else {
|
||||
const { stdout } = await execAsync(command);
|
||||
result = stdout.trim();
|
||||
if (serverId) {
|
||||
const { stdout } = await execAsyncRemote(serverId, command);
|
||||
result = stdout.trim();
|
||||
} else {
|
||||
const { stdout } = await execAsync(command);
|
||||
result = stdout.trim();
|
||||
}
|
||||
if (result === "service") {
|
||||
return "service";
|
||||
}
|
||||
if (result === "standalone") {
|
||||
return "standalone";
|
||||
}
|
||||
return "unknown";
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
return "unknown";
|
||||
}
|
||||
if (result === "service") {
|
||||
return "service";
|
||||
}
|
||||
if (result === "standalone") {
|
||||
return "standalone";
|
||||
}
|
||||
return "unknown";
|
||||
};
|
||||
|
||||
export const reloadDockerResource = async (
|
||||
@@ -294,8 +293,10 @@ export const reloadDockerResource = async (
|
||||
let command = "";
|
||||
if (resourceType === "service") {
|
||||
command = `docker service update --force ${resourceName}`;
|
||||
} else {
|
||||
} else if (resourceType === "standalone") {
|
||||
command = `docker restart ${resourceName}`;
|
||||
} else {
|
||||
throw new Error("Resource type not found");
|
||||
}
|
||||
if (serverId) {
|
||||
await execAsyncRemote(serverId, command);
|
||||
@@ -312,7 +313,7 @@ export const readEnvironmentVariables = async (
|
||||
let command = "";
|
||||
if (resourceType === "service") {
|
||||
command = `docker service inspect ${resourceName} --format '{{json .Spec.TaskTemplate.ContainerSpec.Env}}'`;
|
||||
} else {
|
||||
} else if (resourceType === "standalone") {
|
||||
command = `docker container inspect ${resourceName} --format '{{json .Config.Env}}'`;
|
||||
}
|
||||
let result = "";
|
||||
@@ -339,7 +340,7 @@ export const readPorts = async (
|
||||
let command = "";
|
||||
if (resourceType === "service") {
|
||||
command = `docker service inspect ${resourceName} --format '{{json .Spec.EndpointSpec.Ports}}'`;
|
||||
} else {
|
||||
} else if (resourceType === "standalone") {
|
||||
command = `docker container inspect ${resourceName} --format '{{json .NetworkSettings.Ports}}'`;
|
||||
}
|
||||
let result = "";
|
||||
|
||||
@@ -23,6 +23,23 @@ export const addNewProject = async (
|
||||
);
|
||||
};
|
||||
|
||||
export const addNewEnvironment = async (
|
||||
userId: string,
|
||||
environmentId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const userR = await findMemberById(userId, organizationId);
|
||||
|
||||
await db
|
||||
.update(member)
|
||||
.set({
|
||||
accessedEnvironments: [...userR.accessedEnvironments, environmentId],
|
||||
})
|
||||
.where(
|
||||
and(eq(member.id, userR.id), eq(member.organizationId, organizationId)),
|
||||
);
|
||||
};
|
||||
|
||||
export const addNewService = async (
|
||||
userId: string,
|
||||
serviceId: string,
|
||||
@@ -131,6 +148,21 @@ export const canPerformAccessProject = async (
|
||||
return false;
|
||||
};
|
||||
|
||||
export const canPerformAccessEnvironment = async (
|
||||
userId: string,
|
||||
environmentId: string,
|
||||
organizationId: string,
|
||||
) => {
|
||||
const { accessedEnvironments } = await findMemberById(userId, organizationId);
|
||||
const haveAccessToEnvironment = accessedEnvironments.includes(environmentId);
|
||||
|
||||
if (haveAccessToEnvironment) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const canAccessToTraefikFiles = async (
|
||||
userId: string,
|
||||
organizationId: string,
|
||||
@@ -182,6 +214,32 @@ export const checkServiceAccess = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const checkEnvironmentAccess = async (
|
||||
userId: string,
|
||||
environmentId: string,
|
||||
organizationId: string,
|
||||
action = "access" as const,
|
||||
) => {
|
||||
let hasPermission = false;
|
||||
switch (action) {
|
||||
case "access":
|
||||
hasPermission = await canPerformAccessEnvironment(
|
||||
userId,
|
||||
environmentId,
|
||||
organizationId,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
hasPermission = false;
|
||||
}
|
||||
if (!hasPermission) {
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Permission denied",
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export const checkProjectAccess = async (
|
||||
authId: string,
|
||||
action: "create" | "delete" | "access",
|
||||
|
||||
@@ -578,8 +578,7 @@ export const createTraefikInstance = () => {
|
||||
TRAEFIK_VERSION=${TRAEFIK_VERSION}
|
||||
docker run -d \
|
||||
--name dokploy-traefik \
|
||||
--network dokploy-network \
|
||||
--restart unless-stopped \
|
||||
--restart always \
|
||||
-v /etc/dokploy/traefik/traefik.yml:/etc/traefik/traefik.yml \
|
||||
-v /etc/dokploy/traefik/dynamic:/etc/dokploy/traefik/dynamic \
|
||||
-v /var/run/docker.sock:/var/run/docker.sock \
|
||||
@@ -587,6 +586,8 @@ export const createTraefikInstance = () => {
|
||||
-p ${TRAEFIK_PORT}:${TRAEFIK_PORT} \
|
||||
-p ${TRAEFIK_HTTP3_PORT}:${TRAEFIK_HTTP3_PORT}/udp \
|
||||
traefik:v$TRAEFIK_VERSION
|
||||
|
||||
docker network connect dokploy-network dokploy-traefik;
|
||||
echo "Traefik version $TRAEFIK_VERSION installed ✅"
|
||||
fi
|
||||
`;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
createDeploymentBackup,
|
||||
updateDeploymentStatus,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
@@ -13,8 +14,9 @@ export const runComposeBackup = async (
|
||||
compose: Compose,
|
||||
backup: BackupSchedule,
|
||||
) => {
|
||||
const { projectId, name } = compose;
|
||||
const project = await findProjectById(projectId);
|
||||
const { environmentId, name } = compose;
|
||||
const environment = await findEnvironmentById(environmentId);
|
||||
const project = await findProjectById(environment.projectId);
|
||||
const { prefix, databaseType } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
updateDeploymentStatus,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import type { Mariadb } from "@dokploy/server/services/mariadb";
|
||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
@@ -13,8 +14,9 @@ export const runMariadbBackup = async (
|
||||
mariadb: Mariadb,
|
||||
backup: BackupSchedule,
|
||||
) => {
|
||||
const { projectId, name } = mariadb;
|
||||
const project = await findProjectById(projectId);
|
||||
const { environmentId, name } = mariadb;
|
||||
const environment = await findEnvironmentById(environmentId);
|
||||
const project = await findProjectById(environment.projectId);
|
||||
const { prefix } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
|
||||
@@ -4,14 +4,16 @@ import {
|
||||
updateDeploymentStatus,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import type { Mongo } from "@dokploy/server/services/mongo";
|
||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { getBackupCommand, getS3Credentials, normalizeS3Path } from "./utils";
|
||||
|
||||
export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => {
|
||||
const { projectId, name } = mongo;
|
||||
const project = await findProjectById(projectId);
|
||||
const { environmentId, name } = mongo;
|
||||
const environment = await findEnvironmentById(environmentId);
|
||||
const project = await findProjectById(environment.projectId);
|
||||
const { prefix } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
|
||||
@@ -4,14 +4,16 @@ import {
|
||||
updateDeploymentStatus,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import type { MySql } from "@dokploy/server/services/mysql";
|
||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
import { getBackupCommand, getS3Credentials, normalizeS3Path } from "./utils";
|
||||
|
||||
export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => {
|
||||
const { projectId, name } = mysql;
|
||||
const project = await findProjectById(projectId);
|
||||
const { environmentId, name } = mysql;
|
||||
const environment = await findEnvironmentById(environmentId);
|
||||
const project = await findProjectById(environment.projectId);
|
||||
const { prefix } = backup;
|
||||
const destination = backup.destination;
|
||||
const backupFileName = `${new Date().toISOString()}.sql.gz`;
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
updateDeploymentStatus,
|
||||
} from "@dokploy/server/services/deployment";
|
||||
import type { Postgres } from "@dokploy/server/services/postgres";
|
||||
import { findEnvironmentById } from "@dokploy/server/services/environment";
|
||||
import { findProjectById } from "@dokploy/server/services/project";
|
||||
import { sendDatabaseBackupNotifications } from "../notifications/database-backup";
|
||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||
@@ -13,8 +14,9 @@ export const runPostgresBackup = async (
|
||||
postgres: Postgres,
|
||||
backup: BackupSchedule,
|
||||
) => {
|
||||
const { name, projectId } = postgres;
|
||||
const project = await findProjectById(projectId);
|
||||
const { name, environmentId } = postgres;
|
||||
const environment = await findEnvironmentById(environmentId);
|
||||
const project = await findProjectById(environment.projectId);
|
||||
|
||||
const deployment = await createDeploymentBackup({
|
||||
backupId: backup.backupId,
|
||||
|
||||
@@ -80,7 +80,7 @@ export const runWebServerBackup = async (backup: BackupSchedule) => {
|
||||
writeStream.write("Zipped database and filesystem\n");
|
||||
|
||||
const uploadCommand = `rclone copyto ${rcloneFlags.join(" ")} "${tempDir}/${backupFileName}" "${s3Path}"`;
|
||||
writeStream.write(`Running command: ${uploadCommand}\n`);
|
||||
writeStream.write("Running command to upload backup to S3\n");
|
||||
await execAsync(uploadCommand);
|
||||
writeStream.write("Uploaded backup to S3 ✅\n");
|
||||
writeStream.end();
|
||||
|
||||
@@ -22,7 +22,7 @@ import { spawnAsync } from "../process/spawnAsync";
|
||||
|
||||
export type ComposeNested = InferResultType<
|
||||
"compose",
|
||||
{ project: true; mounts: true; domains: true }
|
||||
{ environment: { with: { project: true } }; mounts: true; domains: true }
|
||||
>;
|
||||
export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||
@@ -72,7 +72,10 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
||||
NODE_ENV: process.env.NODE_ENV,
|
||||
PATH: process.env.PATH,
|
||||
...(composeType === "stack" && {
|
||||
...getEnviromentVariablesObject(compose.env, compose.project.env),
|
||||
...getEnviromentVariablesObject(
|
||||
compose.env,
|
||||
compose.environment.project.env,
|
||||
),
|
||||
}),
|
||||
},
|
||||
},
|
||||
@@ -202,7 +205,8 @@ const createEnvFile = (compose: ComposeNested) => {
|
||||
|
||||
const envFileContent = prepareEnvironmentVariables(
|
||||
envContent,
|
||||
compose.project.env,
|
||||
compose.environment.project.env,
|
||||
compose.environment.env,
|
||||
).join("\n");
|
||||
|
||||
if (!existsSync(dirname(envFilePath))) {
|
||||
@@ -232,7 +236,8 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||
|
||||
const envFileContent = prepareEnvironmentVariables(
|
||||
envContent,
|
||||
compose.project.env,
|
||||
compose.environment.project.env,
|
||||
compose.environment.env,
|
||||
).join("\n");
|
||||
|
||||
const encodedContent = encodeBase64(envFileContent);
|
||||
@@ -247,7 +252,7 @@ const getExportEnvCommand = (compose: ComposeNested) => {
|
||||
|
||||
const envVars = getEnviromentVariablesObject(
|
||||
compose.env,
|
||||
compose.project.env,
|
||||
compose.environment.project.env,
|
||||
);
|
||||
const exports = Object.entries(envVars)
|
||||
.map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
|
||||
|
||||
@@ -28,7 +28,8 @@ export const buildCustomDocker = async (
|
||||
dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || ".";
|
||||
const args = prepareEnvironmentVariables(
|
||||
buildArgs,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const dockerContextPath = getDockerContextPath(application);
|
||||
@@ -51,7 +52,12 @@ export const buildCustomDocker = async (
|
||||
as it could be publicly exposed.
|
||||
*/
|
||||
if (!publishDirectory) {
|
||||
createEnvFile(dockerFilePath, env, application.project.env);
|
||||
createEnvFile(
|
||||
dockerFilePath,
|
||||
env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
}
|
||||
|
||||
await spawnAsync(
|
||||
@@ -92,7 +98,8 @@ export const getDockerCommand = (
|
||||
dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || ".";
|
||||
const args = prepareEnvironmentVariables(
|
||||
buildArgs,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const dockerContextPath =
|
||||
@@ -121,7 +128,8 @@ export const getDockerCommand = (
|
||||
command += createEnvFileCommand(
|
||||
dockerFilePath,
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,8 @@ export const buildHeroku = async (
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
try {
|
||||
const args = [
|
||||
@@ -53,7 +54,8 @@ export const getHerokuCommand = (
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const args = [
|
||||
|
||||
@@ -30,7 +30,7 @@ export type ApplicationNested = InferResultType<
|
||||
redirects: true;
|
||||
ports: true;
|
||||
registry: true;
|
||||
project: true;
|
||||
environment: { with: { project: true } };
|
||||
}
|
||||
>;
|
||||
|
||||
@@ -148,7 +148,8 @@ export const mechanizeDockerContainer = async (
|
||||
const filesMount = generateFileMounts(appName, application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const image = getImageName(application);
|
||||
|
||||
@@ -20,7 +20,8 @@ export const buildNixpacks = async (
|
||||
const buildContainerId = `${appName}-${nanoid(10)}`;
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const writeToStream = (data: string) => {
|
||||
@@ -101,7 +102,8 @@ export const getNixpacksCommand = (
|
||||
const buildContainerId = `${appName}-${nanoid(10)}`;
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const args = ["build", buildAppDirectory, "--name", appName];
|
||||
|
||||
@@ -12,7 +12,8 @@ export const buildPaketo = async (
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
try {
|
||||
const args = [
|
||||
@@ -52,7 +53,8 @@ export const getPaketoCommand = (
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const args = [
|
||||
|
||||
@@ -26,7 +26,8 @@ export const buildRailpack = async (
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -123,7 +124,8 @@ export const getRailpackCommand = (
|
||||
const buildAppDirectory = getBuildAppDirectory(application);
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
env,
|
||||
application.project.env,
|
||||
application.environment.project.env,
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
// Prepare command
|
||||
|
||||
@@ -6,14 +6,17 @@ export const createEnvFile = (
|
||||
directory: string,
|
||||
env: string | null,
|
||||
projectEnv?: string | null,
|
||||
environmentEnv?: string | null,
|
||||
) => {
|
||||
const envFilePath = join(dirname(directory), ".env");
|
||||
if (!existsSync(dirname(envFilePath))) {
|
||||
mkdirSync(dirname(envFilePath), { recursive: true });
|
||||
}
|
||||
const envFileContent = prepareEnvironmentVariables(env, projectEnv).join(
|
||||
"\n",
|
||||
);
|
||||
const envFileContent = prepareEnvironmentVariables(
|
||||
env,
|
||||
projectEnv,
|
||||
environmentEnv,
|
||||
).join("\n");
|
||||
writeFileSync(envFilePath, envFileContent);
|
||||
};
|
||||
|
||||
@@ -21,10 +24,13 @@ export const createEnvFileCommand = (
|
||||
directory: string,
|
||||
env: string | null,
|
||||
projectEnv?: string | null,
|
||||
environmentEnv?: string | null,
|
||||
) => {
|
||||
const envFileContent = prepareEnvironmentVariables(env, projectEnv).join(
|
||||
"\n",
|
||||
);
|
||||
const envFileContent = prepareEnvironmentVariables(
|
||||
env,
|
||||
projectEnv,
|
||||
environmentEnv,
|
||||
).join("\n");
|
||||
|
||||
const encodedContent = encodeBase64(envFileContent || "");
|
||||
const envFilePath = join(dirname(directory), ".env");
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getRemoteDocker } from "../servers/remote-docker";
|
||||
|
||||
export type MariadbNested = InferResultType<
|
||||
"mariadb",
|
||||
{ mounts: true; project: true }
|
||||
{ mounts: true; environment: { with: { project: true } } }
|
||||
>;
|
||||
export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
const {
|
||||
@@ -54,7 +54,8 @@ export const buildMariadb = async (mariadb: MariadbNested) => {
|
||||
});
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
defaultMariadbEnv,
|
||||
mariadb.project.env,
|
||||
mariadb.environment.project.env,
|
||||
mariadb.environment.env,
|
||||
);
|
||||
const volumesMount = generateVolumeMounts(mounts);
|
||||
const bindsMount = generateBindMounts(mounts);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getRemoteDocker } from "../servers/remote-docker";
|
||||
|
||||
export type MongoNested = InferResultType<
|
||||
"mongo",
|
||||
{ mounts: true; project: true }
|
||||
{ mounts: true; environment: { with: { project: true } } }
|
||||
>;
|
||||
|
||||
export const buildMongo = async (mongo: MongoNested) => {
|
||||
@@ -102,7 +102,8 @@ ${command ?? "wait $MONGOD_PID"}`;
|
||||
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
defaultMongoEnv,
|
||||
mongo.project.env,
|
||||
mongo.environment.project.env,
|
||||
mongo.environment.env,
|
||||
);
|
||||
const volumesMount = generateVolumeMounts(mounts);
|
||||
const bindsMount = generateBindMounts(mounts);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getRemoteDocker } from "../servers/remote-docker";
|
||||
|
||||
export type MysqlNested = InferResultType<
|
||||
"mysql",
|
||||
{ mounts: true; project: true }
|
||||
{ mounts: true; environment: { with: { project: true } } }
|
||||
>;
|
||||
|
||||
export const buildMysql = async (mysql: MysqlNested) => {
|
||||
@@ -60,7 +60,8 @@ export const buildMysql = async (mysql: MysqlNested) => {
|
||||
});
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
defaultMysqlEnv,
|
||||
mysql.project.env,
|
||||
mysql.environment.project.env,
|
||||
mysql.environment.env,
|
||||
);
|
||||
const volumesMount = generateVolumeMounts(mounts);
|
||||
const bindsMount = generateBindMounts(mounts);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getRemoteDocker } from "../servers/remote-docker";
|
||||
|
||||
export type PostgresNested = InferResultType<
|
||||
"postgres",
|
||||
{ mounts: true; project: true }
|
||||
{ mounts: true; environment: { with: { project: true } } }
|
||||
>;
|
||||
export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
const {
|
||||
@@ -53,7 +53,8 @@ export const buildPostgres = async (postgres: PostgresNested) => {
|
||||
});
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
defaultPostgresEnv,
|
||||
postgres.project.env,
|
||||
postgres.environment.project.env,
|
||||
postgres.environment.env,
|
||||
);
|
||||
const volumesMount = generateVolumeMounts(mounts);
|
||||
const bindsMount = generateBindMounts(mounts);
|
||||
|
||||
@@ -12,7 +12,7 @@ import { getRemoteDocker } from "../servers/remote-docker";
|
||||
|
||||
export type RedisNested = InferResultType<
|
||||
"redis",
|
||||
{ mounts: true; project: true }
|
||||
{ mounts: true; environment: { with: { project: true } } }
|
||||
>;
|
||||
export const buildRedis = async (redis: RedisNested) => {
|
||||
const {
|
||||
@@ -51,7 +51,8 @@ export const buildRedis = async (redis: RedisNested) => {
|
||||
});
|
||||
const envVariables = prepareEnvironmentVariables(
|
||||
defaultRedisEnv,
|
||||
redis.project.env,
|
||||
redis.environment.project.env,
|
||||
redis.environment.env,
|
||||
);
|
||||
const volumesMount = generateVolumeMounts(mounts);
|
||||
const bindsMount = generateBindMounts(mounts);
|
||||
|
||||
@@ -259,21 +259,44 @@ export const removeService = async (
|
||||
export const prepareEnvironmentVariables = (
|
||||
serviceEnv: string | null,
|
||||
projectEnv?: string | null,
|
||||
environmentEnv?: string | null,
|
||||
) => {
|
||||
const projectVars = parse(projectEnv ?? "");
|
||||
const environmentVars = parse(environmentEnv ?? "");
|
||||
const serviceVars = parse(serviceEnv ?? "");
|
||||
|
||||
const resolvedVars = Object.entries(serviceVars).map(([key, value]) => {
|
||||
let resolvedValue = value;
|
||||
|
||||
// Replace project variables
|
||||
if (projectVars) {
|
||||
resolvedValue = value.replace(/\$\{\{project\.(.*?)\}\}/g, (_, ref) => {
|
||||
if (projectVars[ref] !== undefined) {
|
||||
return projectVars[ref];
|
||||
}
|
||||
throw new Error(`Invalid project environment variable: project.${ref}`);
|
||||
});
|
||||
resolvedValue = resolvedValue.replace(
|
||||
/\$\{\{project\.(.*?)\}\}/g,
|
||||
(_, ref) => {
|
||||
if (projectVars[ref] !== undefined) {
|
||||
return projectVars[ref];
|
||||
}
|
||||
throw new Error(
|
||||
`Invalid project environment variable: project.${ref}`,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Replace environment variables
|
||||
if (environmentVars) {
|
||||
resolvedValue = resolvedValue.replace(
|
||||
/\$\{\{environment\.(.*?)\}\}/g,
|
||||
(_, ref) => {
|
||||
if (environmentVars[ref] !== undefined) {
|
||||
return environmentVars[ref];
|
||||
}
|
||||
throw new Error(`Invalid environment variable: environment.${ref}`);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Replace self-references (service variables)
|
||||
resolvedValue = resolvedValue.replace(/\$\{\{(.*?)\}\}/g, (_, ref) => {
|
||||
if (serviceVars[ref] !== undefined) {
|
||||
return serviceVars[ref];
|
||||
@@ -301,8 +324,9 @@ export const parseEnvironmentKeyValuePair = (
|
||||
export const getEnviromentVariablesObject = (
|
||||
input: string | null,
|
||||
projectEnv?: string | null,
|
||||
environmentEnv?: string | null,
|
||||
) => {
|
||||
const envs = prepareEnvironmentVariables(input, projectEnv);
|
||||
const envs = prepareEnvironmentVariables(input, projectEnv, environmentEnv);
|
||||
|
||||
const jsonObject: Record<string, string> = {};
|
||||
|
||||
|
||||
@@ -42,7 +42,9 @@ export const buildDocker = async (
|
||||
await mechanizeDockerContainer(application);
|
||||
writeStream.write("\nDocker Deployed: ✅\n");
|
||||
} catch (error) {
|
||||
writeStream.write("❌ Error");
|
||||
writeStream.write(
|
||||
`❌ Error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
);
|
||||
throw error;
|
||||
} finally {
|
||||
writeStream.end();
|
||||
|
||||
Reference in New Issue
Block a user