feat(environment): implement environment management with create, duplicate, and delete functionalities; add environment schema and database migrations

This commit is contained in:
Mauricio Siu
2025-09-01 17:36:27 -06:00
parent fd199fdcc0
commit 6fc325fe95
23 changed files with 14108 additions and 82 deletions

View File

@@ -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";
@@ -182,6 +183,9 @@ export const applications = pgTable("application", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
githubId: text("githubId").references(() => github.githubId, {
onDelete: "set null",
}),
@@ -206,6 +210,10 @@ export const applicationsRelations = relations(
fields: [applications.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [applications.environmentId],
references: [environments.environmentId],
}),
deployments: many(deployments),
customGitSSHKey: one(sshKeys, {
fields: [applications.customGitSSHKeyId],
@@ -274,6 +282,7 @@ const createSchema = createInsertSchema(applications, {
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(),
@@ -318,6 +327,7 @@ export const apiCreateApplication = createSchema.pick({
appName: true,
description: true,
projectId: true,
environmentId: true,
serverId: true,
});

View File

@@ -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";
@@ -87,6 +88,9 @@ export const compose = pgTable("compose", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
@@ -113,6 +117,10 @@ export const composeRelations = relations(compose, ({ one, many }) => ({
fields: [compose.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [compose.environmentId],
references: [environments.environmentId],
}),
deployments: many(deployments),
mounts: many(mounts),
customGitSSHKey: one(sshKeys, {
@@ -150,6 +158,7 @@ const createSchema = createInsertSchema(compose, {
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),
@@ -161,6 +170,7 @@ export const apiCreateCompose = createSchema.pick({
name: true,
description: true,
projectId: true,
environmentId: true,
composeType: true,
appName: true,
serverId: true,
@@ -170,6 +180,7 @@ export const apiCreateCompose = createSchema.pick({
export const apiCreateComposeByTemplate = createSchema
.pick({
projectId: true,
environmentId: true,
})
.extend({
id: z.string().min(1),

View File

@@ -0,0 +1,84 @@
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()),
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,
});

View File

@@ -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";

View File

@@ -4,6 +4,7 @@ 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";
@@ -69,6 +70,9 @@ export const mariadb = pgTable("mariadb", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade",
}),
@@ -79,6 +83,10 @@ export const mariadbRelations = relations(mariadb, ({ one, many }) => ({
fields: [mariadb.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [mariadb.environmentId],
references: [environments.environmentId],
}),
backups: many(backups),
mounts: many(mounts),
server: one(server, {
@@ -116,6 +124,7 @@ const createSchema = createInsertSchema(mariadb, {
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(),
@@ -137,6 +146,7 @@ export const apiCreateMariaDB = createSchema
dockerImage: true,
databaseRootPassword: true,
projectId: true,
environmentId: true,
description: true,
databaseName: true,
databaseUser: true,

View File

@@ -4,6 +4,7 @@ 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";
@@ -65,6 +66,9 @@ export const mongo = pgTable("mongo", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade",
}),
@@ -76,6 +80,10 @@ export const mongoRelations = relations(mongo, ({ one, many }) => ({
fields: [mongo.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [mongo.environmentId],
references: [environments.environmentId],
}),
backups: many(backups),
mounts: many(mounts),
server: one(server, {
@@ -105,6 +113,7 @@ const createSchema = createInsertSchema(mongo, {
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(),
@@ -126,6 +135,7 @@ export const apiCreateMongo = createSchema
appName: true,
dockerImage: true,
projectId: true,
environmentId: true,
description: true,
databaseUser: true,
databasePassword: true,

View File

@@ -4,6 +4,7 @@ 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";
@@ -67,6 +68,9 @@ export const mysql = pgTable("mysql", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade",
}),
@@ -77,6 +81,10 @@ export const mysqlRelations = relations(mysql, ({ one, many }) => ({
fields: [mysql.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [mysql.environmentId],
references: [environments.environmentId],
}),
backups: many(backups),
mounts: many(mounts),
server: one(server, {
@@ -134,6 +142,7 @@ export const apiCreateMySql = createSchema
appName: true,
dockerImage: true,
projectId: true,
environmentId: true,
description: true,
databaseName: true,
databaseUser: true,

View File

@@ -4,6 +4,7 @@ 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";
@@ -67,6 +68,9 @@ export const postgres = pgTable("postgres", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade",
}),
@@ -77,6 +81,10 @@ export const postgresRelations = relations(postgres, ({ one, many }) => ({
fields: [postgres.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [postgres.environmentId],
references: [environments.environmentId],
}),
backups: many(backups),
mounts: many(mounts),
server: one(server, {
@@ -105,6 +113,7 @@ const createSchema = createInsertSchema(postgres, {
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(),
@@ -129,6 +138,7 @@ export const apiCreatePostgres = createSchema
databasePassword: true,
dockerImage: true,
projectId: true,
environmentId: true,
description: true,
serverId: true,
})

View File

@@ -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],

View File

@@ -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";
@@ -63,6 +64,9 @@ export const redis = pgTable("redis", {
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
environmentId: text("environmentId")
.notNull()
.references(() => environments.environmentId, { onDelete: "cascade" }),
serverId: text("serverId").references(() => server.serverId, {
onDelete: "cascade",
}),
@@ -73,6 +77,10 @@ export const redisRelations = relations(redis, ({ one, many }) => ({
fields: [redis.projectId],
references: [projects.projectId],
}),
environment: one(environments, {
fields: [redis.environmentId],
references: [environments.environmentId],
}),
mounts: many(mounts),
server: one(server, {
fields: [redis.serverId],
@@ -94,6 +102,7 @@ const createSchema = createInsertSchema(redis, {
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(),
@@ -115,6 +124,7 @@ export const apiCreateRedis = createSchema
databasePassword: true,
dockerImage: true,
projectId: true,
environmentId: true,
description: true,
serverId: true,
})