feat: initial commit

This commit is contained in:
Mauricio Siu
2024-04-28 23:57:52 -06:00
parent 8857a20344
commit be56ba046c
412 changed files with 60777 additions and 1 deletions

92
server/db/schema/admin.ts Normal file
View File

@@ -0,0 +1,92 @@
import { relations } from "drizzle-orm";
import { boolean, integer, pgTable, text } from "drizzle-orm/pg-core";
import { nanoid } from "nanoid";
import { auth } from "./auth";
import { users } from "./user";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { certificateType } from "./shared";
export const admins = pgTable("admin", {
adminId: text("adminId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
githubAppId: integer("githubAppId"),
githubAppName: text("githubAppName"),
serverIp: text("serverIp"),
certificateType: certificateType("certificateType").notNull().default("none"),
host: text("host"),
githubClientId: text("githubClientId"),
githubClientSecret: text("githubClientSecret"),
githubInstallationId: text("githubInstallationId"),
githubPrivateKey: text("githubPrivateKey"),
letsEncryptEmail: text("letsEncryptEmail"),
sshPrivateKey: text("sshPrivateKey"),
enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false),
authId: text("authId")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
});
export const adminsRelations = relations(admins, ({ one, many }) => ({
auth: one(auth, {
fields: [admins.authId],
references: [auth.id],
}),
users: many(users),
}));
const createSchema = createInsertSchema(admins, {
adminId: z.string(),
githubAppName: z.string().optional(),
githubClientId: z.string().optional(),
githubClientSecret: z.string().optional(),
githubInstallationId: z.string().optional(),
githubPrivateKey: z.string().optional(),
githubAppId: z.number().optional(),
enableDockerCleanup: z.boolean().optional(),
sshPrivateKey: z.string().optional(),
certificateType: z.enum(["letsencrypt", "none"]).default("none"),
serverIp: z.string().optional(),
});
export const apiSaveSSHKey = createSchema
.pick({
sshPrivateKey: true,
})
.required();
export const apiAssignDomain = createSchema
.pick({
letsEncryptEmail: true,
host: true,
certificateType: true,
})
.required();
export const apiUpdateDockerCleanup = createSchema
.pick({
enableDockerCleanup: true,
})
.required();
export const apiTraefikConfig = z.object({
traefikConfig: z.string().min(1),
});
export const apiGetBranches = z.object({
repo: z.string().min(1),
owner: z.string().min(1),
});
export const apiModifyTraefikConfig = z.object({
path: z.string().min(1),
traefikConfig: z.string().min(1),
});
export const apiReadTraefikConfig = z.object({
path: z.string().min(1),
});

View File

@@ -0,0 +1,199 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { deployments } from "./deployment";
import { mounts } from "./mount";
import { redirects } from "./redirects";
import { domains } from "./domain";
import { projects } from "./project";
import { security } from "./security";
import { applicationStatus } from "./shared";
import { ports } from "./port";
import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { generateAppName } from "./utils";
export const sourceType = pgEnum("sourceType", ["docker", "git", "github"]);
export const buildType = pgEnum("buildType", [
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks",
]);
export const applications = pgTable("application", {
applicationId: text("applicationId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
appName: text("appName")
.notNull()
.$defaultFn(() => generateAppName("app"))
.unique(),
description: text("description"),
env: text("env"),
memoryReservation: integer("memoryReservation"),
memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"),
title: text("title"),
enabled: boolean("enabled"),
subtitle: text("subtitle"),
command: text("command"),
refreshToken: text("refreshToken").$defaultFn(() => nanoid()),
sourceType: sourceType("sourceType").notNull().default("github"),
// Github
repository: text("repository"),
owner: text("owner"),
branch: text("branch"),
buildPath: text("buildPath").default("/"),
autoDeploy: boolean("autoDeploy"),
// Docker
username: text("username"),
password: text("password"),
dockerImage: text("dockerImage"),
// Git
customGitUrl: text("customGitUrl"),
customGitBranch: text("customGitBranch"),
customGitBuildPath: text("customGitBuildPath"),
customGitSSHKey: text("customGitSSHKey"),
dockerfile: text("dockerfile"),
applicationStatus: applicationStatus("applicationStatus")
.notNull()
.default("idle"),
buildType: buildType("buildType").notNull().default("nixpacks"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
});
export const applicationsRelations = relations(
applications,
({ one, many }) => ({
project: one(projects, {
fields: [applications.projectId],
references: [projects.projectId],
}),
deployments: many(deployments),
domains: many(domains),
mounts: many(mounts),
redirects: many(redirects),
security: many(security),
ports: many(ports),
}),
);
const createSchema = createInsertSchema(applications, {
appName: z.string(),
createdAt: z.string(),
applicationId: z.string(),
autoDeploy: z.boolean(),
env: z.string().optional(),
name: z.string().min(1),
description: z.string().optional(),
memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(),
title: z.string().optional(),
enabled: z.boolean().optional(),
subtitle: z.string().optional(),
dockerImage: z.string().optional(),
username: z.string().optional(),
password: z.string().optional(),
customGitSSHKey: z.string().optional(),
repository: z.string().optional(),
dockerfile: z.string().optional(),
branch: z.string().optional(),
customGitBranch: z.string().optional(),
customGitBuildPath: z.string().optional(),
customGitUrl: z.string().optional(),
buildPath: z.string().optional(),
projectId: z.string(),
sourceType: z.enum(["github", "docker", "git"]).optional(),
applicationStatus: z.enum(["idle", "running", "done", "error"]),
buildType: z.enum([
"dockerfile",
"heroku_buildpacks",
"paketo_buildpacks",
"nixpacks",
]),
owner: z.string(),
});
export const apiCreateApplication = createSchema.pick({
name: true,
description: true,
projectId: true,
});
export const apiFindOneApplication = createSchema
.pick({
applicationId: true,
})
.required();
export const apiReloadApplication = createSchema
.pick({
appName: true,
applicationId: true,
})
.required();
export const apiSaveBuildType = createSchema
.pick({
applicationId: true,
buildType: true,
dockerfile: true,
})
.required();
export const apiSaveGithubProvider = createSchema
.pick({
applicationId: true,
repository: true,
branch: true,
owner: true,
buildPath: true,
})
.required();
export const apiSaveDockerProvider = createSchema
.pick({
dockerImage: true,
applicationId: true,
username: true,
password: true,
})
.required();
export const apiSaveGitProvider = createSchema
.pick({
customGitBranch: true,
applicationId: true,
customGitBuildPath: true,
customGitUrl: true,
})
.required();
export const apiSaveEnviromentVariables = createSchema
.pick({
applicationId: true,
env: true,
})
.required();
export const apiFindMonitoringStats = createSchema
.pick({
appName: true,
})
.required();
export const apiUpdateApplication = createSchema.partial().extend({
applicationId: z.string().min(1),
});

123
server/db/schema/auth.ts Normal file
View File

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

131
server/db/schema/backups.ts Normal file
View File

@@ -0,0 +1,131 @@
import { relations } from "drizzle-orm";
import { destinations } from "./destination";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { postgres } from "./postgres";
import { mariadb } from "./mariadb";
import { mysql } from "./mysql";
import { mongo } from "./mongo";
import {
type AnyPgColumn,
boolean,
pgEnum,
pgTable,
text,
} from "drizzle-orm/pg-core";
export const databaseType = pgEnum("databaseType", [
"postgres",
"mariadb",
"mysql",
"mongo",
]);
export const backups = pgTable("backup", {
backupId: text("backupId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
schedule: text("schedule").notNull(),
enabled: boolean("enabled"),
database: text("database").notNull(),
prefix: text("prefix").notNull(),
destinationId: text("destinationId")
.notNull()
.references(() => destinations.destinationId, { onDelete: "cascade" }),
databaseType: databaseType("databaseType").notNull(),
postgresId: text("postgresId").references(
(): AnyPgColumn => postgres.postgresId,
{
onDelete: "cascade",
},
),
mariadbId: text("mariadbId").references(
(): AnyPgColumn => mariadb.mariadbId,
{
onDelete: "cascade",
},
),
mysqlId: text("mysqlId").references((): AnyPgColumn => mysql.mysqlId, {
onDelete: "cascade",
}),
mongoId: text("mongoId").references((): AnyPgColumn => mongo.mongoId, {
onDelete: "cascade",
}),
});
export const backupsRelations = relations(backups, ({ one }) => ({
destination: one(destinations, {
fields: [backups.destinationId],
references: [destinations.destinationId],
}),
postgres: one(postgres, {
fields: [backups.postgresId],
references: [postgres.postgresId],
}),
mariadb: one(mariadb, {
fields: [backups.mariadbId],
references: [mariadb.mariadbId],
}),
mysql: one(mysql, {
fields: [backups.mysqlId],
references: [mysql.mysqlId],
}),
mongo: one(mongo, {
fields: [backups.mongoId],
references: [mongo.mongoId],
}),
}));
const createSchema = createInsertSchema(backups, {
backupId: z.string(),
destinationId: z.string(),
enabled: z.boolean().optional(),
prefix: z.string().min(1),
database: z.string().min(1),
schedule: z.string(),
databaseType: z.enum(["postgres", "mariadb", "mysql", "mongo"]),
postgresId: z.string().optional(),
mariadbId: z.string().optional(),
mysqlId: z.string().optional(),
mongoId: z.string().optional(),
});
export const apiCreateBackup = createSchema.pick({
schedule: true,
enabled: true,
prefix: true,
destinationId: true,
database: true,
mariadbId: true,
mysqlId: true,
postgresId: true,
mongoId: true,
databaseType: true,
});
export const apiFindOneBackup = createSchema
.pick({
backupId: true,
})
.required();
export const apiRemoveBackup = createSchema
.pick({
backupId: true,
})
.required();
export const apiUpdateBackup = createSchema
.pick({
schedule: true,
enabled: true,
prefix: true,
backupId: true,
destinationId: true,
database: true,
})
.required();

View File

@@ -0,0 +1,43 @@
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { boolean, pgTable, text } from "drizzle-orm/pg-core";
import { generateAppName } from "./utils";
export const certificates = pgTable("certificate", {
certificateId: text("certificateId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
certificateData: text("certificateData").notNull(),
privateKey: text("privateKey").notNull(),
certificatePath: text("certificatePath")
.notNull()
.$defaultFn(() => generateAppName("certificate"))
.unique(),
autoRenew: boolean("autoRenew"),
});
export const apiCreateCertificate = createInsertSchema(certificates, {
name: z.string().min(1),
certificateData: z.string().min(1),
privateKey: z.string().min(1),
autoRenew: z.boolean().optional(),
});
export const apiFindCertificate = z.object({
certificateId: z.string().min(1),
});
export const apiUpdateCertificate = z.object({
certificateId: z.string().min(1),
name: z.string().min(1).optional(),
certificateData: z.string().min(1).optional(),
privateKey: z.string().min(1).optional(),
autoRenew: z.boolean().optional(),
});
export const apiDeleteCertificate = z.object({
certificateId: z.string().min(1),
});

View File

@@ -0,0 +1,57 @@
import { relations } from "drizzle-orm";
import { z } from "zod";
import { nanoid } from "nanoid";
import { applications } from "./application";
import { createInsertSchema } from "drizzle-zod";
import { pgEnum, pgTable, text } from "drizzle-orm/pg-core";
export const deploymentStatus = pgEnum("deploymentStatus", [
"running",
"done",
"error",
]);
export const deployments = pgTable("deployment", {
deploymentId: text("deploymentId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
title: text("title").notNull(),
status: deploymentStatus("status").default("running"),
logPath: text("logPath").notNull(),
applicationId: text("applicationId")
.notNull()
.references(() => applications.applicationId, { onDelete: "cascade" }),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
});
export const deploymentsRelations = relations(deployments, ({ one }) => ({
application: one(applications, {
fields: [deployments.applicationId],
references: [applications.applicationId],
}),
}));
const schema = createInsertSchema(deployments, {
title: z.string().min(1),
status: z.string().default("running"),
logPath: z.string().min(1),
applicationId: z.string().min(1),
});
export const apiCreateDeployment = schema
.pick({
title: true,
status: true,
logPath: true,
applicationId: true,
})
.required();
export const apiFindAllByApplication = schema
.pick({
applicationId: true,
})
.required();

View File

@@ -0,0 +1,80 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { pgTable, text } from "drizzle-orm/pg-core";
import { admins } from "./admin";
import { backups } from "./backups";
export const destinations = pgTable("destination", {
destinationId: text("destinationId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
accessKey: text("accessKey").notNull(),
secretAccessKey: text("secretAccessKey").notNull(),
bucket: text("bucket").notNull(),
region: text("region").notNull(),
// maybe it can be null
endpoint: text("endpoint").notNull(),
adminId: text("adminId")
.notNull()
.references(() => admins.adminId, { onDelete: "cascade" }),
});
export const destinationsRelations = relations(
destinations,
({ many, one }) => ({
backups: many(backups),
admin: one(admins, {
fields: [destinations.adminId],
references: [admins.adminId],
}),
}),
);
const createSchema = createInsertSchema(destinations, {
destinationId: z.string(),
name: z.string().min(1),
accessKey: z.string(),
bucket: z.string(),
endpoint: z.string(),
secretAccessKey: z.string(),
region: z.string(),
});
export const apiCreateDestination = createSchema
.pick({
name: true,
accessKey: true,
bucket: true,
region: true,
endpoint: true,
secretAccessKey: true,
})
.required();
export const apiFindOneDestination = createSchema
.pick({
destinationId: true,
})
.required();
export const apiRemoveDestination = createSchema
.pick({
destinationId: true,
})
.required();
export const apiUpdateDestination = createSchema
.pick({
name: true,
accessKey: true,
bucket: true,
region: true,
endpoint: true,
secretAccessKey: true,
destinationId: true,
})
.required();

View File

@@ -0,0 +1,77 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { boolean, integer, pgTable, serial, text } from "drizzle-orm/pg-core";
import { applications } from "./application";
import { certificateType } from "./shared";
export const domains = pgTable("domain", {
domainId: text("domainId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
host: text("host").notNull(),
https: boolean("https").notNull().default(false),
port: integer("port").default(80),
path: text("path").default("/"),
uniqueConfigKey: serial("uniqueConfigKey"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
applicationId: text("applicationId")
.notNull()
.references(() => applications.applicationId, { onDelete: "cascade" }),
certificateType: certificateType("certificateType").notNull().default("none"),
});
export const domainsRelations = relations(domains, ({ one }) => ({
application: one(applications, {
fields: [domains.applicationId],
references: [applications.applicationId],
}),
}));
const hostnameRegex = /^[a-zA-Z0-9][a-zA-Z0-9\.-]*\.[a-zA-Z]{2,}$/;
const createSchema = createInsertSchema(domains, {
domainId: z.string().min(1),
host: z.string().min(1),
path: z.string().min(1),
port: z.number(),
https: z.boolean(),
applicationId: z.string(),
certificateType: z.enum(["letsencrypt", "none"]),
});
export const apiCreateDomain = createSchema
.pick({
host: true,
path: true,
port: true,
https: true,
applicationId: true,
certificateType: true,
})
.required();
export const apiFindDomain = createSchema
.pick({
domainId: true,
})
.required();
export const apiFindDomainByApplication = createSchema
.pick({
applicationId: true,
})
.required();
export const apiUpdateDomain = createSchema
.pick({
domainId: true,
host: true,
path: true,
port: true,
https: true,
certificateType: true,
})
.required();

22
server/db/schema/index.ts Normal file
View File

@@ -0,0 +1,22 @@
export * from "./application";
export * from "./postgres";
export * from "./user";
export * from "./admin";
export * from "./auth";
export * from "./project";
export * from "./domain";
export * from "./mariadb";
export * from "./mongo";
export * from "./mysql";
export * from "./backups";
export * from "./destination";
export * from "./deployment";
export * from "./mount";
export * from "./certificate";
export * from "./session";
export * from "./redirects";
export * from "./security";
export * from "./port";
export * from "./redis";
export * from "./shared";

134
server/db/schema/mariadb.ts Normal file
View File

@@ -0,0 +1,134 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { applicationStatus } from "./shared";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { projects } from "./project";
import { backups } from "./backups";
import { mounts } from "./mount";
import { generateAppName } from "./utils";
export const mariadb = pgTable("mariadb", {
mariadbId: text("mariadbId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
appName: text("appName")
.notNull()
.$defaultFn(() => generateAppName("mariadb"))
.unique(),
description: text("description"),
databaseName: text("databaseName").notNull(),
databaseUser: text("databaseUser").notNull(),
databasePassword: text("databasePassword").notNull(),
databaseRootPassword: text("rootPassword").notNull(),
dockerImage: text("dockerImage").notNull(),
command: text("command"),
env: text("env"),
// RESOURCES
memoryReservation: integer("memoryReservation"),
memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"),
//
externalPort: integer("externalPort"),
applicationStatus: applicationStatus("applicationStatus")
.notNull()
.default("idle"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
});
export const mariadbRelations = relations(mariadb, ({ one, many }) => ({
project: one(projects, {
fields: [mariadb.projectId],
references: [projects.projectId],
}),
backups: many(backups),
mounts: many(mounts),
}));
const createSchema = createInsertSchema(mariadb, {
mariadbId: z.string(),
name: z.string().min(1),
appName: z.string().min(1),
createdAt: z.string(),
databaseName: z.string().min(1),
databaseUser: z.string().min(1),
databasePassword: z.string(),
databaseRootPassword: z.string().optional(),
dockerImage: z.string().default("mariadb:6"),
command: z.string().optional(),
env: z.string().optional(),
memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(),
projectId: z.string(),
applicationStatus: z.enum(["idle", "running", "done", "error"]),
externalPort: z.number(),
description: z.string().optional(),
});
export const apiCreateMariaDB = createSchema
.pick({
name: true,
dockerImage: true,
databaseRootPassword: true,
projectId: true,
description: true,
databaseName: true,
databaseUser: true,
databasePassword: true,
})
.required();
export const apiFindOneMariaDB = createSchema
.pick({
mariadbId: true,
})
.required();
export const apiChangeMariaDBStatus = createSchema
.pick({
mariadbId: true,
applicationStatus: true,
})
.required();
export const apiSaveEnviromentVariablesMariaDB = createSchema
.pick({
mariadbId: true,
env: true,
})
.required();
export const apiSaveExternalPortMariaDB = createSchema
.pick({
mariadbId: true,
externalPort: true,
})
.required();
export const apiDeployMariaDB = createSchema
.pick({
mariadbId: true,
})
.required();
export const apiResetMariadb = createSchema
.pick({
mariadbId: true,
appName: true,
})
.required();
export const apiUpdateMariaDB = createSchema.partial().extend({
mariadbId: z.string().min(1),
});

126
server/db/schema/mongo.ts Normal file
View File

@@ -0,0 +1,126 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { applicationStatus } from "./shared";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { projects } from "./project";
import { backups } from "./backups";
import { mounts } from "./mount";
import { generateAppName } from "./utils";
export const mongo = pgTable("mongo", {
mongoId: text("mongoId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
appName: text("appName")
.notNull()
.$defaultFn(() => generateAppName("mongo"))
.unique(),
description: text("description"),
databaseUser: text("databaseUser").notNull(),
databasePassword: text("databasePassword").notNull(),
dockerImage: text("dockerImage").notNull(),
command: text("command"),
env: text("env"),
memoryReservation: integer("memoryReservation"),
memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"),
externalPort: integer("externalPort"),
applicationStatus: applicationStatus("applicationStatus")
.notNull()
.default("idle"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
});
export const mongoRelations = relations(mongo, ({ one, many }) => ({
project: one(projects, {
fields: [mongo.projectId],
references: [projects.projectId],
}),
backups: many(backups),
mounts: many(mounts),
}));
const createSchema = createInsertSchema(mongo, {
appName: z.string().min(1),
createdAt: z.string(),
mongoId: z.string(),
name: z.string().min(1),
databasePassword: z.string(),
databaseUser: z.string().min(1),
dockerImage: z.string().default("mongo:15"),
command: z.string().optional(),
env: z.string().optional(),
memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(),
projectId: z.string(),
applicationStatus: z.enum(["idle", "running", "done", "error"]),
externalPort: z.number(),
description: z.string().optional(),
});
export const apiCreateMongo = createSchema
.pick({
name: true,
dockerImage: true,
projectId: true,
description: true,
databaseUser: true,
databasePassword: true,
})
.required();
export const apiFindOneMongo = createSchema
.pick({
mongoId: true,
})
.required();
export const apiChangeMongoStatus = createSchema
.pick({
mongoId: true,
applicationStatus: true,
})
.required();
export const apiSaveEnviromentVariablesMongo = createSchema
.pick({
mongoId: true,
env: true,
})
.required();
export const apiSaveExternalPortMongo = createSchema
.pick({
mongoId: true,
externalPort: true,
})
.required();
export const apiDeployMongo = createSchema
.pick({
mongoId: true,
})
.required();
export const apiUpdateMongo = createSchema.partial().extend({
mongoId: z.string().min(1),
});
export const apiResetMongo = createSchema
.pick({
mongoId: true,
appName: true,
})
.required();

136
server/db/schema/mount.ts Normal file
View File

@@ -0,0 +1,136 @@
import { nanoid } from "nanoid";
import { applications } from "./application";
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { postgres } from "./postgres";
import { mariadb } from "./mariadb";
import { mongo } from "./mongo";
import { mysql } from "./mysql";
import { redis } from "./redis";
export const serviceType = pgEnum("serviceType", [
"application",
"postgres",
"mysql",
"mariadb",
"mongo",
"redis",
]);
export const mountType = pgEnum("mountType", ["bind", "volume", "file"]);
export const mounts = pgTable("mount", {
mountId: text("mountId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
type: mountType("type").notNull(),
hostPath: text("hostPath"),
volumeName: text("volumeName"),
content: text("content"),
serviceType: serviceType("serviceType").notNull().default("application"),
mountPath: text("mountPath").notNull(),
applicationId: text("applicationId").references(
() => applications.applicationId,
{ onDelete: "cascade" },
),
postgresId: text("postgresId").references(() => postgres.postgresId, {
onDelete: "cascade",
}),
mariadbId: text("mariadbId").references(() => mariadb.mariadbId, {
onDelete: "cascade",
}),
mongoId: text("mongoId").references(() => mongo.mongoId, {
onDelete: "cascade",
}),
mysqlId: text("mysqlId").references(() => mysql.mysqlId, {
onDelete: "cascade",
}),
redisId: text("redisId").references(() => redis.redisId, {
onDelete: "cascade",
}),
});
export const MountssRelations = relations(mounts, ({ one }) => ({
application: one(applications, {
fields: [mounts.applicationId],
references: [applications.applicationId],
}),
postgres: one(postgres, {
fields: [mounts.postgresId],
references: [postgres.postgresId],
}),
mariadb: one(mariadb, {
fields: [mounts.mariadbId],
references: [mariadb.mariadbId],
}),
mongo: one(mongo, {
fields: [mounts.mongoId],
references: [mongo.mongoId],
}),
mysql: one(mysql, {
fields: [mounts.mysqlId],
references: [mysql.mysqlId],
}),
redis: one(redis, {
fields: [mounts.redisId],
references: [redis.redisId],
}),
}));
const createSchema = createInsertSchema(mounts, {
applicationId: z.string(),
type: z.enum(["bind", "volume", "file"]),
hostPath: z.string().optional(),
volumeName: z.string().optional(),
content: z.string().optional(),
mountPath: z.string().min(1),
mountId: z.string().optional(),
serviceType: z
.enum(["application", "postgres", "mysql", "mariadb", "mongo", "redis"])
.default("application"),
});
export type ServiceType = NonNullable<
z.infer<typeof createSchema>["serviceType"]
>;
export const apiCreateMount = createSchema
.pick({
type: true,
hostPath: true,
volumeName: true,
content: true,
mountPath: true,
serviceType: true,
})
.extend({
serviceId: z.string().min(1),
});
export const apiFindOneMount = createSchema
.pick({
mountId: true,
})
.required();
export const apiRemoveMount = createSchema
.pick({
mountId: true,
})
// .extend({
// appName: z.string().min(1),
// })
.required();
export const apiFindMountByApplicationId = createSchema
.extend({
serviceId: z.string().min(1),
})
.pick({
serviceId: true,
serviceType: true,
})
.required();

132
server/db/schema/mysql.ts Normal file
View File

@@ -0,0 +1,132 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { applicationStatus } from "./shared";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { projects } from "./project";
import { backups } from "./backups";
import { mounts } from "./mount";
import { generateAppName } from "./utils";
export const mysql = pgTable("mysql", {
mysqlId: text("mysqlId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
appName: text("appName")
.notNull()
.$defaultFn(() => generateAppName("mysql"))
.unique(),
description: text("description"),
databaseName: text("databaseName").notNull(),
databaseUser: text("databaseUser").notNull(),
databasePassword: text("databasePassword").notNull(),
databaseRootPassword: text("rootPassword").notNull(),
dockerImage: text("dockerImage").notNull(),
command: text("command"),
env: text("env"),
memoryReservation: integer("memoryReservation"),
memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"),
externalPort: integer("externalPort"),
applicationStatus: applicationStatus("applicationStatus")
.notNull()
.default("idle"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
});
export const mysqlRelations = relations(mysql, ({ one, many }) => ({
project: one(projects, {
fields: [mysql.projectId],
references: [projects.projectId],
}),
backups: many(backups),
mounts: many(mounts),
}));
const createSchema = createInsertSchema(mysql, {
mysqlId: z.string(),
appName: z.string().min(1),
createdAt: z.string(),
name: z.string().min(1),
databaseName: z.string().min(1),
databaseUser: z.string().min(1),
databasePassword: z.string(),
databaseRootPassword: z.string().optional(),
dockerImage: z.string().default("mysql:8"),
command: z.string().optional(),
env: z.string().optional(),
memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(),
projectId: z.string(),
applicationStatus: z.enum(["idle", "running", "done", "error"]),
externalPort: z.number(),
description: z.string().optional(),
});
export const apiCreateMySql = createSchema
.pick({
name: true,
dockerImage: true,
projectId: true,
description: true,
databaseName: true,
databaseUser: true,
databasePassword: true,
databaseRootPassword: true,
})
.required();
export const apiFindOneMySql = createSchema
.pick({
mysqlId: true,
})
.required();
export const apiChangeMySqlStatus = createSchema
.pick({
mysqlId: true,
applicationStatus: true,
})
.required();
export const apiSaveEnviromentVariablesMySql = createSchema
.pick({
mysqlId: true,
env: true,
})
.required();
export const apiSaveExternalPortMySql = createSchema
.pick({
mysqlId: true,
externalPort: true,
})
.required();
export const apiResetMysql = createSchema
.pick({
mysqlId: true,
appName: true,
})
.required();
export const apiDeployMySql = createSchema
.pick({
mysqlId: true,
})
.required();
export const apiUpdateMySql = createSchema.partial().extend({
mysqlId: z.string().min(1),
});

61
server/db/schema/port.ts Normal file
View File

@@ -0,0 +1,61 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core";
import { applications } from "./application";
export const protocolType = pgEnum("protocolType", ["tcp", "udp"]);
export const ports = pgTable("port", {
portId: text("portId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
publishedPort: integer("publishedPort").notNull(),
targetPort: integer("targetPort").notNull(),
protocol: protocolType("protocol").notNull(),
applicationId: text("applicationId")
.notNull()
.references(() => applications.applicationId, { onDelete: "cascade" }),
});
export const portsRelations = relations(ports, ({ one }) => ({
application: one(applications, {
fields: [ports.applicationId],
references: [applications.applicationId],
}),
}));
const createSchema = createInsertSchema(ports, {
portId: z.string().min(1),
applicationId: z.string().min(1),
publishedPort: z.number(),
targetPort: z.number(),
protocol: z.enum(["tcp", "udp"]).default("tcp"),
});
export const apiCreatePort = createSchema
.pick({
publishedPort: true,
targetPort: true,
protocol: true,
applicationId: true,
})
.required();
export const apiFindOnePort = createSchema
.pick({
portId: true,
})
.required();
export const apiUpdatePort = createSchema
.pick({
portId: true,
publishedPort: true,
targetPort: true,
protocol: true,
})
.required();

View File

@@ -0,0 +1,128 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { applicationStatus } from "./shared";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { projects } from "./project";
import { backups } from "./backups";
import { mounts } from "./mount";
import { generateAppName } from "./utils";
export const postgres = pgTable("postgres", {
postgresId: text("postgresId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
appName: text("appName")
.notNull()
.$defaultFn(() => generateAppName("postgres"))
.unique(),
databaseName: text("databaseName").notNull(),
databaseUser: text("databaseUser").notNull(),
databasePassword: text("databasePassword").notNull(),
description: text("description"),
dockerImage: text("dockerImage").notNull(),
command: text("command"),
env: text("env"),
memoryReservation: integer("memoryReservation"),
externalPort: integer("externalPort"),
memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"),
applicationStatus: applicationStatus("applicationStatus")
.notNull()
.default("idle"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
});
export const postgresRelations = relations(postgres, ({ one, many }) => ({
project: one(projects, {
fields: [postgres.projectId],
references: [projects.projectId],
}),
backups: many(backups),
mounts: many(mounts),
}));
const createSchema = createInsertSchema(postgres, {
postgresId: z.string(),
name: z.string().min(1),
databasePassword: z.string(),
databaseName: z.string().min(1),
databaseUser: z.string().min(1),
dockerImage: z.string().default("postgres:15"),
command: z.string().optional(),
env: z.string().optional(),
memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(),
projectId: z.string(),
applicationStatus: z.enum(["idle", "running", "done", "error"]),
externalPort: z.number(),
createdAt: z.string(),
description: z.string().optional(),
});
export const apiCreatePostgres = createSchema
.pick({
name: true,
databaseName: true,
databaseUser: true,
databasePassword: true,
dockerImage: true,
projectId: true,
description: true,
})
.required();
export const apiFindOnePostgres = createSchema
.pick({
postgresId: true,
})
.required();
export const apiChangePostgresStatus = createSchema
.pick({
postgresId: true,
applicationStatus: true,
})
.required();
export const apiSaveEnviromentVariablesPostgres = createSchema
.pick({
postgresId: true,
env: true,
})
.required();
export const apiSaveExternalPortPostgres = createSchema
.pick({
postgresId: true,
externalPort: true,
})
.required();
export const apiDeployPostgres = createSchema
.pick({
postgresId: true,
})
.required();
export const apiResetPostgres = createSchema
.pick({
postgresId: true,
appName: true,
})
.required();
export const apiUpdatePostgres = createSchema.partial().extend({
postgresId: z.string().min(1),
});

View File

@@ -0,0 +1,72 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
import { nanoid } from "nanoid";
import { pgTable, text } from "drizzle-orm/pg-core";
import { mysql } from "./mysql";
import { postgres } from "./postgres";
import { mariadb } from "./mariadb";
import { applications } from "./application";
import { mongo } from "./mongo";
import { redis } from "./redis";
import { admins } from "./admin";
export const projects = pgTable("project", {
projectId: text("projectId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
description: text("description"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
adminId: text("adminId")
.notNull()
.references(() => admins.adminId, { onDelete: "cascade" }),
});
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),
admin: one(admins, {
fields: [projects.adminId],
references: [admins.adminId],
}),
}));
const createSchema = createInsertSchema(projects, {
projectId: z.string().min(1),
name: z.string().min(1),
description: z.string().optional(),
});
export const apiCreateProject = createSchema.pick({
name: true,
description: true,
});
export const apiFindOneProject = createSchema
.pick({
projectId: true,
})
.required();
export const apiRemoveProject = createSchema
.pick({
projectId: true,
})
.required();
export const apiUpdateProject = createSchema
.pick({
name: true,
description: true,
projectId: true,
})
.required();

View File

@@ -0,0 +1,60 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { boolean, pgTable, serial, text } from "drizzle-orm/pg-core";
import { applications } from "./application";
export const redirects = pgTable("redirect", {
redirectId: text("redirectId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
regex: text("regex").notNull(),
replacement: text("replacement").notNull(),
permanent: boolean("permanent").notNull().default(false),
uniqueConfigKey: serial("uniqueConfigKey"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
applicationId: text("applicationId")
.notNull()
.references(() => applications.applicationId, { onDelete: "cascade" }),
});
export const redirectRelations = relations(redirects, ({ one }) => ({
application: one(applications, {
fields: [redirects.applicationId],
references: [applications.applicationId],
}),
}));
const createSchema = createInsertSchema(redirects, {
redirectId: z.string().min(1),
regex: z.string().min(1),
replacement: z.string().min(1),
permanent: z.boolean().optional(),
});
export const apiFindOneRedirect = createSchema
.pick({
redirectId: true,
})
.required();
export const apiCreateRedirect = createSchema
.pick({
regex: true,
replacement: true,
permanent: true,
applicationId: true,
})
.required();
export const apiUpdateRedirect = createSchema
.pick({
redirectId: true,
regex: true,
replacement: true,
permanent: true,
})
.required();

122
server/db/schema/redis.ts Normal file
View File

@@ -0,0 +1,122 @@
import { relations } from "drizzle-orm";
import { z } from "zod";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { applicationStatus } from "./shared";
import { integer, pgTable, text } from "drizzle-orm/pg-core";
import { projects } from "./project";
import { mounts } from "./mount";
import { generateAppName } from "./utils";
export const redis = pgTable("redis", {
redisId: text("redisId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
name: text("name").notNull(),
appName: text("appName")
.notNull()
.$defaultFn(() => generateAppName("redis"))
.unique(),
description: text("description"),
databasePassword: text("password").notNull(),
dockerImage: text("dockerImage").notNull(),
command: text("command"),
env: text("env"),
memoryReservation: integer("memoryReservation"),
memoryLimit: integer("memoryLimit"),
cpuReservation: integer("cpuReservation"),
cpuLimit: integer("cpuLimit"),
externalPort: integer("externalPort"),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
applicationStatus: applicationStatus("applicationStatus")
.notNull()
.default("idle"),
projectId: text("projectId")
.notNull()
.references(() => projects.projectId, { onDelete: "cascade" }),
});
export const redisRelations = relations(redis, ({ one, many }) => ({
project: one(projects, {
fields: [redis.projectId],
references: [projects.projectId],
}),
mounts: many(mounts),
}));
const createSchema = createInsertSchema(redis, {
redisId: z.string(),
appName: z.string().min(1),
createdAt: z.string(),
name: z.string().min(1),
databasePassword: z.string(),
dockerImage: z.string().default("redis:8"),
command: z.string().optional(),
env: z.string().optional(),
memoryReservation: z.number().optional(),
memoryLimit: z.number().optional(),
cpuReservation: z.number().optional(),
cpuLimit: z.number().optional(),
projectId: z.string(),
applicationStatus: z.enum(["idle", "running", "done", "error"]),
externalPort: z.number(),
description: z.string().optional(),
});
export const apiCreateRedis = createSchema
.pick({
name: true,
databasePassword: true,
dockerImage: true,
projectId: true,
description: true,
})
.required();
export const apiFindOneRedis = createSchema
.pick({
redisId: true,
})
.required();
export const apiChangeRedisStatus = createSchema
.pick({
redisId: true,
applicationStatus: true,
})
.required();
export const apiSaveEnviromentVariablesRedis = createSchema
.pick({
redisId: true,
env: true,
})
.required();
export const apiSaveExternalPortRedis = createSchema
.pick({
redisId: true,
externalPort: true,
})
.required();
export const apiDeployRedis = createSchema
.pick({
redisId: true,
})
.required();
export const apiResetRedis = createSchema
.pick({
redisId: true,
appName: true,
})
.required();
export const apiUpdateRedis = createSchema.partial().extend({
redisId: z.string().min(1),
});

View File

@@ -0,0 +1,61 @@
import { relations } from "drizzle-orm";
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { z } from "zod";
import { pgTable, text, unique } from "drizzle-orm/pg-core";
import { applications } from "./application";
export const security = pgTable(
"security",
{
securityId: text("securityId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
username: text("username").notNull(),
password: text("password").notNull(),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
applicationId: text("applicationId")
.notNull()
.references(() => applications.applicationId, { onDelete: "cascade" }),
},
(t) => ({
unq: unique().on(t.username, t.applicationId),
}),
);
export const securityRelations = relations(security, ({ one }) => ({
application: one(applications, {
fields: [security.applicationId],
references: [applications.applicationId],
}),
}));
const createSchema = createInsertSchema(security, {
securityId: z.string().min(1),
username: z.string().min(1),
password: z.string().min(1),
});
export const apiFindOneSecurity = createSchema
.pick({
securityId: true,
})
.required();
export const apiCreateSecurity = createSchema
.pick({
applicationId: true,
username: true,
password: true,
})
.required();
export const apiUpdateSecurity = createSchema
.pick({
securityId: true,
username: true,
password: true,
})
.required();

View File

@@ -0,0 +1,20 @@
import { auth } from "./auth";
import { pgTable, text, timestamp } from "drizzle-orm/pg-core";
// export const sessionTable = sqliteTable("session", {
// id: text("id").notNull().primaryKey(),
// userId: text("user_id")
// .notNull()
// .references(() => users.id),
// expiresAt: integer("expires_at").notNull(),
// });
export const sessionTable = pgTable("session", {
id: text("id").primaryKey(),
userId: text("user_id")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
expiresAt: timestamp("expires_at", {
withTimezone: true,
mode: "date",
}).notNull(),
});

View File

@@ -0,0 +1,13 @@
import { pgEnum } from "drizzle-orm/pg-core";
export const applicationStatus = pgEnum("applicationStatus", [
"idle",
"running",
"done",
"error",
]);
export const certificateType = pgEnum("certificateType", [
"letsencrypt",
"none",
]);

View File

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

121
server/db/schema/user.ts Normal file
View File

@@ -0,0 +1,121 @@
import { createInsertSchema } from "drizzle-zod";
import { nanoid } from "nanoid";
import { relations, sql } from "drizzle-orm";
import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core";
import { auth } from "./auth";
import { admins } from "./admin";
import { z } from "zod";
/**
* This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same
* database instance for multiple projects.
*
* @see https://orm.drizzle.team/docs/goodies#multi-project-schema
*/
export const users = pgTable("user", {
userId: text("userId")
.notNull()
.primaryKey()
.$defaultFn(() => nanoid()),
token: text("token").notNull(),
isRegistered: boolean("isRegistered").notNull().default(false),
expirationDate: timestamp("expirationDate", {
precision: 3,
mode: "string",
}).notNull(),
createdAt: text("createdAt")
.notNull()
.$defaultFn(() => new Date().toISOString()),
canCreateProjects: boolean("canCreateProjects").notNull().default(false),
canCreateServices: boolean("canCreateServices").notNull().default(false),
canDeleteProjects: boolean("canDeleteProjects").notNull().default(false),
canDeleteServices: boolean("canDeleteServices").notNull().default(false),
canAccessToDocker: boolean("canAccessToDocker").notNull().default(false),
canAccessToTraefikFiles: boolean("canAccessToTraefikFiles")
.notNull()
.default(false),
accesedProjects: text("accesedProjects")
.array()
.notNull()
.default(sql`ARRAY[]::text[]`),
accesedServices: text("accesedServices")
.array()
.notNull()
.default(sql`ARRAY[]::text[]`),
adminId: text("adminId")
.notNull()
.references(() => admins.adminId, { onDelete: "cascade" }),
authId: text("authId")
.notNull()
.references(() => auth.id, { onDelete: "cascade" }),
});
export const usersRelations = relations(users, ({ one }) => ({
auth: one(auth, {
fields: [users.authId],
references: [auth.id],
}),
admin: one(admins, {
fields: [users.adminId],
references: [admins.adminId],
}),
}));
const createSchema = createInsertSchema(users, {
userId: z.string().min(1),
authId: z.string().min(1),
token: z.string().min(1),
isRegistered: z.boolean().optional(),
adminId: z.string(),
accesedProjects: z.array(z.string()).optional(),
accesedServices: z.array(z.string()).optional(),
canCreateProjects: z.boolean().optional(),
canCreateServices: z.boolean().optional(),
canDeleteProjects: z.boolean().optional(),
canDeleteServices: z.boolean().optional(),
canAccessToDocker: z.boolean().optional(),
canAccessToTraefikFiles: z.boolean().optional(),
});
export const apiCreateUserInvitation = createSchema.pick({}).extend({
email: z.string().email(),
});
export const apiRemoveUser = createSchema
.pick({
authId: true,
})
.required();
export const apiFindOneToken = createSchema
.pick({
token: true,
})
.required();
export const apiAssignPermissions = createSchema
.pick({
userId: true,
canCreateProjects: true,
canCreateServices: true,
canDeleteProjects: true,
canDeleteServices: true,
accesedProjects: true,
accesedServices: true,
canAccessToTraefikFiles: true,
canAccessToDocker: true,
})
.required();
export const apiFindOneUser = createSchema
.pick({
userId: true,
})
.required();
export const apiFindOneUserByAuth = createSchema
.pick({
authId: true,
})
.required();

15
server/db/schema/utils.ts Normal file
View File

@@ -0,0 +1,15 @@
import { faker } from "@faker-js/faker";
import { customAlphabet } from "nanoid";
const alphabet = "abcdefghijklmnopqrstuvwxyz123456789";
const customNanoid = customAlphabet(alphabet, 6);
export const generateAppName = (type: string) => {
const verb = faker.hacker.verb().replace(/ /g, "-");
const adjective = faker.hacker.adjective().replace(/ /g, "-");
const noun = faker.hacker.noun().replace(/ /g, "-");
const randomFakerElement = `${verb}-${adjective}-${noun}`;
const nanoidPart = customNanoid();
return `${type}-${randomFakerElement}-${nanoidPart}`;
};