From c04bf3c7e03a09fd840bbbf5cb1c5dd62a0e4d9c Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 9 Feb 2025 18:19:21 -0600 Subject: [PATCH 001/126] feat: add migration --- apps/dokploy/drizzle/0066_broad_marrow.sql | 255 + apps/dokploy/drizzle/meta/0066_snapshot.json | 4686 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/lib/auth.ts | 4 + apps/dokploy/package.json | 1 + apps/dokploy/pages/api/auth/[...all].ts | 7 + apps/dokploy/pages/api/stripe/webhook.ts | 368 +- .../pages/dashboard/project/[projectId].tsx | 4 +- apps/dokploy/pages/index.tsx | 74 +- apps/dokploy/server/server.ts | 16 +- packages/server/auth-schema.ts | 62 + packages/server/package.json | 1 + packages/server/src/db/schema/account.ts | 34 + packages/server/src/db/schema/admin.ts | 195 +- packages/server/src/db/schema/auth.ts | 4 +- packages/server/src/db/schema/certificate.ts | 10 +- packages/server/src/db/schema/destination.ts | 11 +- packages/server/src/db/schema/git-provider.ts | 9 +- packages/server/src/db/schema/index.ts | 1 + packages/server/src/db/schema/notification.ts | 9 +- packages/server/src/db/schema/project.ts | 13 +- packages/server/src/db/schema/registry.ts | 13 +- packages/server/src/db/schema/server.ts | 11 +- packages/server/src/db/schema/session.ts | 16 +- packages/server/src/db/schema/ssh-key.ts | 11 +- packages/server/src/db/schema/user.ts | 117 +- packages/server/src/index.ts | 2 + packages/server/src/lib/auth.ts | 14 + packages/server/src/services/admin.ts | 4 +- .../server/src/utils/access-log/handler.ts | 65 +- pnpm-lock.yaml | 206 +- 31 files changed, 5790 insertions(+), 440 deletions(-) create mode 100644 apps/dokploy/drizzle/0066_broad_marrow.sql create mode 100644 apps/dokploy/drizzle/meta/0066_snapshot.json create mode 100644 apps/dokploy/lib/auth.ts create mode 100644 apps/dokploy/pages/api/auth/[...all].ts create mode 100644 packages/server/auth-schema.ts create mode 100644 packages/server/src/db/schema/account.ts create mode 100644 packages/server/src/lib/auth.ts diff --git a/apps/dokploy/drizzle/0066_broad_marrow.sql b/apps/dokploy/drizzle/0066_broad_marrow.sql new file mode 100644 index 000000000..f10854144 --- /dev/null +++ b/apps/dokploy/drizzle/0066_broad_marrow.sql @@ -0,0 +1,255 @@ +CREATE TABLE "account" ( + "id" text PRIMARY KEY NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamp, + "refresh_token_expires_at" timestamp, + "scope" text, + "password" text, + "is2FAEnabled" boolean DEFAULT false NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "resetPasswordToken" text, + "resetPasswordExpiresAt" text, + "confirmationToken" text, + "confirmationExpiresAt" text +); + +--> statement-breakpoint +CREATE TABLE "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp, + "updated_at" timestamp +); + +-- Primero eliminar las restricciones NOT NULL y foreign keys +ALTER TABLE "user" ALTER COLUMN "adminId" DROP NOT NULL; +ALTER TABLE "user" ALTER COLUMN "authId" DROP NOT NULL; + +ALTER TABLE "user" DROP CONSTRAINT IF EXISTS "user_adminId_admin_adminId_fk"; +ALTER TABLE "user" DROP CONSTRAINT IF EXISTS "user_authId_auth_id_fk"; +ALTER TABLE "admin" DROP CONSTRAINT IF EXISTS "admin_authId_auth_id_fk"; +ALTER TABLE "project" DROP CONSTRAINT IF EXISTS "project_adminId_admin_adminId_fk"; +ALTER TABLE "destination" DROP CONSTRAINT IF EXISTS "destination_adminId_admin_adminId_fk"; +ALTER TABLE "certificate" DROP CONSTRAINT IF EXISTS "certificate_adminId_admin_adminId_fk"; +ALTER TABLE "session" DROP CONSTRAINT IF EXISTS "session_user_id_auth_id_fk"; +ALTER TABLE "registry" DROP CONSTRAINT IF EXISTS "registry_adminId_admin_adminId_fk"; +ALTER TABLE "notification" DROP CONSTRAINT IF EXISTS "notification_adminId_admin_adminId_fk"; +ALTER TABLE "ssh-key" DROP CONSTRAINT IF EXISTS "ssh-key_adminId_admin_adminId_fk"; +ALTER TABLE "git_provider" DROP CONSTRAINT IF EXISTS "git_provider_adminId_admin_adminId_fk"; +ALTER TABLE "server" DROP CONSTRAINT IF EXISTS "server_adminId_admin_adminId_fk"; + +-- Luego renombrar las columnas +ALTER TABLE "user" RENAME COLUMN "userId" TO "id"; +ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "ssh-key" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId"; +ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId"; + +-- Primero agregar todas las columnas sin restricciones +ALTER TABLE "user" ADD COLUMN "name" text; +ALTER TABLE "user" ADD COLUMN "email" text; +ALTER TABLE "user" ADD COLUMN "email_verified" boolean; +ALTER TABLE "user" ADD COLUMN "image" text; +ALTER TABLE "user" ADD COLUMN "role" text; +ALTER TABLE "user" ADD COLUMN "banned" boolean; +ALTER TABLE "user" ADD COLUMN "ban_reason" text; +ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp; +ALTER TABLE "user" ADD COLUMN "updated_at" timestamp; +ALTER TABLE "user" ADD COLUMN "serverIp" text; +ALTER TABLE "user" ADD COLUMN "certificateType" "certificateType" DEFAULT 'none'; +ALTER TABLE "user" ADD COLUMN "host" text; +ALTER TABLE "user" ADD COLUMN "letsEncryptEmail" text; +ALTER TABLE "user" ADD COLUMN "sshPrivateKey" text; +ALTER TABLE "user" ADD COLUMN "enableDockerCleanup" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "enableLogRotation" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "enablePaidFeatures" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'; +ALTER TABLE "user" ADD COLUMN "cleanupCacheApplications" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "cleanupCacheOnPreviews" boolean DEFAULT false; +ALTER TABLE "user" ADD COLUMN "cleanupCacheOnCompose" boolean DEFAULT false; + +ALTER TABLE "user" ALTER COLUMN "token" SET DEFAULT ''; +ALTER TABLE "user" ALTER COLUMN "expirationDate" SET DEFAULT CURRENT_TIMESTAMP + INTERVAL '1 year'; +ALTER TABLE "user" ALTER COLUMN "createdAt" SET DEFAULT to_char(CURRENT_TIMESTAMP, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'); + +--> statement-breakpoint +-- Luego actualizar los valores nulos +UPDATE "user" SET token = '' WHERE token IS NULL; +UPDATE "user" SET "expirationDate" = CURRENT_TIMESTAMP + INTERVAL '1 year' WHERE "expirationDate" IS NULL; +UPDATE "user" SET "createdAt" = to_char(CURRENT_TIMESTAMP, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') WHERE "createdAt" IS NULL; +UPDATE "user" SET "name" = '' WHERE "name" IS NULL; +UPDATE "user" SET "email" = COALESCE("email", '') WHERE true; +UPDATE "user" SET "email_verified" = COALESCE("email_verified", false) WHERE true; +UPDATE "user" SET "role" = COALESCE("role", 'user') WHERE true; +UPDATE "user" SET "banned" = COALESCE("banned", false) WHERE true; +UPDATE "user" SET "updated_at" = COALESCE("updated_at", CURRENT_TIMESTAMP) WHERE true; +UPDATE "user" SET "certificateType" = COALESCE("certificateType", 'none') WHERE true; +UPDATE "user" SET "enableDockerCleanup" = COALESCE("enableDockerCleanup", false) WHERE true; +UPDATE "user" SET "enableLogRotation" = COALESCE("enableLogRotation", false) WHERE true; +UPDATE "user" SET "enablePaidFeatures" = COALESCE("enablePaidFeatures", false) WHERE true; +UPDATE "user" SET "metricsConfig" = COALESCE("metricsConfig", '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}') WHERE true; +UPDATE "user" SET "cleanupCacheApplications" = COALESCE("cleanupCacheApplications", false) WHERE true; +UPDATE "user" SET "cleanupCacheOnPreviews" = COALESCE("cleanupCacheOnPreviews", false) WHERE true; +UPDATE "user" SET "cleanupCacheOnCompose" = COALESCE("cleanupCacheOnCompose", false) WHERE true; +--> statement-breakpoint + +-- Migrar datos de auth a user +INSERT INTO "user" ( + id, + name, + email, + email_verified, + image, + role, + updated_at +) +SELECT + id, + '' as name, + email, + true as email_verified, + image, + CASE + WHEN rol = 'admin' THEN 'admin' + ELSE 'user' + END as role, + CAST("createdAt" AS timestamp) as updated_at +FROM "auth"; + +-- Migrar datos de admin a user +UPDATE "user" u +SET + "serverIp" = a."serverIp", + "certificateType" = a."certificateType", + "host" = a."host", + "letsEncryptEmail" = a."letsEncryptEmail", + "sshPrivateKey" = a."sshPrivateKey", + "enableDockerCleanup" = a."enableDockerCleanup", + "enableLogRotation" = a."enableLogRotation", + "enablePaidFeatures" = a."enablePaidFeatures", + "metricsConfig" = a."metricsConfig", + "cleanupCacheApplications" = a."cleanupCacheApplications", + "cleanupCacheOnPreviews" = a."cleanupCacheOnPreviews", + "cleanupCacheOnCompose" = a."cleanupCacheOnCompose" +FROM "admin" a +WHERE u.id = a."authId"; + +-- Actualizar referencias en las tablas relacionadas +UPDATE "project" p +SET "userId" = a."authId" +FROM "admin" a +WHERE p."userId" = a."adminId"; + +UPDATE "destination" d +SET "userId" = a."authId" +FROM "admin" a +WHERE d."userId" = a."adminId"; + +UPDATE "certificate" c +SET "userId" = a."authId" +FROM "admin" a +WHERE c."userId" = a."adminId"; + +UPDATE "registry" r +SET "userId" = a."authId" +FROM "admin" a +WHERE r."userId" = a."adminId"; + +UPDATE "notification" n +SET "userId" = a."authId" +FROM "admin" a +WHERE n."userId" = a."adminId"; + +UPDATE "ssh-key" s +SET "userId" = a."authId" +FROM "admin" a +WHERE s."userId" = a."adminId"; + +UPDATE "git_provider" g +SET "userId" = a."authId" +FROM "admin" a +WHERE g."userId" = a."adminId"; + +UPDATE "server" s +SET "userId" = a."authId" +FROM "admin" a +WHERE s."userId" = a."adminId"; + +-- Ahora agregar las restricciones NOT NULL después de migrar los datos +ALTER TABLE "user" ALTER COLUMN "name" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "email" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "email_verified" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "updated_at" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "certificateType" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "enableDockerCleanup" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "enableLogRotation" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "enablePaidFeatures" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "metricsConfig" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "cleanupCacheApplications" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "cleanupCacheOnPreviews" SET NOT NULL; +ALTER TABLE "user" ALTER COLUMN "cleanupCacheOnCompose" SET NOT NULL; + +-- Modificar session +ALTER TABLE "session" ALTER COLUMN "expires_at" SET DATA TYPE timestamp; +ALTER TABLE "session" ADD COLUMN "token" text; +ALTER TABLE "session" ADD COLUMN "created_at" timestamp; +ALTER TABLE "session" ADD COLUMN "updated_at" timestamp; +ALTER TABLE "session" ADD COLUMN "ip_address" text; +ALTER TABLE "session" ADD COLUMN "user_agent" text; +ALTER TABLE "session" ADD COLUMN "impersonated_by" text; + +-- Agregar nuevas restricciones después de migrar todos los datos +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; + +-- Agregar restricciones únicas +ALTER TABLE "user" ADD CONSTRAINT "user_email_unique" UNIQUE("email"); +ALTER TABLE "session" ADD CONSTRAINT "session_token_unique" UNIQUE("token"); + +-- Eliminar columnas antiguas +ALTER TABLE "user" DROP COLUMN IF EXISTS "adminId"; +ALTER TABLE "user" DROP COLUMN IF EXISTS "authId"; + +-- Eliminar columnas de admin +ALTER TABLE "admin" DROP COLUMN IF EXISTS "adminId"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "serverIp"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "certificateType"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "host"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "letsEncryptEmail"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "sshPrivateKey"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "enableDockerCleanup"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "enableLogRotation"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "authId"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "createdAt"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "stripeCustomerId"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "stripeSubscriptionId"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "serversQuantity"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "enablePaidFeatures"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "metricsConfig"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "cleanupCacheApplications"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "cleanupCacheOnPreviews"; +ALTER TABLE "admin" DROP COLUMN IF EXISTS "cleanupCacheOnCompose"; + +-- Eliminar tablas antiguas +DROP TABLE IF EXISTS "auth" CASCADE; +DROP TABLE IF EXISTS "admin" CASCADE; diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json new file mode 100644 index 000000000..8572b47b4 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -0,0 +1,4686 @@ +{ + "id": "e7c50cc6-9e18-47c5-b155-dd31fc8bd774", + "prevId": "1240ec96-1751-4de3-b64f-cef9cb716786", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": {}, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_id_fk": { + "name": "destination_userId_user_id_fk", + "tableFrom": "destination", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_id_fk": { + "name": "certificate_userId_user_id_fk", + "tableFrom": "certificate", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_id_fk": { + "name": "registry_userId_user_id_fk", + "tableFrom": "registry", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_userId_user_id_fk": { + "name": "notification_userId_user_id_fk", + "tableFrom": "notification", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_id_fk": { + "name": "ssh-key_userId_user_id_fk", + "tableFrom": "ssh-key", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_id_fk": { + "name": "git_provider_userId_user_id_fk", + "tableFrom": "git_provider", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_id_fk": { + "name": "server_userId_user_id_fk", + "tableFrom": "server", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 588d36eb7..8d09d2090 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -463,6 +463,13 @@ "when": 1739087857244, "tag": "0065_daily_zaladane", "breakpoints": true + }, + { + "idx": 66, + "version": "7", + "when": 1739142819089, + "tag": "0066_broad_marrow", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/lib/auth.ts b/apps/dokploy/lib/auth.ts new file mode 100644 index 000000000..d0b50c7e3 --- /dev/null +++ b/apps/dokploy/lib/auth.ts @@ -0,0 +1,4 @@ +import { createAuthClient } from "better-auth/react"; +export const authClient = createAuthClient({ + baseURL: "http://localhost:3000", // the base url of your auth server +}); diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 213c90739..e2a7c4a2b 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -35,6 +35,7 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { + "better-auth":"1.1.16", "bl": "6.0.11", "rotating-file-stream": "3.2.3", "qrcode": "^1.5.3", diff --git a/apps/dokploy/pages/api/auth/[...all].ts b/apps/dokploy/pages/api/auth/[...all].ts new file mode 100644 index 000000000..48aa03706 --- /dev/null +++ b/apps/dokploy/pages/api/auth/[...all].ts @@ -0,0 +1,7 @@ +import { auth } from "@dokploy/server/index"; +import { toNodeHandler } from "better-auth/node"; + +// Disallow body parsing, we will parse it manually +export const config = { api: { bodyParser: false } }; + +export default toNodeHandler(auth.handler); diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index d4599f784..7701ebd98 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -18,224 +18,224 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - if (!endpointSecret) { - return res.status(400).send("Webhook Error: Missing Stripe Secret Key"); - } - const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { - apiVersion: "2024-09-30.acacia", - maxNetworkRetries: 3, - }); + // if (!endpointSecret) { + // return res.status(400).send("Webhook Error: Missing Stripe Secret Key"); + // } + // const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { + // apiVersion: "2024-09-30.acacia", + // maxNetworkRetries: 3, + // }); - const buf = await buffer(req); - const sig = req.headers["stripe-signature"] as string; + // const buf = await buffer(req); + // const sig = req.headers["stripe-signature"] as string; - let event: Stripe.Event; + // let event: Stripe.Event; - try { - event = stripe.webhooks.constructEvent(buf, sig, endpointSecret); - } catch (err) { - console.error( - "Webhook signature verification failed.", - err instanceof Error ? err.message : err, - ); - return res.status(400).send("Webhook Error: "); - } + // try { + // event = stripe.webhooks.constructEvent(buf, sig, endpointSecret); + // } catch (err) { + // console.error( + // "Webhook signature verification failed.", + // err instanceof Error ? err.message : err, + // ); + // return res.status(400).send("Webhook Error: "); + // } - const webhooksAllowed = [ - "customer.subscription.created", - "customer.subscription.deleted", - "customer.subscription.updated", - "invoice.payment_succeeded", - "invoice.payment_failed", - "customer.deleted", - "checkout.session.completed", - ]; + // const webhooksAllowed = [ + // "customer.subscription.created", + // "customer.subscription.deleted", + // "customer.subscription.updated", + // "invoice.payment_succeeded", + // "invoice.payment_failed", + // "customer.deleted", + // "checkout.session.completed", + // ]; - if (!webhooksAllowed.includes(event.type)) { - return res.status(400).send("Webhook Error: Invalid Event Type"); - } + // if (!webhooksAllowed.includes(event.type)) { + // return res.status(400).send("Webhook Error: Invalid Event Type"); + // } - switch (event.type) { - case "checkout.session.completed": { - const session = event.data.object as Stripe.Checkout.Session; - const adminId = session?.metadata?.adminId as string; + // switch (event.type) { + // case "checkout.session.completed": { + // const session = event.data.object as Stripe.Checkout.Session; + // const adminId = session?.metadata?.adminId as string; - const subscription = await stripe.subscriptions.retrieve( - session.subscription as string, - ); - await db - .update(admins) - .set({ - stripeCustomerId: session.customer as string, - stripeSubscriptionId: session.subscription as string, - serversQuantity: subscription?.items?.data?.[0]?.quantity ?? 0, - }) - .where(eq(admins.adminId, adminId)) - .returning(); + // const subscription = await stripe.subscriptions.retrieve( + // session.subscription as string, + // ); + // await db + // .update(admins) + // .set({ + // stripeCustomerId: session.customer as string, + // stripeSubscriptionId: session.subscription as string, + // serversQuantity: subscription?.items?.data?.[0]?.quantity ?? 0, + // }) + // .where(eq(admins.adminId, adminId)) + // .returning(); - const admin = await findAdminById(adminId); - if (!admin) { - return res.status(400).send("Webhook Error: Admin not found"); - } - const newServersQuantity = admin.serversQuantity; - await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); - break; - } - case "customer.subscription.created": { - const newSubscription = event.data.object as Stripe.Subscription; + // const admin = await findAdminById(adminId); + // if (!admin) { + // return res.status(400).send("Webhook Error: Admin not found"); + // } + // const newServersQuantity = admin.serversQuantity; + // await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); + // break; + // } + // case "customer.subscription.created": { + // const newSubscription = event.data.object as Stripe.Subscription; - await db - .update(admins) - .set({ - stripeSubscriptionId: newSubscription.id, - stripeCustomerId: newSubscription.customer as string, - }) - .where(eq(admins.stripeCustomerId, newSubscription.customer as string)) - .returning(); + // await db + // .update(admins) + // .set({ + // stripeSubscriptionId: newSubscription.id, + // stripeCustomerId: newSubscription.customer as string, + // }) + // .where(eq(admins.stripeCustomerId, newSubscription.customer as string)) + // .returning(); - break; - } + // break; + // } - case "customer.subscription.deleted": { - const newSubscription = event.data.object as Stripe.Subscription; + // case "customer.subscription.deleted": { + // const newSubscription = event.data.object as Stripe.Subscription; - await db - .update(admins) - .set({ - stripeSubscriptionId: null, - serversQuantity: 0, - }) - .where(eq(admins.stripeCustomerId, newSubscription.customer as string)); + // await db + // .update(admins) + // .set({ + // stripeSubscriptionId: null, + // serversQuantity: 0, + // }) + // .where(eq(admins.stripeCustomerId, newSubscription.customer as string)); - const admin = await findAdminByStripeCustomerId( - newSubscription.customer as string, - ); + // const admin = await findAdminByStripeCustomerId( + // newSubscription.customer as string, + // ); - if (!admin) { - return res.status(400).send("Webhook Error: Admin not found"); - } + // if (!admin) { + // return res.status(400).send("Webhook Error: Admin not found"); + // } - await disableServers(admin.adminId); - break; - } - case "customer.subscription.updated": { - const newSubscription = event.data.object as Stripe.Subscription; + // await disableServers(admin.adminId); + // break; + // } + // case "customer.subscription.updated": { + // const newSubscription = event.data.object as Stripe.Subscription; - const admin = await findAdminByStripeCustomerId( - newSubscription.customer as string, - ); + // const admin = await findAdminByStripeCustomerId( + // newSubscription.customer as string, + // ); - if (!admin) { - return res.status(400).send("Webhook Error: Admin not found"); - } + // if (!admin) { + // return res.status(400).send("Webhook Error: Admin not found"); + // } - if (newSubscription.status === "active") { - await db - .update(admins) - .set({ - serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, - }) - .where( - eq(admins.stripeCustomerId, newSubscription.customer as string), - ); + // if (newSubscription.status === "active") { + // await db + // .update(admins) + // .set({ + // serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, + // }) + // .where( + // eq(admins.stripeCustomerId, newSubscription.customer as string), + // ); - const newServersQuantity = admin.serversQuantity; - await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); - } else { - await disableServers(admin.adminId); - await db - .update(admins) - .set({ serversQuantity: 0 }) - .where( - eq(admins.stripeCustomerId, newSubscription.customer as string), - ); - } + // const newServersQuantity = admin.serversQuantity; + // await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); + // } else { + // await disableServers(admin.adminId); + // await db + // .update(admins) + // .set({ serversQuantity: 0 }) + // .where( + // eq(admins.stripeCustomerId, newSubscription.customer as string), + // ); + // } - break; - } - case "invoice.payment_succeeded": { - const newInvoice = event.data.object as Stripe.Invoice; + // break; + // } + // case "invoice.payment_succeeded": { + // const newInvoice = event.data.object as Stripe.Invoice; - const suscription = await stripe.subscriptions.retrieve( - newInvoice.subscription as string, - ); + // const suscription = await stripe.subscriptions.retrieve( + // newInvoice.subscription as string, + // ); - if (suscription.status !== "active") { - console.log( - `Skipping invoice.payment_succeeded for subscription ${suscription.id} with status ${suscription.status}`, - ); - break; - } + // if (suscription.status !== "active") { + // console.log( + // `Skipping invoice.payment_succeeded for subscription ${suscription.id} with status ${suscription.status}`, + // ); + // break; + // } - await db - .update(admins) - .set({ - serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, - }) - .where(eq(admins.stripeCustomerId, suscription.customer as string)); + // await db + // .update(admins) + // .set({ + // serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, + // }) + // .where(eq(admins.stripeCustomerId, suscription.customer as string)); - const admin = await findAdminByStripeCustomerId( - suscription.customer as string, - ); + // const admin = await findAdminByStripeCustomerId( + // suscription.customer as string, + // ); - if (!admin) { - return res.status(400).send("Webhook Error: Admin not found"); - } - const newServersQuantity = admin.serversQuantity; - await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); - break; - } - case "invoice.payment_failed": { - const newInvoice = event.data.object as Stripe.Invoice; + // if (!admin) { + // return res.status(400).send("Webhook Error: Admin not found"); + // } + // const newServersQuantity = admin.serversQuantity; + // await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); + // break; + // } + // case "invoice.payment_failed": { + // const newInvoice = event.data.object as Stripe.Invoice; - const subscription = await stripe.subscriptions.retrieve( - newInvoice.subscription as string, - ); + // const subscription = await stripe.subscriptions.retrieve( + // newInvoice.subscription as string, + // ); - if (subscription.status !== "active") { - const admin = await findAdminByStripeCustomerId( - newInvoice.customer as string, - ); + // if (subscription.status !== "active") { + // const admin = await findAdminByStripeCustomerId( + // newInvoice.customer as string, + // ); - if (!admin) { - return res.status(400).send("Webhook Error: Admin not found"); - } - await db - .update(admins) - .set({ - serversQuantity: 0, - }) - .where(eq(admins.stripeCustomerId, newInvoice.customer as string)); + // if (!admin) { + // return res.status(400).send("Webhook Error: Admin not found"); + // } + // await db + // .update(admins) + // .set({ + // serversQuantity: 0, + // }) + // .where(eq(admins.stripeCustomerId, newInvoice.customer as string)); - await disableServers(admin.adminId); - } + // await disableServers(admin.adminId); + // } - break; - } + // break; + // } - case "customer.deleted": { - const customer = event.data.object as Stripe.Customer; + // case "customer.deleted": { + // const customer = event.data.object as Stripe.Customer; - const admin = await findAdminByStripeCustomerId(customer.id); - if (!admin) { - return res.status(400).send("Webhook Error: Admin not found"); - } + // const admin = await findAdminByStripeCustomerId(customer.id); + // if (!admin) { + // return res.status(400).send("Webhook Error: Admin not found"); + // } - await disableServers(admin.adminId); - await db - .update(admins) - .set({ - stripeCustomerId: null, - stripeSubscriptionId: null, - serversQuantity: 0, - }) - .where(eq(admins.stripeCustomerId, customer.id)); + // await disableServers(admin.adminId); + // await db + // .update(admins) + // .set({ + // stripeCustomerId: null, + // stripeSubscriptionId: null, + // serversQuantity: 0, + // }) + // .where(eq(admins.stripeCustomerId, customer.id)); - break; - } - default: - console.log(`Unhandled event type: ${event.type}`); - } + // break; + // } + // default: + // console.log(`Unhandled event type: ${event.type}`); + // } return res.status(200).json({ received: true }); } diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 26317ca92..ea23ad3a8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -70,9 +70,9 @@ import type { } from "next"; import Head from "next/head"; import { useRouter } from "next/router"; -import { useMemo, useState, type ReactElement } from "react"; -import superjson from "superjson"; +import { type ReactElement, useMemo, useState } from "react"; import { toast } from "sonner"; +import superjson from "superjson"; export type Services = { appName: string; diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 2c5ab0bbd..3aafe9bf9 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -201,51 +201,51 @@ Home.getLayout = (page: ReactElement) => { return {page}; }; export async function getServerSideProps(context: GetServerSidePropsContext) { - if (IS_CLOUD) { - try { - const { user } = await validateRequest(context.req, context.res); + // if (IS_CLOUD) { + // try { + // const { user } = await validateRequest(context.req, context.res); - if (user) { - return { - redirect: { - permanent: true, - destination: "/dashboard/projects", - }, - }; - } - } catch (error) {} + // if (user) { + // return { + // redirect: { + // permanent: true, + // destination: "/dashboard/projects", + // }, + // }; + // } + // } catch (error) {} - return { - props: { - IS_CLOUD: IS_CLOUD, - }, - }; - } - const hasAdmin = await isAdminPresent(); + // return { + // props: { + // IS_CLOUD: IS_CLOUD, + // }, + // }; + // } + // const hasAdmin = await isAdminPresent(); - if (!hasAdmin) { - return { - redirect: { - permanent: true, - destination: "/register", - }, - }; - } + // if (!hasAdmin) { + // return { + // redirect: { + // permanent: true, + // destination: "/register", + // }, + // }; + // } - const { user } = await validateRequest(context.req, context.res); + // const { user } = await validateRequest(context.req, context.res); - if (user) { - return { - redirect: { - permanent: true, - destination: "/dashboard/projects", - }, - }; - } + // if (user) { + // return { + // redirect: { + // permanent: true, + // destination: "/dashboard/projects", + // }, + // }; + // } return { props: { - hasAdmin, + // hasAdmin, }, }; } diff --git a/apps/dokploy/server/server.ts b/apps/dokploy/server/server.ts index c8f53f6f9..a277dc0ac 100644 --- a/apps/dokploy/server/server.ts +++ b/apps/dokploy/server/server.ts @@ -34,14 +34,14 @@ void app.prepare().then(async () => { }); // WEBSOCKET - setupDrawerLogsWebSocketServer(server); - setupDeploymentLogsWebSocketServer(server); - setupDockerContainerLogsWebSocketServer(server); - setupDockerContainerTerminalWebSocketServer(server); - setupTerminalWebSocketServer(server); - if (!IS_CLOUD) { - setupDockerStatsMonitoringSocketServer(server); - } + // setupDrawerLogsWebSocketServer(server); + // setupDeploymentLogsWebSocketServer(server); + // setupDockerContainerLogsWebSocketServer(server); + // setupDockerContainerTerminalWebSocketServer(server); + // setupTerminalWebSocketServer(server); + // if (!IS_CLOUD) { + // setupDockerStatsMonitoringSocketServer(server); + // } if (process.env.NODE_ENV === "production" && !IS_CLOUD) { setupDirectories(); diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts new file mode 100644 index 000000000..8f8be24d9 --- /dev/null +++ b/packages/server/auth-schema.ts @@ -0,0 +1,62 @@ +import { + pgTable, + text, + integer, + timestamp, + boolean, +} from "drizzle-orm/pg-core"; + +export const user = pgTable("user", { + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").notNull(), + image: text("image"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + role: text("role"), + banned: boolean("banned"), + banReason: text("ban_reason"), + banExpires: timestamp("ban_expires"), +}); + +export const session = pgTable("session", { + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at").notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id") + .notNull() + .references(() => user.id), + impersonatedBy: text("impersonated_by"), +}); + +// export const account = pgTable("account", { +// id: text("id").primaryKey(), +// accountId: text("account_id").notNull(), +// providerId: text("provider_id").notNull(), +// userId: text("user_id") +// .notNull() +// .references(() => user.id), +// accessToken: text("access_token"), +// refreshToken: text("refresh_token"), +// idToken: text("id_token"), +// accessTokenExpiresAt: timestamp("access_token_expires_at"), +// refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), +// scope: text("scope"), +// password: text("password"), +// createdAt: timestamp("created_at").notNull(), +// updatedAt: timestamp("updated_at").notNull(), +// }); + +// export const verification = pgTable("verification", { +// id: text("id").primaryKey(), +// identifier: text("identifier").notNull(), +// value: text("value").notNull(), +// expiresAt: timestamp("expires_at").notNull(), +// createdAt: timestamp("created_at"), +// updatedAt: timestamp("updated_at"), +// }); diff --git a/packages/server/package.json b/packages/server/package.json index cfff36fe8..228344210 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,6 +28,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "better-auth":"1.1.16", "rotating-file-stream": "3.2.3", "@faker-js/faker": "^8.4.1", "@lucia-auth/adapter-drizzle": "1.0.7", diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts new file mode 100644 index 000000000..7117f4a44 --- /dev/null +++ b/packages/server/src/db/schema/account.ts @@ -0,0 +1,34 @@ +import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { users } from "./user"; + +export const account = pgTable("account", { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => users.id), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at"), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), + scope: text("scope"), + password: text("password"), + is2FAEnabled: boolean("is2FAEnabled").notNull().default(false), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + resetPasswordToken: text("resetPasswordToken"), + resetPasswordExpiresAt: text("resetPasswordExpiresAt"), + confirmationToken: text("confirmationToken"), + confirmationExpiresAt: text("confirmationExpiresAt"), +}); + +export const verification = pgTable("verification", { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at").notNull(), + createdAt: timestamp("created_at"), + updatedAt: timestamp("updated_at"), +}); diff --git a/packages/server/src/db/schema/admin.ts b/packages/server/src/db/schema/admin.ts index 983f99fd6..c842bd7a3 100644 --- a/packages/server/src/db/schema/admin.ts +++ b/packages/server/src/db/schema/admin.ts @@ -18,128 +18,127 @@ import { sshKeys } from "./ssh-key"; import { users } from "./user"; export const admins = pgTable("admin", { - adminId: text("adminId") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - serverIp: text("serverIp"), - certificateType: certificateType("certificateType").notNull().default("none"), - host: text("host"), - letsEncryptEmail: text("letsEncryptEmail"), - sshPrivateKey: text("sshPrivateKey"), - enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), - enableLogRotation: boolean("enableLogRotation").notNull().default(false), - authId: text("authId") - .notNull() - .references(() => auth.id, { onDelete: "cascade" }), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), - stripeCustomerId: text("stripeCustomerId"), - stripeSubscriptionId: text("stripeSubscriptionId"), - serversQuantity: integer("serversQuantity").notNull().default(0), - - // Metrics - enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), - metricsConfig: jsonb("metricsConfig") - .$type<{ - server: { - type: "Dokploy" | "Remote"; - refreshRate: number; - port: number; - token: string; - urlCallback: string; - retentionDays: number; - cronJob: string; - thresholds: { - cpu: number; - memory: number; - }; - }; - containers: { - refreshRate: number; - services: { - include: string[]; - exclude: string[]; - }; - }; - }>() - .notNull() - .default({ - server: { - type: "Dokploy", - refreshRate: 60, - port: 4500, - token: "", - retentionDays: 2, - cronJob: "", - urlCallback: "", - thresholds: { - cpu: 0, - memory: 0, - }, - }, - containers: { - refreshRate: 60, - services: { - include: [], - exclude: [], - }, - }, - }), - cleanupCacheApplications: boolean("cleanupCacheApplications") - .notNull() - .default(false), - cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") - .notNull() - .default(false), - cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") - .notNull() - .default(false), + // adminId: text("adminId") + // .notNull() + // .primaryKey() + // .$defaultFn(() => nanoid()), + // serverIp: text("serverIp"), + // certificateType: certificateType("certificateType").notNull().default("none"), + // host: text("host"), + // letsEncryptEmail: text("letsEncryptEmail"), + // sshPrivateKey: text("sshPrivateKey"), + // enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), + // enableLogRotation: boolean("enableLogRotation").notNull().default(false), + // authId: text("authId") + // .notNull() + // .references(() => auth.id, { onDelete: "cascade" }), + // createdAt: text("createdAt") + // .notNull() + // .$defaultFn(() => new Date().toISOString()), + // stripeCustomerId: text("stripeCustomerId"), + // stripeSubscriptionId: text("stripeSubscriptionId"), + // serversQuantity: integer("serversQuantity").notNull().default(0), + // // Metrics + // enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), + // metricsConfig: jsonb("metricsConfig") + // .$type<{ + // server: { + // type: "Dokploy" | "Remote"; + // refreshRate: number; + // port: number; + // token: string; + // urlCallback: string; + // retentionDays: number; + // cronJob: string; + // thresholds: { + // cpu: number; + // memory: number; + // }; + // }; + // containers: { + // refreshRate: number; + // services: { + // include: string[]; + // exclude: string[]; + // }; + // }; + // }>() + // .notNull() + // .default({ + // server: { + // type: "Dokploy", + // refreshRate: 60, + // port: 4500, + // token: "", + // retentionDays: 2, + // cronJob: "", + // urlCallback: "", + // thresholds: { + // cpu: 0, + // memory: 0, + // }, + // }, + // containers: { + // refreshRate: 60, + // services: { + // include: [], + // exclude: [], + // }, + // }, + // }), + // cleanupCacheApplications: boolean("cleanupCacheApplications") + // .notNull() + // .default(false), + // cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") + // .notNull() + // .default(false), + // cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") + // .notNull() + // .default(false), }); export const adminsRelations = relations(admins, ({ one, many }) => ({ - auth: one(auth, { - fields: [admins.authId], - references: [auth.id], - }), - users: many(users), - registry: many(registry), - sshKeys: many(sshKeys), - certificates: many(certificates), + // auth: one(auth, { + // fields: [admins.authId], + // references: [auth.id], + // }), + // users: many(users), + // registry: many(registry), + // sshKeys: many(sshKeys), + // certificates: many(certificates), })); const createSchema = createInsertSchema(admins, { - adminId: z.string(), - enableDockerCleanup: z.boolean().optional(), - sshPrivateKey: z.string().optional(), - certificateType: z.enum(["letsencrypt", "none"]).default("none"), - serverIp: z.string().optional(), - letsEncryptEmail: z.string().optional(), + // adminId: z.string(), + // enableDockerCleanup: z.boolean().optional(), + // sshPrivateKey: z.string().optional(), + // certificateType: z.enum(["letsencrypt", "none"]).default("none"), + // serverIp: z.string().optional(), + // letsEncryptEmail: z.string().optional(), }); export const apiUpdateAdmin = createSchema.partial(); export const apiSaveSSHKey = createSchema .pick({ - sshPrivateKey: true, + // sshPrivateKey: true, }) .required(); export const apiAssignDomain = createSchema .pick({ - host: true, - certificateType: true, - letsEncryptEmail: true, + // host: true, + // certificateType: true, + // letsEncryptEmail: true, }) .required() .partial({ - letsEncryptEmail: true, + // letsEncryptEmail: true, }); export const apiUpdateDockerCleanup = createSchema .pick({ - enableDockerCleanup: true, + // enableDockerCleanup: true, }) .required() .extend({ diff --git a/packages/server/src/db/schema/auth.ts b/packages/server/src/db/schema/auth.ts index 3e16c68ee..7093a40c3 100644 --- a/packages/server/src/db/schema/auth.ts +++ b/packages/server/src/db/schema/auth.ts @@ -4,7 +4,7 @@ import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { admins } from "./admin"; +// import { admins } from "./admin"; import { users } from "./user"; const randomImages = [ @@ -55,7 +55,7 @@ export const auth = pgTable("auth", { }); export const authRelations = relations(auth, ({ many }) => ({ - admins: many(admins), + // admins: many(admins), users: many(users), })); const createSchema = createInsertSchema(auth, { diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index 1df61be86..22f4f9cb7 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -3,9 +3,9 @@ import { boolean, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { admins } from "./admin"; import { server } from "./server"; import { generateAppName } from "./utils"; +import { users } from "./user"; export const certificates = pgTable("certificate", { certificateId: text("certificateId") @@ -20,7 +20,7 @@ export const certificates = pgTable("certificate", { .$defaultFn(() => generateAppName("certificate")) .unique(), autoRenew: boolean("autoRenew"), - adminId: text("adminId").references(() => admins.adminId, { + userId: text("userId").references(() => users.id, { onDelete: "cascade", }), serverId: text("serverId").references(() => server.serverId, { @@ -35,9 +35,9 @@ export const certificatesRelations = relations( fields: [certificates.serverId], references: [server.serverId], }), - admin: one(admins, { - fields: [certificates.adminId], - references: [admins.adminId], + user: one(users, { + fields: [certificates.userId], + references: [users.id], }), }), ); diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index 7d7be6141..c89ed1e37 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -5,6 +5,7 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { backups } from "./backups"; +import { users } from "./user"; export const destinations = pgTable("destination", { destinationId: text("destinationId") @@ -19,18 +20,18 @@ export const destinations = pgTable("destination", { region: text("region").notNull(), // maybe it can be null endpoint: text("endpoint").notNull(), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users.id, { onDelete: "cascade" }), }); export const destinationsRelations = relations( destinations, ({ many, one }) => ({ backups: many(backups), - admin: one(admins, { - fields: [destinations.adminId], - references: [admins.adminId], + user: one(users, { + fields: [destinations.userId], + references: [users.id], }), }), ); diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index dbbfc183b..00dc928d4 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -7,6 +7,7 @@ import { admins } from "./admin"; import { bitbucket } from "./bitbucket"; import { github } from "./github"; import { gitlab } from "./gitlab"; +import { users } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ "github", @@ -24,7 +25,7 @@ export const gitProvider = pgTable("git_provider", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - adminId: text("adminId").references(() => admins.adminId, { + userId: text("userId").references(() => users.id, { onDelete: "cascade", }), }); @@ -42,9 +43,9 @@ export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ fields: [gitProvider.gitProviderId], references: [bitbucket.gitProviderId], }), - admin: one(admins, { - fields: [gitProvider.adminId], - references: [admins.adminId], + user: one(users, { + fields: [gitProvider.userId], + references: [users.id], }), })); diff --git a/packages/server/src/db/schema/index.ts b/packages/server/src/db/schema/index.ts index 9c7a079c5..405fa383b 100644 --- a/packages/server/src/db/schema/index.ts +++ b/packages/server/src/db/schema/index.ts @@ -30,3 +30,4 @@ export * from "./gitlab"; export * from "./server"; export * from "./utils"; export * from "./preview-deployments"; +export * from "./account"; diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 73d22e1cc..4b7a09eac 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -4,6 +4,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; +import { users } from "./user"; export const notificationType = pgEnum("notificationType", [ "slack", @@ -44,7 +45,7 @@ export const notifications = pgTable("notification", { gotifyId: text("gotifyId").references(() => gotify.gotifyId, { onDelete: "cascade", }), - adminId: text("adminId").references(() => admins.adminId, { + userId: text("userId").references(() => users.id, { onDelete: "cascade", }), }); @@ -121,9 +122,9 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.gotifyId], references: [gotify.gotifyId], }), - admin: one(admins, { - fields: [notifications.adminId], - references: [admins.adminId], + user: one(users, { + fields: [notifications.userId], + references: [users.id], }), })); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index 7ed140d6f..440368383 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -4,7 +4,7 @@ import { pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { admins } from "./admin"; +// import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { mariadb } from "./mariadb"; @@ -12,6 +12,7 @@ import { mongo } from "./mongo"; import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; +import { users } from "./user"; export const projects = pgTable("project", { projectId: text("projectId") @@ -23,9 +24,9 @@ export const projects = pgTable("project", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users.id, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); @@ -37,9 +38,9 @@ export const projectRelations = relations(projects, ({ many, one }) => ({ mongo: many(mongo), redis: many(redis), compose: many(compose), - admin: one(admins, { - fields: [projects.adminId], - references: [admins.adminId], + user: one(users, { + fields: [projects.userId], + references: [users.id], }), })); diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 20544a587..d1f790686 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -5,6 +5,7 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { applications } from "./application"; +import { users } from "./user"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -27,15 +28,15 @@ export const registry = pgTable("registry", { .notNull() .$defaultFn(() => new Date().toISOString()), registryType: registryType("selfHosted").notNull().default("cloud"), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users.id, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ - admin: one(admins, { - fields: [registry.adminId], - references: [admins.adminId], + user: one(users, { + fields: [registry.userId], + references: [users.id], }), applications: many(applications), })); @@ -45,7 +46,7 @@ const createSchema = createInsertSchema(registry, { username: z.string().min(1), password: z.string().min(1), registryUrl: z.string(), - adminId: z.string().min(1), + userId: z.string().min(1), registryId: z.string().min(1), registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index c4ec6a09f..71d6ca080 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -23,6 +23,7 @@ import { postgres } from "./postgres"; import { redis } from "./redis"; import { sshKeys } from "./ssh-key"; import { generateAppName } from "./utils"; +import { users } from "./user"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); @@ -43,9 +44,9 @@ export const server = pgTable("server", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users.id, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { @@ -100,9 +101,9 @@ export const server = pgTable("server", { }); export const serverRelations = relations(server, ({ one, many }) => ({ - admin: one(admins, { - fields: [server.adminId], - references: [admins.adminId], + user: one(users, { + fields: [server.userId], + references: [users.id], }), deployments: many(deployments), sshKey: one(sshKeys, { diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index 1b6d8cc17..396e81949 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,13 +1,17 @@ import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { auth } from "./auth"; +import { users } from "./user"; +// OLD TABLE export const sessionTable = pgTable("session", { id: text("id").primaryKey(), + expiresAt: timestamp("expires_at").notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => auth.id, { onDelete: "cascade" }), - expiresAt: timestamp("expires_at", { - withTimezone: true, - mode: "date", - }).notNull(), + .references(() => users.id), + impersonatedBy: text("impersonated_by"), }); diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index e4842851b..7a4f50613 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -7,6 +7,7 @@ import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; +import { users } from "./user"; export const sshKeys = pgTable("ssh-key", { sshKeyId: text("sshKeyId") @@ -21,7 +22,7 @@ export const sshKeys = pgTable("ssh-key", { .notNull() .$defaultFn(() => new Date().toISOString()), lastUsedAt: text("lastUsedAt"), - adminId: text("adminId").references(() => admins.adminId, { + userId: text("userId").references(() => users.id, { onDelete: "cascade", }), }); @@ -30,9 +31,9 @@ export const sshKeysRelations = relations(sshKeys, ({ many, one }) => ({ applications: many(applications), compose: many(compose), servers: many(server), - admin: one(admins, { - fields: [sshKeys.adminId], - references: [admins.adminId], + user: one(users, { + fields: [sshKeys.userId], + references: [users.id], }), })); @@ -48,7 +49,7 @@ export const apiCreateSshKey = createSchema description: true, privateKey: true, publicKey: true, - adminId: true, + userId: true, }) .merge(sshKeyCreate.pick({ privateKey: true })); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 735898f9a..473cf1907 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -1,10 +1,11 @@ import { relations, sql } from "drizzle-orm"; -import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { boolean, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { auth } from "./auth"; +import { certificateType } from "./shared"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -12,12 +13,13 @@ import { auth } from "./auth"; * @see https://orm.drizzle.team/docs/goodies#multi-project-schema */ +// OLD TABLE export const users = pgTable("user", { - userId: text("userId") + id: text("id") .notNull() .primaryKey() .$defaultFn(() => nanoid()), - + name: text("name").notNull().default(""), token: text("token").notNull(), isRegistered: boolean("isRegistered").notNull().default(false), expirationDate: timestamp("expirationDate", { @@ -48,31 +50,102 @@ export const users = pgTable("user", { .array() .notNull() .default(sql`ARRAY[]::text[]`), - adminId: text("adminId") + // authId: text("authId") + // .notNull() + // .references(() => auth.id, { onDelete: "cascade" }), + // Auth + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").notNull(), + image: text("image"), + role: text("role"), + banned: boolean("banned"), + banReason: text("ban_reason"), + banExpires: timestamp("ban_expires"), + updatedAt: timestamp("updated_at").notNull(), + // Admin + serverIp: text("serverIp"), + certificateType: certificateType("certificateType").notNull().default("none"), + host: text("host"), + letsEncryptEmail: text("letsEncryptEmail"), + sshPrivateKey: text("sshPrivateKey"), + enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), + enableLogRotation: boolean("enableLogRotation").notNull().default(false), + // Metrics + enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), + metricsConfig: jsonb("metricsConfig") + .$type<{ + server: { + type: "Dokploy" | "Remote"; + refreshRate: number; + port: number; + token: string; + urlCallback: string; + retentionDays: number; + cronJob: string; + thresholds: { + cpu: number; + memory: number; + }; + }; + containers: { + refreshRate: number; + services: { + include: string[]; + exclude: string[]; + }; + }; + }>() .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), - authId: text("authId") + .default({ + server: { + type: "Dokploy", + refreshRate: 60, + port: 4500, + token: "", + retentionDays: 2, + cronJob: "", + urlCallback: "", + thresholds: { + cpu: 0, + memory: 0, + }, + }, + containers: { + refreshRate: 60, + services: { + include: [], + exclude: [], + }, + }, + }), + cleanupCacheApplications: boolean("cleanupCacheApplications") .notNull() - .references(() => auth.id, { onDelete: "cascade" }), + .default(false), + cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") + .notNull() + .default(false), + cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") + .notNull() + .default(false), }); export const usersRelations = relations(users, ({ one }) => ({ - auth: one(auth, { - fields: [users.authId], - references: [auth.id], - }), - admin: one(admins, { - fields: [users.adminId], - references: [admins.adminId], - }), + // 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), + id: z.string().min(1), + // authId: z.string().min(1), token: z.string().min(1), isRegistered: z.boolean().optional(), - adminId: z.string(), + // adminId: z.string(), accessedProjects: z.array(z.string()).optional(), accessedServices: z.array(z.string()).optional(), canCreateProjects: z.boolean().optional(), @@ -89,7 +162,7 @@ export const apiCreateUserInvitation = createSchema.pick({}).extend({ export const apiRemoveUser = createSchema .pick({ - authId: true, + // authId: true, }) .required(); @@ -101,7 +174,7 @@ export const apiFindOneToken = createSchema export const apiAssignPermissions = createSchema .pick({ - userId: true, + id: true, canCreateProjects: true, canCreateServices: true, canDeleteProjects: true, @@ -118,12 +191,12 @@ export const apiAssignPermissions = createSchema export const apiFindOneUser = createSchema .pick({ - userId: true, + id: true, }) .required(); export const apiFindOneUserByAuth = createSchema .pick({ - authId: true, + // authId: true, }) .required(); diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 71549a388..345849ed9 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -118,3 +118,5 @@ export * from "./monitoring/utils"; export * from "./db/validations/domain"; export * from "./db/validations/index"; export * from "./utils/gpu-setup"; + +export * from "./lib/auth"; diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts new file mode 100644 index 000000000..fada2c434 --- /dev/null +++ b/packages/server/src/lib/auth.ts @@ -0,0 +1,14 @@ +import { betterAuth } from "better-auth"; +import { drizzleAdapter } from "better-auth/adapters/drizzle"; +import { admin } from "better-auth/plugins"; +import { db } from "../db"; + +export const auth = betterAuth({ + database: drizzleAdapter(db, { + provider: "pg", + }), + emailAndPassword: { + enabled: true, + }, + plugins: [admin()], +}); diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index b6d9c7fb4..056b81785 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -94,7 +94,9 @@ export const updateAdminById = async ( }; export const isAdminPresent = async () => { - const admin = await db.query.admins.findFirst(); + const admin = await db.query.users.findFirst({ + where: eq(users.role, "admin"), + }); if (!admin) { return false; } diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index b1fd925c6..668855144 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -23,27 +23,27 @@ class LogRotationManager { } private async initialize(): Promise { - const isActive = await this.getStateFromDB(); - if (isActive) { - await this.activateStream(); - } + // const isActive = await this.getStateFromDB(); + // if (isActive) { + // await this.activateStream(); + // } } - private async getStateFromDB(): Promise { - const setting = await db.query.admins.findFirst({}); - return setting?.enableLogRotation ?? false; - } + // private async getStateFromDB(): Promise { + // const setting = await db.query.admins.findFirst({}); + // return setting?.enableLogRotation ?? false; + // } - private async setStateInDB(active: boolean): Promise { - const admin = await db.query.admins.findFirst({}); + // private async setStateInDB(active: boolean): Promise { + // const admin = await db.query.admins.findFirst({}); - if (!admin) { - return; - } - await updateAdmin(admin?.authId, { - enableLogRotation: active, - }); - } + // if (!admin) { + // return; + // } + // await updateAdmin(admin?.authId, { + // enableLogRotation: active, + // }); + // } private async activateStream(): Promise { const { DYNAMIC_TRAEFIK_PATH } = paths(); @@ -76,26 +76,26 @@ class LogRotationManager { } public async activate(): Promise { - const currentState = await this.getStateFromDB(); - if (currentState) { - return true; - } + // const currentState = await this.getStateFromDB(); + // if (currentState) { + // return true; + // } - await this.setStateInDB(true); - await this.activateStream(); + // await this.setStateInDB(true); + // await this.activateStream(); return true; } public async deactivate(): Promise { console.log("Deactivating log rotation..."); - const currentState = await this.getStateFromDB(); - if (!currentState) { - console.log("Log rotation is already inactive in DB"); - return true; - } + // const currentState = await this.getStateFromDB(); + // if (!currentState) { + // console.log("Log rotation is already inactive in DB"); + // return true; + // } - await this.setStateInDB(false); - await this.deactivateStream(); + // await this.setStateInDB(false); + // await this.deactivateStream(); console.log("Log rotation deactivated successfully"); return true; } @@ -115,8 +115,9 @@ class LogRotationManager { } } public async getStatus(): Promise { - const dbState = await this.getStateFromDB(); - return dbState; + // const dbState = await this.getStateFromDB(); + // return dbState; + return false; } } export const logRotationManager = LogRotationManager.getInstance(); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c6514644..555822409 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -238,6 +238,9 @@ importers: bcrypt: specifier: 5.1.1 version: 5.1.1(encoding@0.1.13) + better-auth: + specifier: 1.1.16 + version: 1.1.16 bl: specifier: 6.0.11 version: 6.0.11 @@ -273,10 +276,10 @@ importers: version: 16.4.5 drizzle-orm: specifier: ^0.39.1 - version: 0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) + version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) drizzle-zod: specifier: 0.5.1 - version: 0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8) + version: 0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8) fancy-ansi: specifier: ^0.1.3 version: 0.1.3 @@ -505,7 +508,7 @@ importers: version: 16.4.5 drizzle-orm: specifier: ^0.39.1 - version: 0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) + version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) hono: specifier: ^4.5.8 version: 4.5.8 @@ -567,6 +570,9 @@ importers: bcrypt: specifier: 5.1.1 version: 5.1.1(encoding@0.1.13) + better-auth: + specifier: 1.1.16 + version: 1.1.16 bl: specifier: 6.0.11 version: 6.0.11 @@ -584,10 +590,10 @@ importers: version: 16.4.5 drizzle-orm: specifier: ^0.39.1 - version: 0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) + version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) drizzle-zod: specifier: 0.5.1 - version: 0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8) + version: 0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8) hi-base32: specifier: ^0.5.1 version: 0.5.1 @@ -748,6 +754,12 @@ packages: '@balena/dockerignore@1.0.2': resolution: {integrity: sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==} + '@better-auth/utils@0.2.3': + resolution: {integrity: sha512-Ap1GaSmo6JYhJhxJOpUB0HobkKPTNzfta+bLV89HfpyCAHN7p8ntCrmNFHNAVD0F6v0mywFVEUg1FUhNCc81Rw==} + + '@better-fetch/fetch@1.1.12': + resolution: {integrity: sha512-B3bfloI/2UBQWIATRN6qmlORrvx3Mp0kkNjmXLv0b+DtbtR+pP4/I5kQA/rDUv+OReLywCCldf6co4LdDmh8JA==} + '@biomejs/biome@1.9.4': resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} engines: {node: '>=14.21.3'} @@ -1518,6 +1530,9 @@ packages: '@hapi/bourne@3.0.0': resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==} + '@hexagon/base64@1.1.28': + resolution: {integrity: sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw==} + '@hono/node-server@1.12.1': resolution: {integrity: sha512-C9l+08O8xtXB7Ppmy8DjBFH1hYji7JKzsU32Yt1poIIbdPp6S7aOI8IldDHD9YFJ55lv2c21ovNrmxatlHfhAg==} engines: {node: '>=18.14.1'} @@ -1697,6 +1712,9 @@ packages: '@leichtgewicht/ip-codec@2.0.5': resolution: {integrity: sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==} + '@levischuck/tiny-cbor@0.2.11': + resolution: {integrity: sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow==} + '@lezer/common@1.2.1': resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} @@ -1807,10 +1825,17 @@ packages: cpu: [x64] os: [win32] + '@noble/ciphers@0.6.0': + resolution: {integrity: sha512-mIbq/R9QXk5/cTfESb1OKtyFnk7oc1Om/8onA1158K9/OZUQFDEVy55jVTato+xmp3XX6F6Qh0zz0Nc1AxAlRQ==} + '@noble/hashes@1.5.0': resolution: {integrity: sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==} engines: {node: ^14.21.3 || >=16} + '@noble/hashes@1.7.1': + resolution: {integrity: sha512-B8XBPsn4vT/KJAGqDzbwztd+6Yte3P4V7iafm24bxgDe/mlRuK6xmWPuCNrKt2vDafZ8MfJLlchDG/vYafQEjQ==} + engines: {node: ^14.21.3 || >=16} + '@node-rs/argon2-android-arm-eabi@1.7.0': resolution: {integrity: sha512-udDqkr5P9E+wYX1SZwAVPdyfYvaF4ry9Tm+R9LkfSHbzWH0uhU6zjIwNRp7m+n4gx691rk+lqqDAIP8RLKwbhg==} engines: {node: '>= 10'} @@ -2136,6 +2161,21 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@peculiar/asn1-android@2.3.15': + resolution: {integrity: sha512-8U2TIj59cRlSXTX2d0mzUKP7whfWGFMzTeC3qPgAbccXFrPNZLaDhpNEdG5U2QZ/tBv/IHlCJ8s+KYXpJeop6w==} + + '@peculiar/asn1-ecc@2.3.15': + resolution: {integrity: sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA==} + + '@peculiar/asn1-rsa@2.3.15': + resolution: {integrity: sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg==} + + '@peculiar/asn1-schema@2.3.15': + resolution: {integrity: sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w==} + + '@peculiar/asn1-x509@2.3.15': + resolution: {integrity: sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg==} + '@pkgjs/parseargs@0.11.0': resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} @@ -3147,6 +3187,13 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + '@simplewebauthn/browser@13.1.0': + resolution: {integrity: sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg==} + + '@simplewebauthn/server@13.1.1': + resolution: {integrity: sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA==} + engines: {node: '>=20.0.0'} + '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} @@ -3762,6 +3809,10 @@ packages: asn1@0.2.6: resolution: {integrity: sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==} + asn1js@3.0.5: + resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} + engines: {node: '>=12.0.0'} + assertion-error@1.1.0: resolution: {integrity: sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==} @@ -3804,6 +3855,12 @@ packages: before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} + better-auth@1.1.16: + resolution: {integrity: sha512-Xc5pxafKZw4QVU8WYfkV2z4Hd8KCXXbphrgOpe2gA/EfanysLBhE1G/F7cEi5e0bW2pGR+vw6gf0ARHA7VFihg==} + + better-call@0.3.3: + resolution: {integrity: sha512-N4lDVm0NGmFfDJ0XMQ4O83Zm/3dPlvIQdxvwvgSLSkjFX5PM4GUYSVAuxNzXN27QZMHDkrJTWUqxBrm4tPC3eA==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -5120,6 +5177,9 @@ packages: resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} hasBin: true + jose@5.9.6: + resolution: {integrity: sha512-AMlnetc9+CV9asI19zHmrgS/WYsWUwCn2R7RzlbJWD7F9eWYUTGyBmU9o6PxngtLGOiDGPRu+Uc4fhKzbpteZQ==} + joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} @@ -5223,6 +5283,10 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + kysely@0.27.5: + resolution: {integrity: sha512-s7hZHcQeSNKpzCkHRm8yA+0JPLjncSWnjb+2TIElwS2JAqYr+Kv3Ess+9KFfJS0C1xcQ1i9NkNHpWwCYpHMWsA==} + engines: {node: '>=14.0.0'} + leac@0.6.0: resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} @@ -5608,6 +5672,10 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanostores@0.11.3: + resolution: {integrity: sha512-TUes3xKIX33re4QzdxwZ6tdbodjmn3tWXCEc1uokiEmo14sI1EaGYNs2k3bU2pyyGNmBqFGAVl6jAGWd06AVIg==} + engines: {node: ^18.0.0 || >=20.0.0} + napi-build-utils@1.0.2: resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} @@ -6058,6 +6126,13 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pvtsutils@1.3.6: + resolution: {integrity: sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg==} + + pvutils@1.1.3: + resolution: {integrity: sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==} + engines: {node: '>=6.0.0'} + qrcode@1.5.4: resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} engines: {node: '>=10.13.0'} @@ -6403,6 +6478,9 @@ packages: resolution: {integrity: sha512-cfmm3tqdnbuYw2FBmRTPBDaohYEbMJ3211T35o6eZdr4d7v69+ZeK1Av84Br7FLj2dlzyeZSbN6qTuXXE6dawQ==} engines: {node: '>=14.0'} + rou3@0.5.1: + resolution: {integrity: sha512-OXMmJ3zRk2xeXFGfA3K+EOPHC5u7RDFG7lIOx0X1pdnhUkI8MdVrbV+sNsD80ElpUZ+MRHdyxPnFthq9VHs8uQ==} + run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -6872,6 +6950,9 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + tsx@4.16.2: resolution: {integrity: sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==} engines: {node: '>=18.0.0'} @@ -7237,6 +7318,9 @@ packages: zod@3.23.8: resolution: {integrity: sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==} + zod@3.24.1: + resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + snapshots: '@alloc/quick-lru@5.2.0': {} @@ -7266,6 +7350,12 @@ snapshots: '@balena/dockerignore@1.0.2': {} + '@better-auth/utils@0.2.3': + dependencies: + uncrypto: 0.1.3 + + '@better-fetch/fetch@1.1.12': {} + '@biomejs/biome@1.9.4': optionalDependencies: '@biomejs/cli-darwin-arm64': 1.9.4 @@ -7832,6 +7922,8 @@ snapshots: '@hapi/bourne@3.0.0': {} + '@hexagon/base64@1.1.28': {} + '@hono/node-server@1.12.1': {} '@hono/zod-validator@0.3.0(hono@4.5.8)(zod@3.23.8)': @@ -7981,6 +8073,8 @@ snapshots: '@leichtgewicht/ip-codec@2.0.5': {} + '@levischuck/tiny-cbor@0.2.11': {} + '@lezer/common@1.2.1': {} '@lezer/highlight@1.2.0': @@ -8071,8 +8165,12 @@ snapshots: '@next/swc-win32-x64-msvc@15.0.1': optional: true + '@noble/ciphers@0.6.0': {} + '@noble/hashes@1.5.0': {} + '@noble/hashes@1.7.1': {} + '@node-rs/argon2-android-arm-eabi@1.7.0': optional: true @@ -8401,6 +8499,39 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@peculiar/asn1-android@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + asn1js: 3.0.5 + tslib: 2.8.1 + + '@peculiar/asn1-ecc@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + '@peculiar/asn1-x509': 2.3.15 + asn1js: 3.0.5 + tslib: 2.8.1 + + '@peculiar/asn1-rsa@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + '@peculiar/asn1-x509': 2.3.15 + asn1js: 3.0.5 + tslib: 2.8.1 + + '@peculiar/asn1-schema@2.3.15': + dependencies: + asn1js: 3.0.5 + pvtsutils: 1.3.6 + tslib: 2.8.1 + + '@peculiar/asn1-x509@2.3.15': + dependencies: + '@peculiar/asn1-schema': 2.3.15 + asn1js: 3.0.5 + pvtsutils: 1.3.6 + tslib: 2.8.1 + '@pkgjs/parseargs@0.11.0': optional: true @@ -9394,6 +9525,18 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 + '@simplewebauthn/browser@13.1.0': {} + + '@simplewebauthn/server@13.1.1': + dependencies: + '@hexagon/base64': 1.1.28 + '@levischuck/tiny-cbor': 0.2.11 + '@peculiar/asn1-android': 2.3.15 + '@peculiar/asn1-ecc': 2.3.15 + '@peculiar/asn1-rsa': 2.3.15 + '@peculiar/asn1-schema': 2.3.15 + '@peculiar/asn1-x509': 2.3.15 + '@sinclair/typebox@0.27.8': {} '@sindresorhus/is@5.6.0': {} @@ -10304,6 +10447,12 @@ snapshots: dependencies: safer-buffer: 2.1.2 + asn1js@3.0.5: + dependencies: + pvtsutils: 1.3.6 + pvutils: 1.1.3 + tslib: 2.8.1 + assertion-error@1.1.0: {} async-await-queue@2.1.4: {} @@ -10352,6 +10501,28 @@ snapshots: before-after-hook@2.2.3: {} + better-auth@1.1.16: + dependencies: + '@better-auth/utils': 0.2.3 + '@better-fetch/fetch': 1.1.12 + '@noble/ciphers': 0.6.0 + '@noble/hashes': 1.7.1 + '@simplewebauthn/browser': 13.1.0 + '@simplewebauthn/server': 13.1.1 + better-call: 0.3.3 + defu: 6.1.4 + jose: 5.9.6 + kysely: 0.27.5 + nanostores: 0.11.3 + zod: 3.24.1 + + better-call@0.3.3: + dependencies: + '@better-fetch/fetch': 1.1.12 + rou3: 0.5.1 + uncrypto: 0.1.3 + zod: 3.24.1 + binary-extensions@2.3.0: {} bindings@1.5.0: @@ -10957,16 +11128,17 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7): + drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7): optionalDependencies: '@types/react': 18.3.5 + kysely: 0.27.5 postgres: 3.4.4 react: 18.2.0 sqlite3: 5.1.7 - drizzle-zod@0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8): + drizzle-zod@0.5.1(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7))(zod@3.23.8): dependencies: - drizzle-orm: 0.39.1(@types/react@18.3.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) + drizzle-orm: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) zod: 3.23.8 eastasianwidth@0.2.0: {} @@ -11686,6 +11858,8 @@ snapshots: jiti@1.21.6: {} + jose@5.9.6: {} + joycon@3.1.1: {} js-base64@3.7.7: {} @@ -11843,6 +12017,8 @@ snapshots: dependencies: json-buffer: 3.0.1 + kysely@0.27.5: {} + leac@0.6.0: {} lefthook-darwin-arm64@1.8.4: @@ -12215,6 +12391,8 @@ snapshots: nanoid@3.3.7: {} + nanostores@0.11.3: {} + napi-build-utils@1.0.2: optional: true @@ -12692,6 +12870,12 @@ snapshots: punycode@2.3.1: {} + pvtsutils@1.3.6: + dependencies: + tslib: 2.8.1 + + pvutils@1.1.3: {} + qrcode@1.5.4: dependencies: dijkstrajs: 1.0.3 @@ -13054,6 +13238,8 @@ snapshots: rotating-file-stream@3.2.3: {} + rou3@0.5.1: {} + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -13617,6 +13803,8 @@ snapshots: tslib@2.6.3: {} + tslib@2.8.1: {} + tsx@4.16.2: dependencies: esbuild: 0.21.5 @@ -13994,3 +14182,5 @@ snapshots: zod: 3.23.8 zod@3.23.8: {} + + zod@3.24.1: {} From fafc238e701c74f7a524a762865fc2302674fc40 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 9 Feb 2025 18:56:17 -0600 Subject: [PATCH 002/126] refactor: migration --- apps/dokploy/drizzle/0066_broad_marrow.sql | 25 +++++++++- apps/dokploy/package.json | 2 +- apps/dokploy/pages/index.tsx | 47 +++++++++++-------- apps/dokploy/pages/register.tsx | 36 ++++++++------ apps/dokploy/server/api/routers/admin.ts | 2 +- packages/server/auth-schema.ts | 4 +- packages/server/src/db/schema/account.ts | 4 +- packages/server/src/db/schema/auth.ts | 4 +- packages/server/src/db/schema/certificate.ts | 8 ++-- packages/server/src/db/schema/destination.ts | 8 ++-- packages/server/src/db/schema/git-provider.ts | 8 ++-- packages/server/src/db/schema/notification.ts | 8 ++-- packages/server/src/db/schema/project.ts | 8 ++-- packages/server/src/db/schema/registry.ts | 8 ++-- packages/server/src/db/schema/server.ts | 8 ++-- packages/server/src/db/schema/session.ts | 4 +- packages/server/src/db/schema/ssh-key.ts | 8 ++-- packages/server/src/db/schema/user.ts | 6 +-- packages/server/src/lib/auth.ts | 3 +- packages/server/src/services/admin.ts | 2 +- packages/server/src/services/auth.ts | 2 +- packages/server/src/services/user.ts | 34 +++++++------- 22 files changed, 138 insertions(+), 101 deletions(-) diff --git a/apps/dokploy/drizzle/0066_broad_marrow.sql b/apps/dokploy/drizzle/0066_broad_marrow.sql index f10854144..d7e3acf0e 100644 --- a/apps/dokploy/drizzle/0066_broad_marrow.sql +++ b/apps/dokploy/drizzle/0066_broad_marrow.sql @@ -90,7 +90,8 @@ UPDATE "user" SET token = '' WHERE token IS NULL; UPDATE "user" SET "expirationDate" = CURRENT_TIMESTAMP + INTERVAL '1 year' WHERE "expirationDate" IS NULL; UPDATE "user" SET "createdAt" = to_char(CURRENT_TIMESTAMP, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') WHERE "createdAt" IS NULL; UPDATE "user" SET "name" = '' WHERE "name" IS NULL; -UPDATE "user" SET "email" = COALESCE("email", '') WHERE true; +-- Generar emails únicos para registros vacíos +UPDATE "user" SET "email" = CONCAT('user_', id, '@dokploy.local') WHERE "email" = '' OR "email" IS NULL; UPDATE "user" SET "email_verified" = COALESCE("email_verified", false) WHERE true; UPDATE "user" SET "role" = COALESCE("role", 'user') WHERE true; UPDATE "user" SET "banned" = COALESCE("banned", false) WHERE true; @@ -128,6 +129,28 @@ SELECT CAST("createdAt" AS timestamp) as updated_at FROM "auth"; +-- Migrar datos de auth a account +INSERT INTO "account" ( + id, + account_id, + provider_id, + user_id, + password, + "is2FAEnabled", + created_at, + updated_at +) +SELECT + id as id, + id as account_id, + 'credentials' as provider_id, + id as user_id, + password, + "is2FAEnabled", + CAST("createdAt" AS timestamp) as created_at, + CAST("createdAt" AS timestamp) as updated_at +FROM "auth"; + -- Migrar datos de admin a user UPDATE "user" u SET diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index e2a7c4a2b..9e531cf8c 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -35,7 +35,7 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { - "better-auth":"1.1.16", + "better-auth": "1.1.16", "bl": "6.0.11", "rotating-file-stream": "3.2.3", "qrcode": "^1.5.3", diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 3aafe9bf9..2691fc5c3 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -13,6 +13,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { authClient } from "@/lib/auth"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server"; @@ -65,8 +66,8 @@ export default function Home({ IS_CLOUD }: Props) { const router = useRouter(); const form = useForm({ defaultValues: { - email: "", - password: "", + email: "siumauricio@hotmail.com", + password: "Password1234", }, resolver: zodResolver(loginSchema), }); @@ -76,25 +77,31 @@ export default function Home({ IS_CLOUD }: Props) { }, [form, form.reset, form.formState.isSubmitSuccessful]); const onSubmit = async (values: Login) => { - await mutateAsync({ - email: values.email.toLowerCase(), + const { data, error } = await authClient.signIn.email({ + email: values.email, password: values.password, - }) - .then((data) => { - if (data.is2FAEnabled) { - setTemp(data); - } else { - toast.success("Successfully signed in", { - duration: 2000, - }); - router.push("/dashboard/projects"); - } - }) - .catch(() => { - toast.error("Signin failed", { - duration: 2000, - }); - }); + }); + + console.log(data, error); + // await mutateAsync({ + // email: values.email.toLowerCase(), + // password: values.password, + // }) + // .then((data) => { + // if (data.is2FAEnabled) { + // setTemp(data); + // } else { + // toast.success("Successfully signed in", { + // duration: 2000, + // }); + // router.push("/dashboard/projects"); + // } + // }) + // .catch(() => { + // toast.error("Signin failed", { + // duration: 2000, + // }); + // }); }; return ( <> diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index e42231a2f..4ff3d4dad 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -17,6 +17,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { authClient } from "@/lib/auth"; import { api } from "@/utils/api"; import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -79,9 +80,9 @@ const Register = ({ isCloud }: Props) => { const form = useForm({ defaultValues: { - email: "", - password: "", - confirmPassword: "", + email: "user5@yopmail.com", + password: "Password1234", + confirmPassword: "Password1234", }, resolver: zodResolver(registerSchema), }); @@ -91,19 +92,24 @@ const Register = ({ isCloud }: Props) => { }, [form, form.reset, form.formState.isSubmitSuccessful]); const onSubmit = async (values: Register) => { - await mutateAsync({ - email: values.email.toLowerCase(), + const { data, error } = await authClient.signUp.email({ + email: values.email, password: values.password, - }) - .then(() => { - toast.success("User registered successfuly", { - duration: 2000, - }); - if (!isCloud) { - router.push("/"); - } - }) - .catch((e) => e); + name: "Mauricio Siu", + }); + // await mutateAsync({ + // email: values.email.toLowerCase(), + // password: values.password, + // }) + // .then(() => { + // toast.success("User registered successfuly", { + // duration: 2000, + // }); + // if (!isCloud) { + // router.push("/"); + // } + // }) + // .catch((e) => e); }; return (
diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 31612fe06..6a4764f56 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -6,7 +6,7 @@ import { apiRemoveUser, apiUpdateAdmin, apiUpdateWebServerMonitoring, - users, + user, } from "@/server/db/schema"; import { IS_CLOUD, diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 8f8be24d9..8865078b2 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,9 +1,9 @@ import { + boolean, + integer, pgTable, text, - integer, timestamp, - boolean, } from "drizzle-orm/pg-core"; export const user = pgTable("user", { diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 7117f4a44..a332ec920 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,5 +1,5 @@ import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { users } from "./user"; +import { user } from "./user"; export const account = pgTable("account", { id: text("id").primaryKey(), @@ -7,7 +7,7 @@ export const account = pgTable("account", { providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => users.id), + .references(() => user.id), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), diff --git a/packages/server/src/db/schema/auth.ts b/packages/server/src/db/schema/auth.ts index 7093a40c3..35f4dc853 100644 --- a/packages/server/src/db/schema/auth.ts +++ b/packages/server/src/db/schema/auth.ts @@ -5,7 +5,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; // import { admins } from "./admin"; -import { users } from "./user"; +import { user } from "./user"; const randomImages = [ "/avatars/avatar-1.png", @@ -56,7 +56,7 @@ export const auth = pgTable("auth", { export const authRelations = relations(auth, ({ many }) => ({ // admins: many(admins), - users: many(users), + users: many(user), })); const createSchema = createInsertSchema(auth, { email: z.string().email(), diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index 22f4f9cb7..3a7ec596d 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -4,8 +4,8 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { server } from "./server"; +import { user } from "./user"; import { generateAppName } from "./utils"; -import { users } from "./user"; export const certificates = pgTable("certificate", { certificateId: text("certificateId") @@ -20,7 +20,7 @@ export const certificates = pgTable("certificate", { .$defaultFn(() => generateAppName("certificate")) .unique(), autoRenew: boolean("autoRenew"), - userId: text("userId").references(() => users.id, { + userId: text("userId").references(() => user.id, { onDelete: "cascade", }), serverId: text("serverId").references(() => server.serverId, { @@ -35,9 +35,9 @@ export const certificatesRelations = relations( fields: [certificates.serverId], references: [server.serverId], }), - user: one(users, { + user: one(user, { fields: [certificates.userId], - references: [users.id], + references: [user.id], }), }), ); diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index c89ed1e37..37faf0fa4 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -5,7 +5,7 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { backups } from "./backups"; -import { users } from "./user"; +import { user } from "./user"; export const destinations = pgTable("destination", { destinationId: text("destinationId") @@ -22,16 +22,16 @@ export const destinations = pgTable("destination", { endpoint: text("endpoint").notNull(), userId: text("userId") .notNull() - .references(() => users.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), }); export const destinationsRelations = relations( destinations, ({ many, one }) => ({ backups: many(backups), - user: one(users, { + user: one(user, { fields: [destinations.userId], - references: [users.id], + references: [user.id], }), }), ); diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index 00dc928d4..f044b0a09 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -7,7 +7,7 @@ import { admins } from "./admin"; import { bitbucket } from "./bitbucket"; import { github } from "./github"; import { gitlab } from "./gitlab"; -import { users } from "./user"; +import { user } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ "github", @@ -25,7 +25,7 @@ export const gitProvider = pgTable("git_provider", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - userId: text("userId").references(() => users.id, { + userId: text("userId").references(() => user.id, { onDelete: "cascade", }), }); @@ -43,9 +43,9 @@ export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ fields: [gitProvider.gitProviderId], references: [bitbucket.gitProviderId], }), - user: one(users, { + user: one(user, { fields: [gitProvider.userId], - references: [users.id], + references: [user.id], }), })); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 4b7a09eac..5174c17c1 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -4,7 +4,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; -import { users } from "./user"; +import { user } from "./user"; export const notificationType = pgEnum("notificationType", [ "slack", @@ -45,7 +45,7 @@ export const notifications = pgTable("notification", { gotifyId: text("gotifyId").references(() => gotify.gotifyId, { onDelete: "cascade", }), - userId: text("userId").references(() => users.id, { + userId: text("userId").references(() => user.id, { onDelete: "cascade", }), }); @@ -122,9 +122,9 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.gotifyId], references: [gotify.gotifyId], }), - user: one(users, { + user: one(user, { fields: [notifications.userId], - references: [users.id], + references: [user.id], }), })); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index 440368383..2602a8ec0 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -12,7 +12,7 @@ import { mongo } from "./mongo"; import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; -import { users } from "./user"; +import { user } from "./user"; export const projects = pgTable("project", { projectId: text("projectId") @@ -26,7 +26,7 @@ export const projects = pgTable("project", { .$defaultFn(() => new Date().toISOString()), userId: text("userId") .notNull() - .references(() => users.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); @@ -38,9 +38,9 @@ export const projectRelations = relations(projects, ({ many, one }) => ({ mongo: many(mongo), redis: many(redis), compose: many(compose), - user: one(users, { + user: one(user, { fields: [projects.userId], - references: [users.id], + references: [user.id], }), })); diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index d1f790686..166168c5f 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -5,7 +5,7 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { applications } from "./application"; -import { users } from "./user"; +import { user } from "./user"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -30,13 +30,13 @@ export const registry = pgTable("registry", { registryType: registryType("selfHosted").notNull().default("cloud"), userId: text("userId") .notNull() - .references(() => users.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ - user: one(users, { + user: one(user, { fields: [registry.userId], - references: [users.id], + references: [user.id], }), applications: many(applications), })); diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index 71d6ca080..e3a14f953 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -22,8 +22,8 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { sshKeys } from "./ssh-key"; +import { user } from "./user"; import { generateAppName } from "./utils"; -import { users } from "./user"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); @@ -46,7 +46,7 @@ export const server = pgTable("server", { .$defaultFn(() => new Date().toISOString()), userId: text("userId") .notNull() - .references(() => users.id, { onDelete: "cascade" }), + .references(() => user.id, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { @@ -101,9 +101,9 @@ export const server = pgTable("server", { }); export const serverRelations = relations(server, ({ one, many }) => ({ - user: one(users, { + user: one(user, { fields: [server.userId], - references: [users.id], + references: [user.id], }), deployments: many(deployments), sshKey: one(sshKeys, { diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index 396e81949..e7d579fb1 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,5 +1,5 @@ import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { users } from "./user"; +import { user } from "./user"; // OLD TABLE export const sessionTable = pgTable("session", { @@ -12,6 +12,6 @@ export const sessionTable = pgTable("session", { userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => users.id), + .references(() => user.id), impersonatedBy: text("impersonated_by"), }); diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index 7a4f50613..6c5ba0a7b 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -7,7 +7,7 @@ import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; -import { users } from "./user"; +import { user } from "./user"; export const sshKeys = pgTable("ssh-key", { sshKeyId: text("sshKeyId") @@ -22,7 +22,7 @@ export const sshKeys = pgTable("ssh-key", { .notNull() .$defaultFn(() => new Date().toISOString()), lastUsedAt: text("lastUsedAt"), - userId: text("userId").references(() => users.id, { + userId: text("userId").references(() => user.id, { onDelete: "cascade", }), }); @@ -31,9 +31,9 @@ export const sshKeysRelations = relations(sshKeys, ({ many, one }) => ({ applications: many(applications), compose: many(compose), servers: many(server), - user: one(users, { + user: one(user, { fields: [sshKeys.userId], - references: [users.id], + references: [user.id], }), })); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 473cf1907..372b79f87 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -14,7 +14,7 @@ import { certificateType } from "./shared"; */ // OLD TABLE -export const users = pgTable("user", { +export const user = pgTable("user", { id: text("id") .notNull() .primaryKey() @@ -129,7 +129,7 @@ export const users = pgTable("user", { .default(false), }); -export const usersRelations = relations(users, ({ one }) => ({ +export const usersRelations = relations(user, ({ one }) => ({ // auth: one(auth, { // fields: [users.authId], // references: [auth.id], @@ -140,7 +140,7 @@ export const usersRelations = relations(users, ({ one }) => ({ // }), })); -const createSchema = createInsertSchema(users, { +const createSchema = createInsertSchema(user, { id: z.string().min(1), // authId: z.string().min(1), token: z.string().min(1), diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index fada2c434..93eed4ca2 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -2,10 +2,11 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { admin } from "better-auth/plugins"; import { db } from "../db"; - +import * as schema from "../db/schema"; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", + schema: schema, }), emailAndPassword: { enabled: true, diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 056b81785..ea0df9b08 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -4,7 +4,7 @@ import { admins, type apiCreateUserInvitation, auth, - users, + user, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 65a01c412..376f10e90 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -5,7 +5,7 @@ import { type apiCreateAdmin, type apiCreateUser, auth, - users, + user, } from "@dokploy/server/db/schema"; import { getPublicIpWithFallback } from "@dokploy/server/wss/utils"; import { TRPCError } from "@trpc/server"; diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index d8d9862c4..f50e678ad 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,15 +1,15 @@ import { db } from "@dokploy/server/db"; -import { users } from "@dokploy/server/db/schema"; +import { user } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -export type User = typeof users.$inferSelect; +export type User = typeof user.$inferSelect; export const findUserById = async (userId: string) => { - const user = await db.query.users.findFirst({ - where: eq(users.userId, userId), + const userR = await db.query.user.findFirst({ + where: eq(user.userId, userId), }); - if (!user) { + if (!userR) { throw new TRPCError({ code: "NOT_FOUND", message: "User not found", @@ -19,8 +19,8 @@ export const findUserById = async (userId: string) => { }; export const findUserByAuthId = async (authId: string) => { - const user = await db.query.users.findFirst({ - where: eq(users.authId, authId), + const userR = await db.query.user.findFirst({ + where: eq(user.authId, authId), with: { auth: true, }, @@ -35,8 +35,8 @@ export const findUserByAuthId = async (authId: string) => { }; export const findUsers = async (adminId: string) => { - const currentUsers = await db.query.users.findMany({ - where: eq(users.adminId, adminId), + const currentUsers = await db.query.user.findMany({ + where: eq(user.adminId, adminId), with: { auth: { columns: { @@ -49,24 +49,24 @@ export const findUsers = async (adminId: string) => { }; export const addNewProject = async (authId: string, projectId: string) => { - const user = await findUserByAuthId(authId); + const userR = await findUserByAuthId(authId); await db - .update(users) + .update(user) .set({ - accessedProjects: [...user.accessedProjects, projectId], + accessedProjects: [...userR.accessedProjects, projectId], }) - .where(eq(users.authId, authId)); + .where(eq(user.authId, authId)); }; export const addNewService = async (authId: string, serviceId: string) => { - const user = await findUserByAuthId(authId); + const userR = await findUserByAuthId(authId); await db - .update(users) + .update(user) .set({ - accessedServices: [...user.accessedServices, serviceId], + accessedServices: [...userR.accessedServices, serviceId], }) - .where(eq(users.authId, authId)); + .where(eq(user.authId, authId)); }; export const canPerformCreationService = async ( From 8bd72a8a343488e9f51007e7bcddfa80e0563796 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:53:06 -0600 Subject: [PATCH 003/126] refactor: add organizations system --- .../dashboard/projects/handle-project.tsx | 37 +++++++- .../components/dashboard/projects/show.tsx | 6 +- .../components/dashboard/search-command.tsx | 7 +- apps/dokploy/components/layouts/side.tsx | 34 +++---- apps/dokploy/components/layouts/user-nav.tsx | 8 +- apps/dokploy/lib/auth.ts | 3 + .../pages/api/providers/github/setup.ts | 2 +- apps/dokploy/pages/dashboard/docker.tsx | 2 +- .../pages/dashboard/project/[projectId].tsx | 4 +- .../services/application/[applicationId].tsx | 4 +- .../services/compose/[composeId].tsx | 4 +- .../services/mariadb/[mariadbId].tsx | 4 +- .../[projectId]/services/mongo/[mongoId].tsx | 4 +- .../[projectId]/services/mysql/[mysqlId].tsx | 4 +- .../services/postgres/[postgresId].tsx | 4 +- .../[projectId]/services/redis/[redisId].tsx | 4 +- .../pages/dashboard/settings/billing.tsx | 2 +- .../pages/dashboard/settings/certificates.tsx | 2 +- .../pages/dashboard/settings/cluster.tsx | 2 +- .../pages/dashboard/settings/destinations.tsx | 2 +- .../dashboard/settings/git-providers.tsx | 2 +- .../pages/dashboard/settings/index.tsx | 2 +- .../dashboard/settings/notifications.tsx | 2 +- .../pages/dashboard/settings/profile.tsx | 6 +- .../pages/dashboard/settings/registry.tsx | 2 +- .../pages/dashboard/settings/server.tsx | 2 +- .../pages/dashboard/settings/servers.tsx | 2 +- .../pages/dashboard/settings/ssh-keys.tsx | 2 +- .../pages/dashboard/settings/users.tsx | 2 +- apps/dokploy/pages/dashboard/swarm.tsx | 2 +- apps/dokploy/pages/dashboard/traefik.tsx | 2 +- apps/dokploy/pages/index.tsx | 94 ++++++++++-------- apps/dokploy/pages/register.tsx | 28 +++++- apps/dokploy/pages/swagger.tsx | 2 +- apps/dokploy/server/api/routers/auth.ts | 3 +- apps/dokploy/server/api/routers/project.ts | 7 +- apps/dokploy/server/api/routers/settings.ts | 2 +- apps/dokploy/server/api/trpc.ts | 18 ++-- packages/server/auth-schema.ts | 95 ++++++++++++------- packages/server/src/auth/auth.ts | 5 +- packages/server/src/db/schema/account.ts | 35 +++++++ packages/server/src/db/schema/session.ts | 3 +- packages/server/src/db/schema/user.ts | 13 ++- packages/server/src/lib/auth.ts | 39 +++++++- packages/server/src/services/admin.ts | 4 +- packages/server/src/services/auth.ts | 7 +- packages/server/src/services/user.ts | 10 +- 47 files changed, 359 insertions(+), 171 deletions(-) diff --git a/apps/dokploy/components/dashboard/projects/handle-project.tsx b/apps/dokploy/components/dashboard/projects/handle-project.tsx index 08e3e0a8d..5b0777716 100644 --- a/apps/dokploy/components/dashboard/projects/handle-project.tsx +++ b/apps/dokploy/components/dashboard/projects/handle-project.tsx @@ -21,6 +21,7 @@ import { import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; +import { authClient } from "@/lib/auth"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon, SquarePen } from "lucide-react"; @@ -97,6 +98,18 @@ export const HandleProject = ({ projectId }: Props) => { ); }); }; + // useEffect(() => { + // const getUsers = async () => { + // const users = await authClient.admin.listUsers({ + // query: { + // limit: 100, + // }, + // }); + // console.log(users); + // }; + + // getUsers(); + // }); return ( @@ -110,10 +123,26 @@ export const HandleProject = ({ projectId }: Props) => { Update ) : ( - + <> + + + )} diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 8d058b6cb..98ef14a4b 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -57,7 +57,7 @@ export const ShowProjects = () => { authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); const { mutateAsync } = api.project.remove.useMutation(); @@ -91,7 +91,7 @@ export const ShowProjects = () => { - {(auth?.rol === "admin" || user?.canCreateProjects) && ( + {(auth?.role === "admin" || user?.canCreateProjects) && (
@@ -293,7 +293,7 @@ export const ShowProjects = () => {
e.stopPropagation()} > - {(auth?.rol === "admin" || + {(auth?.role === "admin" || user?.canDeleteProjects) && ( diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 4d3c75f98..f52fd14f8 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -28,6 +28,7 @@ import { BookIcon, CircuitBoard, GlobeIcon } from "lucide-react"; import { useRouter } from "next/router"; import React from "react"; import { StatusTooltip } from "../shared/status-tooltip"; +import { authClient } from "@/lib/auth"; type Project = Awaited>; @@ -35,8 +36,10 @@ export const SearchCommand = () => { const router = useRouter(); const [open, setOpen] = React.useState(false); const [search, setSearch] = React.useState(""); - - const { data } = api.project.all.useQuery(); + const { data: session } = authClient.getSession(); + const { data } = api.project.all.useQuery(undefined, { + enabled: !!session, + }); const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); React.useEffect(() => { diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 44a4b0ed0..88740054b 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -155,7 +155,7 @@ const MENU: Menu = { // Only enabled for admins and users with access to Traefik files in non-cloud environments isEnabled: ({ auth, user, isCloud }) => !!( - (auth?.rol === "admin" || user?.canAccessToTraefikFiles) && + (auth?.role === "admin" || user?.canAccessToTraefikFiles) && !isCloud ), }, @@ -166,7 +166,7 @@ const MENU: Menu = { icon: BlocksIcon, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!((auth?.rol === "admin" || user?.canAccessToDocker) && !isCloud), + !!((auth?.role === "admin" || user?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -175,7 +175,7 @@ const MENU: Menu = { icon: PieChart, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!((auth?.rol === "admin" || user?.canAccessToDocker) && !isCloud), + !!((auth?.role === "admin" || user?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -184,7 +184,7 @@ const MENU: Menu = { icon: Forward, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!((auth?.rol === "admin" || user?.canAccessToDocker) && !isCloud), + !!((auth?.role === "admin" || user?.canAccessToDocker) && !isCloud), }, // Legacy unused menu, adjusted to the new structure @@ -252,7 +252,7 @@ const MENU: Menu = { icon: Activity, // Only enabled for admins in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!(auth?.rol === "admin" && !isCloud), + !!(auth?.role === "admin" && !isCloud), }, { isSingle: true, @@ -266,7 +266,7 @@ const MENU: Menu = { url: "/dashboard/settings/servers", icon: Server, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), }, { isSingle: true, @@ -274,7 +274,7 @@ const MENU: Menu = { icon: Users, url: "/dashboard/settings/users", // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), }, { isSingle: true, @@ -283,7 +283,7 @@ const MENU: Menu = { url: "/dashboard/settings/ssh-keys", // Only enabled for admins and users with access to SSH keys isEnabled: ({ auth, user }) => - !!(auth?.rol === "admin" || user?.canAccessToSSHKeys), + !!(auth?.role === "admin" || user?.canAccessToSSHKeys), }, { isSingle: true, @@ -292,7 +292,7 @@ const MENU: Menu = { icon: GitBranch, // Only enabled for admins and users with access to Git providers isEnabled: ({ auth, user }) => - !!(auth?.rol === "admin" || user?.canAccessToGitProviders), + !!(auth?.role === "admin" || user?.canAccessToGitProviders), }, { isSingle: true, @@ -300,7 +300,7 @@ const MENU: Menu = { url: "/dashboard/settings/registry", icon: Package, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), }, { isSingle: true, @@ -308,7 +308,7 @@ const MENU: Menu = { url: "/dashboard/settings/destinations", icon: Database, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), }, { @@ -317,7 +317,7 @@ const MENU: Menu = { url: "/dashboard/settings/certificates", icon: ShieldCheck, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), }, { isSingle: true, @@ -326,7 +326,7 @@ const MENU: Menu = { icon: Boxes, // Only enabled for admins in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!(auth?.rol === "admin" && !isCloud), + !!(auth?.role === "admin" && !isCloud), }, { isSingle: true, @@ -334,7 +334,7 @@ const MENU: Menu = { url: "/dashboard/settings/notifications", icon: Bell, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.rol === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), }, { isSingle: true, @@ -343,7 +343,7 @@ const MENU: Menu = { icon: CreditCard, // Only enabled for admins in cloud environments isEnabled: ({ auth, user, isCloud }) => - !!(auth?.rol === "admin" && isCloud), + !!(auth?.role === "admin" && isCloud), }, ], @@ -537,7 +537,7 @@ export default function Page({ children }: Props) { authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); @@ -783,7 +783,7 @@ export default function Page({ children }: Props) { ))} - {!isCloud && auth?.rol === "admin" && ( + {!isCloud && auth?.role === "admin" && ( diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 67c5cbfdc..d2ad153be 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -24,6 +24,7 @@ import { useRouter } from "next/router"; import { useEffect, useRef, useState } from "react"; import { ModeToggle } from "../ui/modeToggle"; import { SidebarMenuButton } from "../ui/sidebar"; +import { authClient } from "@/lib/auth"; const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7; @@ -40,7 +41,7 @@ export const UserNav = () => { }, ); const { locale, setLocale } = useLocale(); - const { mutateAsync } = api.auth.logout.useMutation(); + // const { mutateAsync } = api.auth.logout.useMutation(); return ( @@ -178,9 +179,12 @@ export const UserNav = () => { { - await mutateAsync().then(() => { + await authClient.signOut().then(() => { router.push("/"); }); + // await mutateAsync().then(() => { + // router.push("/"); + // }); }} > Log out diff --git a/apps/dokploy/lib/auth.ts b/apps/dokploy/lib/auth.ts index d0b50c7e3..d050b3608 100644 --- a/apps/dokploy/lib/auth.ts +++ b/apps/dokploy/lib/auth.ts @@ -1,4 +1,7 @@ import { createAuthClient } from "better-auth/react"; +import { organizationClient } from "better-auth/client/plugins"; + export const authClient = createAuthClient({ baseURL: "http://localhost:3000", // the base url of your auth server + plugins: [organizationClient()], }); diff --git a/apps/dokploy/pages/api/providers/github/setup.ts b/apps/dokploy/pages/api/providers/github/setup.ts index a1ce98d48..38a281b1b 100644 --- a/apps/dokploy/pages/api/providers/github/setup.ts +++ b/apps/dokploy/pages/api/providers/github/setup.ts @@ -42,7 +42,7 @@ export default async function handler( const auth = await findAuthById(value as string); let adminId = ""; - if (auth.rol === "admin") { + if (auth.role === "admin") { const admin = await findAdminByAuthId(auth.id); adminId = admin.adminId; } else { diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index 96387c07d..5449b9bdc 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.rol === "user") { + if (auth.role === "user") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index ea23ad3a8..f8c39a3a3 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -206,7 +206,7 @@ const Project = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); const { data, isLoading, refetch } = api.project.one.useQuery({ projectId }); @@ -335,7 +335,7 @@ const Project = ( {data?.description} - {(auth?.rol === "admin" || user?.canCreateServices) && ( + {(auth?.role === "admin" || user?.canCreateServices) && (
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 9d3ffe4fb..e2bf8455b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -93,7 +93,7 @@ const Service = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); @@ -186,7 +186,7 @@ const Service = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index c68402142..ddc613e5f 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -87,7 +87,7 @@ const Service = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); @@ -181,7 +181,7 @@ const Service = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index a2ee90512..813ea7292 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -68,7 +68,7 @@ const Mariadb = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -154,7 +154,7 @@ const Mariadb = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index 4f3947c2a..0361b7e29 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -68,7 +68,7 @@ const Mongo = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); @@ -156,7 +156,7 @@ const Mongo = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index baf7e6f8b..eff9cfe79 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -67,7 +67,7 @@ const MySql = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); @@ -156,7 +156,7 @@ const MySql = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index e3fd8b444..808c7aee4 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -66,7 +66,7 @@ const Postgresql = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); @@ -154,7 +154,7 @@ const Postgresql = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 3421e759c..82180401b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -67,7 +67,7 @@ const Redis = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.rol === "user", + enabled: !!auth?.id && auth?.role === "user", }, ); @@ -155,7 +155,7 @@ const Redis = (
- {(auth?.rol === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "admin" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/settings/billing.tsx b/apps/dokploy/pages/dashboard/settings/billing.tsx index 5c58e25a9..dd70c22ae 100644 --- a/apps/dokploy/pages/dashboard/settings/billing.tsx +++ b/apps/dokploy/pages/dashboard/settings/billing.tsx @@ -30,7 +30,7 @@ export async function getServerSideProps( } const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/certificates.tsx b/apps/dokploy/pages/dashboard/settings/certificates.tsx index 732b6622a..d5bbf0332 100644 --- a/apps/dokploy/pages/dashboard/settings/certificates.tsx +++ b/apps/dokploy/pages/dashboard/settings/certificates.tsx @@ -25,7 +25,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index a6605c573..9affb9ee2 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -34,7 +34,7 @@ export async function getServerSideProps( }; } const { user, session } = await validateRequest(ctx.req, ctx.res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/destinations.tsx b/apps/dokploy/pages/dashboard/settings/destinations.tsx index c5c6f2f81..671087fe8 100644 --- a/apps/dokploy/pages/dashboard/settings/destinations.tsx +++ b/apps/dokploy/pages/dashboard/settings/destinations.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index dc37522ac..6e8bf5874 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -51,7 +51,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.rol === "user") { + if (auth.role === "user") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/settings/index.tsx b/apps/dokploy/pages/dashboard/settings/index.tsx index bf76607b4..9b4c80485 100644 --- a/apps/dokploy/pages/dashboard/settings/index.tsx +++ b/apps/dokploy/pages/dashboard/settings/index.tsx @@ -190,7 +190,7 @@ export async function getServerSideProps( }, }; } - if (user.rol === "user") { + if (user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/notifications.tsx b/apps/dokploy/pages/dashboard/settings/notifications.tsx index 0b75fc67a..054f92e5a 100644 --- a/apps/dokploy/pages/dashboard/settings/notifications.tsx +++ b/apps/dokploy/pages/dashboard/settings/notifications.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 44e007f11..00f10ec72 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -19,7 +19,7 @@ const Page = () => { authId: data?.id || "", }, { - enabled: !!data?.id && data?.rol === "user", + enabled: !!data?.id && data?.role === "user", }, ); @@ -28,7 +28,7 @@ const Page = () => {
- {(user?.canAccessToAPI || data?.rol === "admin") && } + {(user?.canAccessToAPI || data?.role === "admin") && } {isCloud && }
@@ -62,7 +62,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); await helpers.auth.get.prefetch(); - if (user?.rol === "user") { + if (user?.role === "user") { await helpers.user.byAuthId.prefetch({ authId: user.authId, }); diff --git a/apps/dokploy/pages/dashboard/settings/registry.tsx b/apps/dokploy/pages/dashboard/settings/registry.tsx index 49c9ec20f..441bee395 100644 --- a/apps/dokploy/pages/dashboard/settings/registry.tsx +++ b/apps/dokploy/pages/dashboard/settings/registry.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index a229d0bcb..98710d06e 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -107,7 +107,7 @@ export async function getServerSideProps( }, }; } - if (user.rol === "user") { + if (user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/servers.tsx b/apps/dokploy/pages/dashboard/settings/servers.tsx index 36fde9834..27f88be24 100644 --- a/apps/dokploy/pages/dashboard/settings/servers.tsx +++ b/apps/dokploy/pages/dashboard/settings/servers.tsx @@ -36,7 +36,7 @@ export async function getServerSideProps( }, }; } - if (user.rol === "user") { + if (user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index 239edac6a..fcb11666f 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -51,7 +51,7 @@ export async function getServerSideProps( const auth = await helpers.auth.get.fetch(); await helpers.settings.isCloud.prefetch(); - if (auth.rol === "user") { + if (auth.role === "user") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index e70728909..ace439692 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.rol === "user") { + if (!user || user.role === "user") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index 3a8a60b28..524b464af 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.rol === "user") { + if (auth.role === "user") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 9cd7eefcf..b8ff13f3f 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.rol === "user") { + if (auth.role === "user") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 2691fc5c3..7877e65cc 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -16,8 +16,11 @@ import { Input } from "@/components/ui/input"; import { authClient } from "@/lib/auth"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server"; +import { auth, IS_CLOUD, isAdminPresent } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { zodResolver } from "@hookform/resolvers/zod"; +import { getSessionCookie, Session } from "better-auth"; +import { betterFetch } from "better-auth/react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; @@ -66,7 +69,7 @@ export default function Home({ IS_CLOUD }: Props) { const router = useRouter(); const form = useForm({ defaultValues: { - email: "siumauricio@hotmail.com", + email: "user5@yopmail.com", password: "Password1234", }, resolver: zodResolver(loginSchema), @@ -82,6 +85,17 @@ export default function Home({ IS_CLOUD }: Props) { password: values.password, }); + if (!error) { + // if (data) { + // setTemp(data); + // } else { + toast.success("Successfully signed in", { + duration: 2000, + }); + // router.push("/dashboard/projects"); + // } + } + console.log(data, error); // await mutateAsync({ // email: values.email.toLowerCase(), @@ -208,51 +222,51 @@ Home.getLayout = (page: ReactElement) => { return {page}; }; export async function getServerSideProps(context: GetServerSidePropsContext) { - // if (IS_CLOUD) { - // try { - // const { user } = await validateRequest(context.req, context.res); + if (IS_CLOUD) { + try { + const { user } = await validateRequest(context.req); + if (user) { + return { + redirect: { + permanent: true, + destination: "/dashboard/projects", + }, + }; + } + } catch (error) {} - // if (user) { - // return { - // redirect: { - // permanent: true, - // destination: "/dashboard/projects", - // }, - // }; - // } - // } catch (error) {} + return { + props: { + IS_CLOUD: IS_CLOUD, + }, + }; + } + const hasAdmin = await isAdminPresent(); - // return { - // props: { - // IS_CLOUD: IS_CLOUD, - // }, - // }; - // } - // const hasAdmin = await isAdminPresent(); + if (!hasAdmin) { + return { + redirect: { + permanent: true, + destination: "/register", + }, + }; + } - // if (!hasAdmin) { - // return { - // redirect: { - // permanent: true, - // destination: "/register", - // }, - // }; - // } + const { user } = await validateRequest(context.req); + console.log("Response", user); - // const { user } = await validateRequest(context.req, context.res); - - // if (user) { - // return { - // redirect: { - // permanent: true, - // destination: "/dashboard/projects", - // }, - // }; - // } + if (user) { + return { + redirect: { + permanent: true, + destination: "/dashboard/projects", + }, + }; + } return { props: { - // hasAdmin, + hasAdmin, }, }; } diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index 4ff3d4dad..f79024788 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -32,6 +32,9 @@ import { z } from "zod"; const registerSchema = z .object({ + name: z.string().min(1, { + message: "Name is required", + }), email: z .string() .min(1, { @@ -80,6 +83,7 @@ const Register = ({ isCloud }: Props) => { const form = useForm({ defaultValues: { + name: "Mauricio Siu", email: "user5@yopmail.com", password: "Password1234", confirmPassword: "Password1234", @@ -95,8 +99,17 @@ const Register = ({ isCloud }: Props) => { const { data, error } = await authClient.signUp.email({ email: values.email, password: values.password, - name: "Mauricio Siu", + name: values.name, }); + + // const { data, error } = await authClient.admin.createUser({ + // name: values.name, + // email: values.email, + // password: values.password, + // role: "superAdmin", + // }); + + // consol/e.log(data, error); // await mutateAsync({ // email: values.email.toLowerCase(), // password: values.password, @@ -153,6 +166,19 @@ const Register = ({ isCloud }: Props) => { className="grid gap-4" >
+ ( + + Name + + + + + + )} + /> { - const auth = await findAuthById(ctx.user.authId); + console.log(ctx.user); + const auth = await findAuthById(ctx.user.id); return auth; }), diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 35b2669bd..c2852ff6b 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -124,9 +124,10 @@ export const projectRouter = createTRPCRouter({ return project; }), all: protectedProcedure.query(async ({ ctx }) => { + // console.log(ctx.user); if (ctx.user.rol === "user") { const { accessedProjects, accessedServices } = await findUserByAuthId( - ctx.user.authId, + ctx.user.id, ); if (accessedProjects.length === 0) { @@ -139,7 +140,7 @@ export const projectRouter = createTRPCRouter({ accessedProjects.map((projectId) => sql`${projectId}`), sql`, `, )})`, - eq(projects.adminId, ctx.user.adminId), + eq(projects.userId, ctx.user.id), ), with: { applications: { @@ -193,7 +194,7 @@ export const projectRouter = createTRPCRouter({ }, }, }, - where: eq(projects.adminId, ctx.user.adminId), + where: eq(projects.userId, ctx.user.id), orderBy: desc(projects.createdAt), }); }), diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index cb0e32d9a..116de6ad8 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -655,7 +655,7 @@ export const settingsRouter = createTRPCRouter({ return true; }), - isCloud: protectedProcedure.query(async () => { + isCloud: publicProcedure.query(async () => { return IS_CLOUD; }), health: publicProcedure.query(async () => { diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index db4f7adfe..9579407b5 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -9,7 +9,8 @@ // import { getServerAuthSession } from "@/server/auth"; import { db } from "@/server/db"; -import { validateBearerToken, validateRequest } from "@dokploy/server"; +import { validateBearerToken } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import type { OpenApiMeta } from "@dokploy/trpc-openapi"; import { TRPCError, initTRPC } from "@trpc/server"; import type { CreateNextContextOptions } from "@trpc/server/adapters/next"; @@ -18,7 +19,7 @@ import { experimental_isMultipartFormDataRequest, experimental_parseMultipartFormData, } from "@trpc/server/adapters/node-http/content-type/form-data"; -import type { Session, User } from "lucia"; +import type { Session, User } from "better-auth"; import superjson from "superjson"; import { ZodError } from "zod"; /** @@ -30,7 +31,7 @@ import { ZodError } from "zod"; */ interface CreateContextOptions { - user: (User & { authId: string; adminId: string }) | null; + user: (User & { authId: string; rol: "admin" | "user" }) | null; session: Session | null; req: CreateNextContextOptions["req"]; res: CreateNextContextOptions["res"]; @@ -65,10 +66,11 @@ const createInnerTRPCContext = (opts: CreateContextOptions) => { export const createTRPCContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; - let { session, user } = await validateBearerToken(req); + // Get from the request + let { session, user } = await validateRequest(req); if (!session) { - const cookieResult = await validateRequest(req, res); + const cookieResult = await validateRequest(req); session = cookieResult.session; user = cookieResult.user; } @@ -79,12 +81,10 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { session: session, ...((user && { user: { - authId: user.id, + authId: "Null", email: user.email, - rol: user.rol, + rol: user.role, id: user.id, - secret: user.secret, - adminId: user.adminId, }, }) || { user: null, diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 8865078b2..0bd911414 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,9 +1,9 @@ import { - boolean, - integer, pgTable, text, + integer, timestamp, + boolean, } from "drizzle-orm/pg-core"; export const user = pgTable("user", { @@ -14,10 +14,6 @@ export const user = pgTable("user", { image: text("image"), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), - role: text("role"), - banned: boolean("banned"), - banReason: text("ban_reason"), - banExpires: timestamp("ban_expires"), }); export const session = pgTable("session", { @@ -31,32 +27,67 @@ export const session = pgTable("session", { userId: text("user_id") .notNull() .references(() => user.id), - impersonatedBy: text("impersonated_by"), + activeOrganizationId: text("active_organization_id"), }); -// export const account = pgTable("account", { -// id: text("id").primaryKey(), -// accountId: text("account_id").notNull(), -// providerId: text("provider_id").notNull(), -// userId: text("user_id") -// .notNull() -// .references(() => user.id), -// accessToken: text("access_token"), -// refreshToken: text("refresh_token"), -// idToken: text("id_token"), -// accessTokenExpiresAt: timestamp("access_token_expires_at"), -// refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), -// scope: text("scope"), -// password: text("password"), -// createdAt: timestamp("created_at").notNull(), -// updatedAt: timestamp("updated_at").notNull(), -// }); +export const account = pgTable("account", { + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => user.id), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at"), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), + scope: text("scope"), + password: text("password"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), +}); -// export const verification = pgTable("verification", { -// id: text("id").primaryKey(), -// identifier: text("identifier").notNull(), -// value: text("value").notNull(), -// expiresAt: timestamp("expires_at").notNull(), -// createdAt: timestamp("created_at"), -// updatedAt: timestamp("updated_at"), -// }); +export const verification = pgTable("verification", { + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at").notNull(), + createdAt: timestamp("created_at"), + updatedAt: timestamp("updated_at"), +}); + +export const organization = pgTable("organization", { + id: text("id").primaryKey(), + name: text("name").notNull(), + slug: text("slug").unique(), + logo: text("logo"), + createdAt: timestamp("created_at").notNull(), + metadata: text("metadata"), +}); + +export const member = pgTable("member", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id), + userId: text("user_id") + .notNull() + .references(() => user.id), + role: text("role").notNull(), + createdAt: timestamp("created_at").notNull(), +}); + +export const invitation = pgTable("invitation", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id), + email: text("email").notNull(), + role: text("role"), + status: text("status").notNull(), + expiresAt: timestamp("expires_at").notNull(), + inviterId: text("inviter_id") + .notNull() + .references(() => user.id), +}); diff --git a/packages/server/src/auth/auth.ts b/packages/server/src/auth/auth.ts index ab340d0af..423c4333f 100644 --- a/packages/server/src/auth/auth.ts +++ b/packages/server/src/auth/auth.ts @@ -6,9 +6,9 @@ import { TimeSpan } from "lucia"; import { Lucia } from "lucia/dist/core.js"; import type { Session, User } from "lucia/dist/core.js"; import { db } from "../db"; -import { type DatabaseUser, auth, sessionTable } from "../db/schema"; +import { type DatabaseUser, auth, session } from "../db/schema"; -export const adapter = new DrizzlePostgreSQLAdapter(db, sessionTable, auth); +export const adapter = new DrizzlePostgreSQLAdapter(db, session, auth); export const lucia = new Lucia(adapter, { sessionCookie: { @@ -46,6 +46,7 @@ export async function validateRequest( req: IncomingMessage, res: ServerResponse, ): ReturnValidateToken { + console.log(session); const sessionId = lucia.readSessionCookie(req.headers.cookie ?? ""); if (!sessionId) { diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index a332ec920..38c630275 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -32,3 +32,38 @@ export const verification = pgTable("verification", { createdAt: timestamp("created_at"), updatedAt: timestamp("updated_at"), }); + +export const organization = pgTable("organization", { + id: text("id").primaryKey(), + name: text("name").notNull(), + slug: text("slug").unique(), + logo: text("logo"), + createdAt: timestamp("created_at").notNull(), + metadata: text("metadata"), +}); + +export const member = pgTable("member", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id), + userId: text("user_id") + .notNull() + .references(() => user.id), + role: text("role").notNull(), + createdAt: timestamp("created_at").notNull(), +}); + +export const invitation = pgTable("invitation", { + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id), + email: text("email").notNull(), + role: text("role"), + status: text("status").notNull(), + expiresAt: timestamp("expires_at").notNull(), + inviterId: text("inviter_id") + .notNull() + .references(() => user.id), +}); diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index e7d579fb1..66a410b30 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -2,7 +2,7 @@ import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { user } from "./user"; // OLD TABLE -export const sessionTable = pgTable("session", { +export const session = pgTable("session", { id: text("id").primaryKey(), expiresAt: timestamp("expires_at").notNull(), token: text("token").notNull().unique(), @@ -14,4 +14,5 @@ export const sessionTable = pgTable("session", { .notNull() .references(() => user.id), impersonatedBy: text("impersonated_by"), + activeOrganizationId: text("active_organization_id"), }); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 372b79f87..bb06f8bc0 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -24,11 +24,16 @@ export const user = pgTable("user", { isRegistered: boolean("isRegistered").notNull().default(false), expirationDate: timestamp("expirationDate", { precision: 3, - mode: "string", - }).notNull(), - createdAt: text("createdAt") + mode: "date", + }) .notNull() - .$defaultFn(() => new Date().toISOString()), + .$defaultFn(() => new Date()), + createdAt: timestamp("createdAt", { + precision: 3, + mode: "date", + }) + .notNull() + .$defaultFn(() => new Date()), canCreateProjects: boolean("canCreateProjects").notNull().default(false), canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), canCreateServices: boolean("canCreateServices").notNull().default(false), diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 93eed4ca2..082176b2c 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -1,8 +1,10 @@ import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { admin } from "better-auth/plugins"; +import { admin, createAuthMiddleware, organization } from "better-auth/plugins"; import { db } from "../db"; import * as schema from "../db/schema"; +import type { IncomingMessage } from "node:http"; +import { eq } from "drizzle-orm"; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", @@ -11,5 +13,38 @@ export const auth = betterAuth({ emailAndPassword: { enabled: true, }, - plugins: [admin()], + hooks: { + after: createAuthMiddleware(async (ctx) => { + if (ctx.path.startsWith("/sign-up")) { + const newSession = ctx.context.newSession; + await db + .update(schema.user) + .set({ + role: "admin", + }) + .where(eq(schema.user.id, newSession?.user?.id || "")); + } + }), + }, + user: { + additionalFields: {}, + }, + plugins: [organization()], }); + +export const validateRequest = async (request: IncomingMessage) => { + const session = await auth.api.getSession({ + headers: new Headers({ + cookie: request.headers.cookie || "", + }), + }); + + if (!session?.session || !session.user) { + return { + session: null, + user: null, + }; + } + + return session; +}; diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index ea0df9b08..21c8f13e5 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -94,8 +94,8 @@ export const updateAdminById = async ( }; export const isAdminPresent = async () => { - const admin = await db.query.users.findFirst({ - where: eq(users.role, "admin"), + const admin = await db.query.user.findFirst({ + where: eq(user.role, "admin"), }); if (!admin) { return false; diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 376f10e90..f391d011d 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -100,10 +100,11 @@ export const findAuthByEmail = async (email: string) => { }; export const findAuthById = async (authId: string) => { - const result = await db.query.auth.findFirst({ - where: eq(auth.id, authId), + const result = await db.query.user.findFirst({ + where: eq(user.id, authId), columns: { - password: false, + createdAt: false, + updatedAt: false, }, }); if (!result) { diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index f50e678ad..afc3ec009 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -20,18 +20,16 @@ export const findUserById = async (userId: string) => { export const findUserByAuthId = async (authId: string) => { const userR = await db.query.user.findFirst({ - where: eq(user.authId, authId), - with: { - auth: true, - }, + where: eq(user.id, authId), + with: {}, }); - if (!user) { + if (!userR) { throw new TRPCError({ code: "NOT_FOUND", message: "User not found", }); } - return user; + return userR; }; export const findUsers = async (adminId: string) => { From afd3d2eea3706f5a98db6ad079f97dfd681f052f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 9 Feb 2025 20:53:14 -0600 Subject: [PATCH 004/126] refactor: lint --- apps/dokploy/components/dashboard/search-command.tsx | 2 +- apps/dokploy/components/layouts/user-nav.tsx | 2 +- apps/dokploy/lib/auth.ts | 2 +- apps/dokploy/pages/index.tsx | 4 ++-- packages/server/auth-schema.ts | 4 ++-- packages/server/src/lib/auth.ts | 4 ++-- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index f52fd14f8..952bbe5c8 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -18,6 +18,7 @@ import { CommandList, CommandSeparator, } from "@/components/ui/command"; +import { authClient } from "@/lib/auth"; import { type Services, extractServices, @@ -28,7 +29,6 @@ import { BookIcon, CircuitBoard, GlobeIcon } from "lucide-react"; import { useRouter } from "next/router"; import React from "react"; import { StatusTooltip } from "../shared/status-tooltip"; -import { authClient } from "@/lib/auth"; type Project = Awaited>; diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index d2ad153be..18d459f07 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -15,6 +15,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { authClient } from "@/lib/auth"; import { Languages } from "@/lib/languages"; import { api } from "@/utils/api"; import useLocale from "@/utils/hooks/use-locale"; @@ -24,7 +25,6 @@ import { useRouter } from "next/router"; import { useEffect, useRef, useState } from "react"; import { ModeToggle } from "../ui/modeToggle"; import { SidebarMenuButton } from "../ui/sidebar"; -import { authClient } from "@/lib/auth"; const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7; diff --git a/apps/dokploy/lib/auth.ts b/apps/dokploy/lib/auth.ts index d050b3608..a5b193700 100644 --- a/apps/dokploy/lib/auth.ts +++ b/apps/dokploy/lib/auth.ts @@ -1,5 +1,5 @@ -import { createAuthClient } from "better-auth/react"; import { organizationClient } from "better-auth/client/plugins"; +import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient({ baseURL: "http://localhost:3000", // the base url of your auth server diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 7877e65cc..e5cc0500d 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -16,10 +16,10 @@ import { Input } from "@/components/ui/input"; import { authClient } from "@/lib/auth"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { auth, IS_CLOUD, isAdminPresent } from "@dokploy/server"; +import { IS_CLOUD, auth, isAdminPresent } from "@dokploy/server"; import { validateRequest } from "@dokploy/server/lib/auth"; import { zodResolver } from "@hookform/resolvers/zod"; -import { getSessionCookie, Session } from "better-auth"; +import { Session, getSessionCookie } from "better-auth"; import { betterFetch } from "better-auth/react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 0bd911414..10e9965b5 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,9 +1,9 @@ import { + boolean, + integer, pgTable, text, - integer, timestamp, - boolean, } from "drizzle-orm/pg-core"; export const user = pgTable("user", { diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 082176b2c..8a3e1781c 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -1,10 +1,10 @@ +import type { IncomingMessage } from "node:http"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { admin, createAuthMiddleware, organization } from "better-auth/plugins"; +import { eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; -import type { IncomingMessage } from "node:http"; -import { eq } from "drizzle-orm"; export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", From 1db6ba94f4153b39d0295f18c72bf6ceee6e003d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 9 Feb 2025 21:36:36 -0600 Subject: [PATCH 005/126] refactor: remove --- apps/dokploy/drizzle/meta/0066_snapshot.json | 4686 ----------------- apps/dokploy/drizzle/meta/_journal.json | 8 +- .../{0066_broad_marrow.sql => old_migration} | 0 3 files changed, 1 insertion(+), 4693 deletions(-) delete mode 100644 apps/dokploy/drizzle/meta/0066_snapshot.json rename apps/dokploy/drizzle/{0066_broad_marrow.sql => old_migration} (100%) diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json deleted file mode 100644 index 8572b47b4..000000000 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ /dev/null @@ -1,4686 +0,0 @@ -{ - "id": "e7c50cc6-9e18-47c5-b155-dd31fc8bd774", - "prevId": "1240ec96-1751-4de3-b64f-cef9cb716786", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.application": { - "name": "application", - "schema": "", - "columns": { - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewEnv": { - "name": "previewEnv", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewBuildArgs": { - "name": "previewBuildArgs", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewWildcard": { - "name": "previewWildcard", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewPort": { - "name": "previewPort", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 3000 - }, - "previewHttps": { - "name": "previewHttps", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "previewPath": { - "name": "previewPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - }, - "previewLimit": { - "name": "previewLimit", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 3 - }, - "isPreviewDeploymentsActive": { - "name": "isPreviewDeploymentsActive", - "type": "boolean", - "primaryKey": false, - "notNull": false, - "default": false - }, - "buildArgs": { - "name": "buildArgs", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "enabled": { - "name": "enabled", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "subtitle": { - "name": "subtitle", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refreshToken": { - "name": "refreshToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sourceType": { - "name": "sourceType", - "type": "sourceType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'github'" - }, - "repository": { - "name": "repository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "owner": { - "name": "owner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "branch": { - "name": "branch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "buildPath": { - "name": "buildPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "autoDeploy": { - "name": "autoDeploy", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "gitlabProjectId": { - "name": "gitlabProjectId", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "gitlabRepository": { - "name": "gitlabRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabOwner": { - "name": "gitlabOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabBranch": { - "name": "gitlabBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabBuildPath": { - "name": "gitlabBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "gitlabPathNamespace": { - "name": "gitlabPathNamespace", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketRepository": { - "name": "bitbucketRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketOwner": { - "name": "bitbucketOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketBranch": { - "name": "bitbucketBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketBuildPath": { - "name": "bitbucketBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "registryUrl": { - "name": "registryUrl", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitUrl": { - "name": "customGitUrl", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitBranch": { - "name": "customGitBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitBuildPath": { - "name": "customGitBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitSSHKeyId": { - "name": "customGitSSHKeyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerfile": { - "name": "dockerfile", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerContextPath": { - "name": "dockerContextPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerBuildStage": { - "name": "dockerBuildStage", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dropBuildPath": { - "name": "dropBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "healthCheckSwarm": { - "name": "healthCheckSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "restartPolicySwarm": { - "name": "restartPolicySwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "placementSwarm": { - "name": "placementSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "updateConfigSwarm": { - "name": "updateConfigSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "rollbackConfigSwarm": { - "name": "rollbackConfigSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "modeSwarm": { - "name": "modeSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "labelsSwarm": { - "name": "labelsSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "networkSwarm": { - "name": "networkSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "replicas": { - "name": "replicas", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 1 - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "buildType": { - "name": "buildType", - "type": "buildType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'nixpacks'" - }, - "herokuVersion": { - "name": "herokuVersion", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'24'" - }, - "publishDirectory": { - "name": "publishDirectory", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "registryId": { - "name": "registryId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "githubId": { - "name": "githubId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabId": { - "name": "gitlabId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketId": { - "name": "bitbucketId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { - "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", - "tableFrom": "application", - "tableTo": "ssh-key", - "columnsFrom": [ - "customGitSSHKeyId" - ], - "columnsTo": [ - "sshKeyId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_registryId_registry_registryId_fk": { - "name": "application_registryId_registry_registryId_fk", - "tableFrom": "application", - "tableTo": "registry", - "columnsFrom": [ - "registryId" - ], - "columnsTo": [ - "registryId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_projectId_project_projectId_fk": { - "name": "application_projectId_project_projectId_fk", - "tableFrom": "application", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "application_githubId_github_githubId_fk": { - "name": "application_githubId_github_githubId_fk", - "tableFrom": "application", - "tableTo": "github", - "columnsFrom": [ - "githubId" - ], - "columnsTo": [ - "githubId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_gitlabId_gitlab_gitlabId_fk": { - "name": "application_gitlabId_gitlab_gitlabId_fk", - "tableFrom": "application", - "tableTo": "gitlab", - "columnsFrom": [ - "gitlabId" - ], - "columnsTo": [ - "gitlabId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_bitbucketId_bitbucket_bitbucketId_fk": { - "name": "application_bitbucketId_bitbucket_bitbucketId_fk", - "tableFrom": "application", - "tableTo": "bitbucket", - "columnsFrom": [ - "bitbucketId" - ], - "columnsTo": [ - "bitbucketId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_serverId_server_serverId_fk": { - "name": "application_serverId_server_serverId_fk", - "tableFrom": "application", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "application_appName_unique": { - "name": "application_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.postgres": { - "name": "postgres", - "schema": "", - "columns": { - "postgresId": { - "name": "postgresId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseName": { - "name": "databaseName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "postgres_projectId_project_projectId_fk": { - "name": "postgres_projectId_project_projectId_fk", - "tableFrom": "postgres", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "postgres_serverId_server_serverId_fk": { - "name": "postgres_serverId_server_serverId_fk", - "tableFrom": "postgres", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "postgres_appName_unique": { - "name": "postgres_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.user": { - "name": "user", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "isRegistered": { - "name": "isRegistered", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "expirationDate": { - "name": "expirationDate", - "type": "timestamp(3)", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "canCreateProjects": { - "name": "canCreateProjects", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToSSHKeys": { - "name": "canAccessToSSHKeys", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canCreateServices": { - "name": "canCreateServices", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canDeleteProjects": { - "name": "canDeleteProjects", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canDeleteServices": { - "name": "canDeleteServices", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToDocker": { - "name": "canAccessToDocker", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToAPI": { - "name": "canAccessToAPI", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToGitProviders": { - "name": "canAccessToGitProviders", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToTraefikFiles": { - "name": "canAccessToTraefikFiles", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "accesedProjects": { - "name": "accesedProjects", - "type": "text[]", - "primaryKey": false, - "notNull": true, - "default": "ARRAY[]::text[]" - }, - "accesedServices": { - "name": "accesedServices", - "type": "text[]", - "primaryKey": false, - "notNull": true, - "default": "ARRAY[]::text[]" - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "email_verified": { - "name": "email_verified", - "type": "boolean", - "primaryKey": false, - "notNull": true - }, - "image": { - "name": "image", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "banned": { - "name": "banned", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "ban_reason": { - "name": "ban_reason", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "ban_expires": { - "name": "ban_expires", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "serverIp": { - "name": "serverIp", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - }, - "host": { - "name": "host", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "letsEncryptEmail": { - "name": "letsEncryptEmail", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sshPrivateKey": { - "name": "sshPrivateKey", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "enableDockerCleanup": { - "name": "enableDockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "enableLogRotation": { - "name": "enableLogRotation", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "enablePaidFeatures": { - "name": "enablePaidFeatures", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "metricsConfig": { - "name": "metricsConfig", - "type": "jsonb", - "primaryKey": false, - "notNull": true, - "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" - }, - "cleanupCacheApplications": { - "name": "cleanupCacheApplications", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "cleanupCacheOnPreviews": { - "name": "cleanupCacheOnPreviews", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "cleanupCacheOnCompose": { - "name": "cleanupCacheOnCompose", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "user_email_unique": { - "name": "user_email_unique", - "nullsNotDistinct": false, - "columns": [ - "email" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.admin": { - "name": "admin", - "schema": "", - "columns": {}, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.auth": { - "name": "auth", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "rol": { - "name": "rol", - "type": "Roles", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "image": { - "name": "image", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "secret": { - "name": "secret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "is2FAEnabled": { - "name": "is2FAEnabled", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "resetPasswordToken": { - "name": "resetPasswordToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "resetPasswordExpiresAt": { - "name": "resetPasswordExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationToken": { - "name": "confirmationToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationExpiresAt": { - "name": "confirmationExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "auth_email_unique": { - "name": "auth_email_unique", - "nullsNotDistinct": false, - "columns": [ - "email" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.project": { - "name": "project", - "schema": "", - "columns": { - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - } - }, - "indexes": {}, - "foreignKeys": { - "project_userId_user_id_fk": { - "name": "project_userId_user_id_fk", - "tableFrom": "project", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.domain": { - "name": "domain", - "schema": "", - "columns": { - "domainId": { - "name": "domainId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "host": { - "name": "host", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "https": { - "name": "https", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "port": { - "name": "port", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 3000 - }, - "path": { - "name": "path", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "serviceName": { - "name": "serviceName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "domainType": { - "name": "domainType", - "type": "domainType", - "typeSchema": "public", - "primaryKey": false, - "notNull": false, - "default": "'application'" - }, - "uniqueConfigKey": { - "name": "uniqueConfigKey", - "type": "serial", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewDeploymentId": { - "name": "previewDeploymentId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - } - }, - "indexes": {}, - "foreignKeys": { - "domain_composeId_compose_composeId_fk": { - "name": "domain_composeId_compose_composeId_fk", - "tableFrom": "domain", - "tableTo": "compose", - "columnsFrom": [ - "composeId" - ], - "columnsTo": [ - "composeId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "domain_applicationId_application_applicationId_fk": { - "name": "domain_applicationId_application_applicationId_fk", - "tableFrom": "domain", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { - "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", - "tableFrom": "domain", - "tableTo": "preview_deployments", - "columnsFrom": [ - "previewDeploymentId" - ], - "columnsTo": [ - "previewDeploymentId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mariadb": { - "name": "mariadb", - "schema": "", - "columns": { - "mariadbId": { - "name": "mariadbId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "databaseName": { - "name": "databaseName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "rootPassword": { - "name": "rootPassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mariadb_projectId_project_projectId_fk": { - "name": "mariadb_projectId_project_projectId_fk", - "tableFrom": "mariadb", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mariadb_serverId_server_serverId_fk": { - "name": "mariadb_serverId_server_serverId_fk", - "tableFrom": "mariadb", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "mariadb_appName_unique": { - "name": "mariadb_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mongo": { - "name": "mongo", - "schema": "", - "columns": { - "mongoId": { - "name": "mongoId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "replicaSets": { - "name": "replicaSets", - "type": "boolean", - "primaryKey": false, - "notNull": false, - "default": false - } - }, - "indexes": {}, - "foreignKeys": { - "mongo_projectId_project_projectId_fk": { - "name": "mongo_projectId_project_projectId_fk", - "tableFrom": "mongo", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mongo_serverId_server_serverId_fk": { - "name": "mongo_serverId_server_serverId_fk", - "tableFrom": "mongo", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "mongo_appName_unique": { - "name": "mongo_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mysql": { - "name": "mysql", - "schema": "", - "columns": { - "mysqlId": { - "name": "mysqlId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "databaseName": { - "name": "databaseName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "rootPassword": { - "name": "rootPassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mysql_projectId_project_projectId_fk": { - "name": "mysql_projectId_project_projectId_fk", - "tableFrom": "mysql", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mysql_serverId_server_serverId_fk": { - "name": "mysql_serverId_server_serverId_fk", - "tableFrom": "mysql", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "mysql_appName_unique": { - "name": "mysql_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.backup": { - "name": "backup", - "schema": "", - "columns": { - "backupId": { - "name": "backupId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "schedule": { - "name": "schedule", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "enabled": { - "name": "enabled", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "database": { - "name": "database", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "prefix": { - "name": "prefix", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "destinationId": { - "name": "destinationId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseType": { - "name": "databaseType", - "type": "databaseType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "postgresId": { - "name": "postgresId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mariadbId": { - "name": "mariadbId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mysqlId": { - "name": "mysqlId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mongoId": { - "name": "mongoId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "backup_destinationId_destination_destinationId_fk": { - "name": "backup_destinationId_destination_destinationId_fk", - "tableFrom": "backup", - "tableTo": "destination", - "columnsFrom": [ - "destinationId" - ], - "columnsTo": [ - "destinationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_postgresId_postgres_postgresId_fk": { - "name": "backup_postgresId_postgres_postgresId_fk", - "tableFrom": "backup", - "tableTo": "postgres", - "columnsFrom": [ - "postgresId" - ], - "columnsTo": [ - "postgresId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_mariadbId_mariadb_mariadbId_fk": { - "name": "backup_mariadbId_mariadb_mariadbId_fk", - "tableFrom": "backup", - "tableTo": "mariadb", - "columnsFrom": [ - "mariadbId" - ], - "columnsTo": [ - "mariadbId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_mysqlId_mysql_mysqlId_fk": { - "name": "backup_mysqlId_mysql_mysqlId_fk", - "tableFrom": "backup", - "tableTo": "mysql", - "columnsFrom": [ - "mysqlId" - ], - "columnsTo": [ - "mysqlId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_mongoId_mongo_mongoId_fk": { - "name": "backup_mongoId_mongo_mongoId_fk", - "tableFrom": "backup", - "tableTo": "mongo", - "columnsFrom": [ - "mongoId" - ], - "columnsTo": [ - "mongoId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.destination": { - "name": "destination", - "schema": "", - "columns": { - "destinationId": { - "name": "destinationId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "provider": { - "name": "provider", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "accessKey": { - "name": "accessKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "secretAccessKey": { - "name": "secretAccessKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "bucket": { - "name": "bucket", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "region": { - "name": "region", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "endpoint": { - "name": "endpoint", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "destination_userId_user_id_fk": { - "name": "destination_userId_user_id_fk", - "tableFrom": "destination", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.deployment": { - "name": "deployment", - "schema": "", - "columns": { - "deploymentId": { - "name": "deploymentId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "status": { - "name": "status", - "type": "deploymentStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": false, - "default": "'running'" - }, - "logPath": { - "name": "logPath", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isPreviewDeployment": { - "name": "isPreviewDeployment", - "type": "boolean", - "primaryKey": false, - "notNull": false, - "default": false - }, - "previewDeploymentId": { - "name": "previewDeploymentId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "errorMessage": { - "name": "errorMessage", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "deployment_applicationId_application_applicationId_fk": { - "name": "deployment_applicationId_application_applicationId_fk", - "tableFrom": "deployment", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "deployment_composeId_compose_composeId_fk": { - "name": "deployment_composeId_compose_composeId_fk", - "tableFrom": "deployment", - "tableTo": "compose", - "columnsFrom": [ - "composeId" - ], - "columnsTo": [ - "composeId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "deployment_serverId_server_serverId_fk": { - "name": "deployment_serverId_server_serverId_fk", - "tableFrom": "deployment", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { - "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", - "tableFrom": "deployment", - "tableTo": "preview_deployments", - "columnsFrom": [ - "previewDeploymentId" - ], - "columnsTo": [ - "previewDeploymentId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mount": { - "name": "mount", - "schema": "", - "columns": { - "mountId": { - "name": "mountId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "type": { - "name": "type", - "type": "mountType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "hostPath": { - "name": "hostPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "volumeName": { - "name": "volumeName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "filePath": { - "name": "filePath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "content": { - "name": "content", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serviceType": { - "name": "serviceType", - "type": "serviceType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'application'" - }, - "mountPath": { - "name": "mountPath", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "postgresId": { - "name": "postgresId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mariadbId": { - "name": "mariadbId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mongoId": { - "name": "mongoId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mysqlId": { - "name": "mysqlId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "redisId": { - "name": "redisId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mount_applicationId_application_applicationId_fk": { - "name": "mount_applicationId_application_applicationId_fk", - "tableFrom": "mount", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_postgresId_postgres_postgresId_fk": { - "name": "mount_postgresId_postgres_postgresId_fk", - "tableFrom": "mount", - "tableTo": "postgres", - "columnsFrom": [ - "postgresId" - ], - "columnsTo": [ - "postgresId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_mariadbId_mariadb_mariadbId_fk": { - "name": "mount_mariadbId_mariadb_mariadbId_fk", - "tableFrom": "mount", - "tableTo": "mariadb", - "columnsFrom": [ - "mariadbId" - ], - "columnsTo": [ - "mariadbId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_mongoId_mongo_mongoId_fk": { - "name": "mount_mongoId_mongo_mongoId_fk", - "tableFrom": "mount", - "tableTo": "mongo", - "columnsFrom": [ - "mongoId" - ], - "columnsTo": [ - "mongoId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_mysqlId_mysql_mysqlId_fk": { - "name": "mount_mysqlId_mysql_mysqlId_fk", - "tableFrom": "mount", - "tableTo": "mysql", - "columnsFrom": [ - "mysqlId" - ], - "columnsTo": [ - "mysqlId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_redisId_redis_redisId_fk": { - "name": "mount_redisId_redis_redisId_fk", - "tableFrom": "mount", - "tableTo": "redis", - "columnsFrom": [ - "redisId" - ], - "columnsTo": [ - "redisId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_composeId_compose_composeId_fk": { - "name": "mount_composeId_compose_composeId_fk", - "tableFrom": "mount", - "tableTo": "compose", - "columnsFrom": [ - "composeId" - ], - "columnsTo": [ - "composeId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.certificate": { - "name": "certificate", - "schema": "", - "columns": { - "certificateId": { - "name": "certificateId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "certificateData": { - "name": "certificateData", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "privateKey": { - "name": "privateKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "certificatePath": { - "name": "certificatePath", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "autoRenew": { - "name": "autoRenew", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "certificate_userId_user_id_fk": { - "name": "certificate_userId_user_id_fk", - "tableFrom": "certificate", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "certificate_serverId_server_serverId_fk": { - "name": "certificate_serverId_server_serverId_fk", - "tableFrom": "certificate", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "certificate_certificatePath_unique": { - "name": "certificate_certificatePath_unique", - "nullsNotDistinct": false, - "columns": [ - "certificatePath" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.session": { - "name": "session", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "ip_address": { - "name": "ip_address", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "user_agent": { - "name": "user_agent", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "impersonated_by": { - "name": "impersonated_by", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "session_user_id_user_id_fk": { - "name": "session_user_id_user_id_fk", - "tableFrom": "session", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "session_token_unique": { - "name": "session_token_unique", - "nullsNotDistinct": false, - "columns": [ - "token" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.redirect": { - "name": "redirect", - "schema": "", - "columns": { - "redirectId": { - "name": "redirectId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "regex": { - "name": "regex", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "replacement": { - "name": "replacement", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "permanent": { - "name": "permanent", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "uniqueConfigKey": { - "name": "uniqueConfigKey", - "type": "serial", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "redirect_applicationId_application_applicationId_fk": { - "name": "redirect_applicationId_application_applicationId_fk", - "tableFrom": "redirect", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.security": { - "name": "security", - "schema": "", - "columns": { - "securityId": { - "name": "securityId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "security_applicationId_application_applicationId_fk": { - "name": "security_applicationId_application_applicationId_fk", - "tableFrom": "security", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "security_username_applicationId_unique": { - "name": "security_username_applicationId_unique", - "nullsNotDistinct": false, - "columns": [ - "username", - "applicationId" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.port": { - "name": "port", - "schema": "", - "columns": { - "portId": { - "name": "portId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "publishedPort": { - "name": "publishedPort", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "targetPort": { - "name": "targetPort", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "protocol": { - "name": "protocol", - "type": "protocolType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "port_applicationId_application_applicationId_fk": { - "name": "port_applicationId_application_applicationId_fk", - "tableFrom": "port", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.redis": { - "name": "redis", - "schema": "", - "columns": { - "redisId": { - "name": "redisId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "redis_projectId_project_projectId_fk": { - "name": "redis_projectId_project_projectId_fk", - "tableFrom": "redis", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "redis_serverId_server_serverId_fk": { - "name": "redis_serverId_server_serverId_fk", - "tableFrom": "redis", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "redis_appName_unique": { - "name": "redis_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.compose": { - "name": "compose", - "schema": "", - "columns": { - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "composeFile": { - "name": "composeFile", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "refreshToken": { - "name": "refreshToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sourceType": { - "name": "sourceType", - "type": "sourceTypeCompose", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'github'" - }, - "composeType": { - "name": "composeType", - "type": "composeType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'docker-compose'" - }, - "repository": { - "name": "repository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "owner": { - "name": "owner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "branch": { - "name": "branch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "autoDeploy": { - "name": "autoDeploy", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "gitlabProjectId": { - "name": "gitlabProjectId", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "gitlabRepository": { - "name": "gitlabRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabOwner": { - "name": "gitlabOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabBranch": { - "name": "gitlabBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabPathNamespace": { - "name": "gitlabPathNamespace", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketRepository": { - "name": "bitbucketRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketOwner": { - "name": "bitbucketOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketBranch": { - "name": "bitbucketBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitUrl": { - "name": "customGitUrl", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitBranch": { - "name": "customGitBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitSSHKeyId": { - "name": "customGitSSHKeyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "composePath": { - "name": "composePath", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'./docker-compose.yml'" - }, - "suffix": { - "name": "suffix", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "randomize": { - "name": "randomize", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "isolatedDeployment": { - "name": "isolatedDeployment", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "composeStatus": { - "name": "composeStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "githubId": { - "name": "githubId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabId": { - "name": "gitlabId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketId": { - "name": "bitbucketId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { - "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", - "tableFrom": "compose", - "tableTo": "ssh-key", - "columnsFrom": [ - "customGitSSHKeyId" - ], - "columnsTo": [ - "sshKeyId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_projectId_project_projectId_fk": { - "name": "compose_projectId_project_projectId_fk", - "tableFrom": "compose", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "compose_githubId_github_githubId_fk": { - "name": "compose_githubId_github_githubId_fk", - "tableFrom": "compose", - "tableTo": "github", - "columnsFrom": [ - "githubId" - ], - "columnsTo": [ - "githubId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_gitlabId_gitlab_gitlabId_fk": { - "name": "compose_gitlabId_gitlab_gitlabId_fk", - "tableFrom": "compose", - "tableTo": "gitlab", - "columnsFrom": [ - "gitlabId" - ], - "columnsTo": [ - "gitlabId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_bitbucketId_bitbucket_bitbucketId_fk": { - "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", - "tableFrom": "compose", - "tableTo": "bitbucket", - "columnsFrom": [ - "bitbucketId" - ], - "columnsTo": [ - "bitbucketId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_serverId_server_serverId_fk": { - "name": "compose_serverId_server_serverId_fk", - "tableFrom": "compose", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.registry": { - "name": "registry", - "schema": "", - "columns": { - "registryId": { - "name": "registryId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "registryName": { - "name": "registryName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "imagePrefix": { - "name": "imagePrefix", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "registryUrl": { - "name": "registryUrl", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "selfHosted": { - "name": "selfHosted", - "type": "RegistryType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'cloud'" - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "registry_userId_user_id_fk": { - "name": "registry_userId_user_id_fk", - "tableFrom": "registry", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.discord": { - "name": "discord", - "schema": "", - "columns": { - "discordId": { - "name": "discordId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "webhookUrl": { - "name": "webhookUrl", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "decoration": { - "name": "decoration", - "type": "boolean", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.email": { - "name": "email", - "schema": "", - "columns": { - "emailId": { - "name": "emailId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "smtpServer": { - "name": "smtpServer", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "smtpPort": { - "name": "smtpPort", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "fromAddress": { - "name": "fromAddress", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "toAddress": { - "name": "toAddress", - "type": "text[]", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.gotify": { - "name": "gotify", - "schema": "", - "columns": { - "gotifyId": { - "name": "gotifyId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "serverUrl": { - "name": "serverUrl", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appToken": { - "name": "appToken", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "priority": { - "name": "priority", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 5 - }, - "decoration": { - "name": "decoration", - "type": "boolean", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.notification": { - "name": "notification", - "schema": "", - "columns": { - "notificationId": { - "name": "notificationId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appDeploy": { - "name": "appDeploy", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "appBuildError": { - "name": "appBuildError", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "databaseBackup": { - "name": "databaseBackup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "dokployRestart": { - "name": "dokployRestart", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "dockerCleanup": { - "name": "dockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "serverThreshold": { - "name": "serverThreshold", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "notificationType": { - "name": "notificationType", - "type": "notificationType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "slackId": { - "name": "slackId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "telegramId": { - "name": "telegramId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "discordId": { - "name": "discordId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "emailId": { - "name": "emailId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gotifyId": { - "name": "gotifyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "notification_slackId_slack_slackId_fk": { - "name": "notification_slackId_slack_slackId_fk", - "tableFrom": "notification", - "tableTo": "slack", - "columnsFrom": [ - "slackId" - ], - "columnsTo": [ - "slackId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_telegramId_telegram_telegramId_fk": { - "name": "notification_telegramId_telegram_telegramId_fk", - "tableFrom": "notification", - "tableTo": "telegram", - "columnsFrom": [ - "telegramId" - ], - "columnsTo": [ - "telegramId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_discordId_discord_discordId_fk": { - "name": "notification_discordId_discord_discordId_fk", - "tableFrom": "notification", - "tableTo": "discord", - "columnsFrom": [ - "discordId" - ], - "columnsTo": [ - "discordId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_emailId_email_emailId_fk": { - "name": "notification_emailId_email_emailId_fk", - "tableFrom": "notification", - "tableTo": "email", - "columnsFrom": [ - "emailId" - ], - "columnsTo": [ - "emailId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_gotifyId_gotify_gotifyId_fk": { - "name": "notification_gotifyId_gotify_gotifyId_fk", - "tableFrom": "notification", - "tableTo": "gotify", - "columnsFrom": [ - "gotifyId" - ], - "columnsTo": [ - "gotifyId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_userId_user_id_fk": { - "name": "notification_userId_user_id_fk", - "tableFrom": "notification", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.slack": { - "name": "slack", - "schema": "", - "columns": { - "slackId": { - "name": "slackId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "webhookUrl": { - "name": "webhookUrl", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "channel": { - "name": "channel", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.telegram": { - "name": "telegram", - "schema": "", - "columns": { - "telegramId": { - "name": "telegramId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "botToken": { - "name": "botToken", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "chatId": { - "name": "chatId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.ssh-key": { - "name": "ssh-key", - "schema": "", - "columns": { - "sshKeyId": { - "name": "sshKeyId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "privateKey": { - "name": "privateKey", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "publicKey": { - "name": "publicKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "lastUsedAt": { - "name": "lastUsedAt", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "ssh-key_userId_user_id_fk": { - "name": "ssh-key_userId_user_id_fk", - "tableFrom": "ssh-key", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.git_provider": { - "name": "git_provider", - "schema": "", - "columns": { - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "providerType": { - "name": "providerType", - "type": "gitProviderType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'github'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "git_provider_userId_user_id_fk": { - "name": "git_provider_userId_user_id_fk", - "tableFrom": "git_provider", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.bitbucket": { - "name": "bitbucket", - "schema": "", - "columns": { - "bitbucketId": { - "name": "bitbucketId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "bitbucketUsername": { - "name": "bitbucketUsername", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "appPassword": { - "name": "appPassword", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketWorkspaceName": { - "name": "bitbucketWorkspaceName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { - "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", - "tableFrom": "bitbucket", - "tableTo": "git_provider", - "columnsFrom": [ - "gitProviderId" - ], - "columnsTo": [ - "gitProviderId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.github": { - "name": "github", - "schema": "", - "columns": { - "githubId": { - "name": "githubId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "githubAppName": { - "name": "githubAppName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubAppId": { - "name": "githubAppId", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "githubClientId": { - "name": "githubClientId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubClientSecret": { - "name": "githubClientSecret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubInstallationId": { - "name": "githubInstallationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubPrivateKey": { - "name": "githubPrivateKey", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubWebhookSecret": { - "name": "githubWebhookSecret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "github_gitProviderId_git_provider_gitProviderId_fk": { - "name": "github_gitProviderId_git_provider_gitProviderId_fk", - "tableFrom": "github", - "tableTo": "git_provider", - "columnsFrom": [ - "gitProviderId" - ], - "columnsTo": [ - "gitProviderId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.gitlab": { - "name": "gitlab", - "schema": "", - "columns": { - "gitlabId": { - "name": "gitlabId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "gitlabUrl": { - "name": "gitlabUrl", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'https://gitlab.com'" - }, - "application_id": { - "name": "application_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "redirect_uri": { - "name": "redirect_uri", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "secret": { - "name": "secret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "group_name": { - "name": "group_name", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "gitlab_gitProviderId_git_provider_gitProviderId_fk": { - "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", - "tableFrom": "gitlab", - "tableTo": "git_provider", - "columnsFrom": [ - "gitProviderId" - ], - "columnsTo": [ - "gitProviderId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.server": { - "name": "server", - "schema": "", - "columns": { - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "ipAddress": { - "name": "ipAddress", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "port": { - "name": "port", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'root'" - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "enableDockerCleanup": { - "name": "enableDockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverStatus": { - "name": "serverStatus", - "type": "serverStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'active'" - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "sshKeyId": { - "name": "sshKeyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "metricsConfig": { - "name": "metricsConfig", - "type": "jsonb", - "primaryKey": false, - "notNull": true, - "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" - } - }, - "indexes": {}, - "foreignKeys": { - "server_userId_user_id_fk": { - "name": "server_userId_user_id_fk", - "tableFrom": "server", - "tableTo": "user", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "server_sshKeyId_ssh-key_sshKeyId_fk": { - "name": "server_sshKeyId_ssh-key_sshKeyId_fk", - "tableFrom": "server", - "tableTo": "ssh-key", - "columnsFrom": [ - "sshKeyId" - ], - "columnsTo": [ - "sshKeyId" - ], - "onDelete": "set null", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.preview_deployments": { - "name": "preview_deployments", - "schema": "", - "columns": { - "previewDeploymentId": { - "name": "previewDeploymentId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "branch": { - "name": "branch", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestId": { - "name": "pullRequestId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestNumber": { - "name": "pullRequestNumber", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestURL": { - "name": "pullRequestURL", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestTitle": { - "name": "pullRequestTitle", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestCommentId": { - "name": "pullRequestCommentId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "previewStatus": { - "name": "previewStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "domainId": { - "name": "domainId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expiresAt": { - "name": "expiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "preview_deployments_applicationId_application_applicationId_fk": { - "name": "preview_deployments_applicationId_application_applicationId_fk", - "tableFrom": "preview_deployments", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "preview_deployments_domainId_domain_domainId_fk": { - "name": "preview_deployments_domainId_domain_domainId_fk", - "tableFrom": "preview_deployments", - "tableTo": "domain", - "columnsFrom": [ - "domainId" - ], - "columnsTo": [ - "domainId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "preview_deployments_appName_unique": { - "name": "preview_deployments_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.account": { - "name": "account", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "account_id": { - "name": "account_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "provider_id": { - "name": "provider_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "id_token": { - "name": "id_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "access_token_expires_at": { - "name": "access_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "refresh_token_expires_at": { - "name": "refresh_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "scope": { - "name": "scope", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "is2FAEnabled": { - "name": "is2FAEnabled", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "resetPasswordToken": { - "name": "resetPasswordToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "resetPasswordExpiresAt": { - "name": "resetPasswordExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationToken": { - "name": "confirmationToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationExpiresAt": { - "name": "confirmationExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "account_user_id_user_id_fk": { - "name": "account_user_id_user_id_fk", - "tableFrom": "account", - "tableTo": "user", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.verification": { - "name": "verification", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "identifier": { - "name": "identifier", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "value": { - "name": "value", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": { - "public.buildType": { - "name": "buildType", - "schema": "public", - "values": [ - "dockerfile", - "heroku_buildpacks", - "paketo_buildpacks", - "nixpacks", - "static" - ] - }, - "public.sourceType": { - "name": "sourceType", - "schema": "public", - "values": [ - "docker", - "git", - "github", - "gitlab", - "bitbucket", - "drop" - ] - }, - "public.Roles": { - "name": "Roles", - "schema": "public", - "values": [ - "admin", - "user" - ] - }, - "public.domainType": { - "name": "domainType", - "schema": "public", - "values": [ - "compose", - "application", - "preview" - ] - }, - "public.databaseType": { - "name": "databaseType", - "schema": "public", - "values": [ - "postgres", - "mariadb", - "mysql", - "mongo" - ] - }, - "public.deploymentStatus": { - "name": "deploymentStatus", - "schema": "public", - "values": [ - "running", - "done", - "error" - ] - }, - "public.mountType": { - "name": "mountType", - "schema": "public", - "values": [ - "bind", - "volume", - "file" - ] - }, - "public.serviceType": { - "name": "serviceType", - "schema": "public", - "values": [ - "application", - "postgres", - "mysql", - "mariadb", - "mongo", - "redis", - "compose" - ] - }, - "public.protocolType": { - "name": "protocolType", - "schema": "public", - "values": [ - "tcp", - "udp" - ] - }, - "public.applicationStatus": { - "name": "applicationStatus", - "schema": "public", - "values": [ - "idle", - "running", - "done", - "error" - ] - }, - "public.certificateType": { - "name": "certificateType", - "schema": "public", - "values": [ - "letsencrypt", - "none" - ] - }, - "public.composeType": { - "name": "composeType", - "schema": "public", - "values": [ - "docker-compose", - "stack" - ] - }, - "public.sourceTypeCompose": { - "name": "sourceTypeCompose", - "schema": "public", - "values": [ - "git", - "github", - "gitlab", - "bitbucket", - "raw" - ] - }, - "public.RegistryType": { - "name": "RegistryType", - "schema": "public", - "values": [ - "selfHosted", - "cloud" - ] - }, - "public.notificationType": { - "name": "notificationType", - "schema": "public", - "values": [ - "slack", - "telegram", - "discord", - "email", - "gotify" - ] - }, - "public.gitProviderType": { - "name": "gitProviderType", - "schema": "public", - "values": [ - "github", - "gitlab", - "bitbucket" - ] - }, - "public.serverStatus": { - "name": "serverStatus", - "schema": "public", - "values": [ - "active", - "inactive" - ] - } - }, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 8d09d2090..bd626bf9d 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -463,13 +463,7 @@ "when": 1739087857244, "tag": "0065_daily_zaladane", "breakpoints": true - }, - { - "idx": 66, - "version": "7", - "when": 1739142819089, - "tag": "0066_broad_marrow", - "breakpoints": true } + ] } \ No newline at end of file diff --git a/apps/dokploy/drizzle/0066_broad_marrow.sql b/apps/dokploy/drizzle/old_migration similarity index 100% rename from apps/dokploy/drizzle/0066_broad_marrow.sql rename to apps/dokploy/drizzle/old_migration From b7112b89fdc85a7f2b06ef475aabd1fd0430e8e6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 10 Feb 2025 00:39:46 -0600 Subject: [PATCH 006/126] refactor: add migration --- apps/dokploy/drizzle/0066_calm_vengeance.sql | 367 ++ apps/dokploy/drizzle/meta/0066_snapshot.json | 4940 ++++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 8 +- packages/server/auth-schema.ts | 3 + packages/server/package.json | 1 + packages/server/src/db/schema/account.ts | 3 + packages/server/src/db/schema/dbml.ts | 7 + packages/server/src/db/schema/schema.dbml | 872 ++++ packages/server/src/db/schema/user.ts | 26 +- pnpm-lock.yaml | 12 + 10 files changed, 6227 insertions(+), 12 deletions(-) create mode 100644 apps/dokploy/drizzle/0066_calm_vengeance.sql create mode 100644 apps/dokploy/drizzle/meta/0066_snapshot.json create mode 100644 packages/server/src/db/schema/dbml.ts create mode 100644 packages/server/src/db/schema/schema.dbml diff --git a/apps/dokploy/drizzle/0066_calm_vengeance.sql b/apps/dokploy/drizzle/0066_calm_vengeance.sql new file mode 100644 index 000000000..3901740b3 --- /dev/null +++ b/apps/dokploy/drizzle/0066_calm_vengeance.sql @@ -0,0 +1,367 @@ +-- Primero ejecutar todas las modificaciones estructurales +ALTER TABLE "user" RENAME COLUMN "userId" TO "id"; +--> statement-breakpoint +ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "ssh-key" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId"; +--> statement-breakpoint +ALTER TABLE "user" DROP CONSTRAINT "user_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "user" DROP CONSTRAINT "user_authId_auth_id_fk"; +--> statement-breakpoint +ALTER TABLE "admin" DROP CONSTRAINT "admin_authId_auth_id_fk"; +--> statement-breakpoint +ALTER TABLE "project" DROP CONSTRAINT "project_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "destination" DROP CONSTRAINT "destination_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "certificate" DROP CONSTRAINT "certificate_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "session" DROP CONSTRAINT "session_user_id_auth_id_fk"; +--> statement-breakpoint +ALTER TABLE "registry" DROP CONSTRAINT "registry_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "notification" DROP CONSTRAINT "notification_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "ssh-key" DROP CONSTRAINT "ssh-key_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "server" DROP CONSTRAINT "server_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "user" ALTER COLUMN "expirationDate" SET DATA TYPE text; +--> statement-breakpoint +ALTER TABLE "session" ALTER COLUMN "expires_at" SET DATA TYPE timestamp; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "name" text DEFAULT '' NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "email" text NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "email_verified" boolean NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "image" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "role" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "banned" boolean; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "ban_reason" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "updated_at" timestamp NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "serverIp" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "certificateType" "certificateType" DEFAULT 'none' NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "host" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "letsEncryptEmail" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "sshPrivateKey" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "enableDockerCleanup" boolean DEFAULT false NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "enableLogRotation" boolean DEFAULT false NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "enablePaidFeatures" boolean DEFAULT false NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "cleanupCacheApplications" boolean DEFAULT false NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "cleanupCacheOnCompose" boolean DEFAULT false NOT NULL; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "stripeCustomerId" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "stripeSubscriptionId" text; +--> statement-breakpoint +ALTER TABLE "user" ADD COLUMN "serversQuantity" integer DEFAULT 0 NOT NULL; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "token" text NOT NULL; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "created_at" timestamp NOT NULL; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "updated_at" timestamp NOT NULL; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "ip_address" text; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "user_agent" text; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "impersonated_by" text; +--> statement-breakpoint +ALTER TABLE "session" ADD COLUMN "active_organization_id" text; +--> statement-breakpoint +CREATE TABLE "account" ( + "id" text PRIMARY KEY NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamp, + "refresh_token_expires_at" timestamp, + "scope" text, + "password" text, + "is2FAEnabled" boolean DEFAULT false NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "resetPasswordToken" text, + "resetPasswordExpiresAt" text, + "confirmationToken" text, + "confirmationExpiresAt" text +); +--> statement-breakpoint +CREATE TABLE "invitation" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "email" text NOT NULL, + "role" text, + "status" text NOT NULL, + "expires_at" timestamp NOT NULL, + "inviter_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "member" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "user_id" text NOT NULL, + "role" text NOT NULL, + "created_at" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE "organization" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "slug" text, + "logo" text, + "created_at" timestamp NOT NULL, + "metadata" text, + "owner_id" text NOT NULL, + CONSTRAINT "organization_slug_unique" UNIQUE("slug") +); +--> statement-breakpoint +CREATE TABLE "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp, + "updated_at" timestamp +); +--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; +--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "adminId"; +--> statement-breakpoint +ALTER TABLE "user" DROP COLUMN "authId"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "adminId"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "serverIp"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "certificateType"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "host"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "letsEncryptEmail"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "sshPrivateKey"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "enableDockerCleanup"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "enableLogRotation"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "authId"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "createdAt"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "stripeCustomerId"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "stripeSubscriptionId"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "serversQuantity"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "enablePaidFeatures"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "metricsConfig"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "cleanupCacheApplications"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "cleanupCacheOnPreviews"; +--> statement-breakpoint +ALTER TABLE "admin" DROP COLUMN "cleanupCacheOnCompose"; +--> statement-breakpoint +ALTER TABLE "user" ADD CONSTRAINT "user_email_unique" UNIQUE("email"); +--> statement-breakpoint + +-- Primero quitar NOT NULL temporalmente +ALTER TABLE "session" ALTER COLUMN "token" DROP NOT NULL; +--> statement-breakpoint + +-- Actualizar tokens existentes +UPDATE "session" SET + token = gen_random_uuid(), + created_at = COALESCE(created_at, NOW() - interval '1 day'), + updated_at = NOW() +WHERE token IS NULL; + +-- Restablecer restricciones +ALTER TABLE "session" ALTER COLUMN "token" SET NOT NULL; +ALTER TABLE "session" ADD CONSTRAINT "session_token_unique" UNIQUE (token); + +-- Luego realizar la migración de datos +-- Migración de datos para Admins +WITH admin_users AS ( + INSERT INTO "user" ( + id, created_at, token, email, email_verified, role, updated_at, + certificate_type, server_ip, host, lets_encrypt_email, ssh_private_key, + enable_docker_cleanup, enable_log_rotation, enable_paid_features, + metrics_config, cleanup_cache_applications, cleanup_cache_on_previews, + cleanup_cache_on_compose, stripe_customer_id, stripe_subscription_id, + servers_quantity + ) + SELECT + gen_random_uuid(), + a.created_at, + a.token, + a.email, + true, + 'admin', + a.created_at, + ad.certificate_type, + ad.server_ip, + ad.host, + ad.lets_encrypt_email, + ad.ssh_private_key, + ad.enable_docker_cleanup, + ad.enable_log_rotation, + ad.enable_paid_features, + ad.metrics_config, + ad.cleanup_cache_applications, + ad.cleanup_cache_on_previews, + ad.cleanup_cache_on_compose, + ad.stripe_customer_id, + ad.stripe_subscription_id, + ad.servers_quantity + FROM auth a + JOIN admins ad ON a.id = ad.auth_id + RETURNING id AS user_id, created_at, email +) +INSERT INTO account (id, account_id, provider_id, user_id, password, created_at, updated_at) +SELECT + gen_random_uuid(), + a.id, + 'credentials', + au.user_id, + a.password, + au.created_at, + au.created_at +FROM auth a +JOIN admin_users au ON a.email = au.email; + +-- Crear organizaciones para admins +WITH admin_orgs AS ( + INSERT INTO organization (id, name, slug, created_at, owner_id) + SELECT + gen_random_uuid(), + 'My Organization', + 'org/' || au.user_id, + au.created_at, + au.user_id + FROM admin_users au + RETURNING id AS org_id, owner_id +) +-- Migrar usuarios regulares y asociar a organizaciones +INSERT INTO "user" ( + id, created_at, token, email, email_verified, role, updated_at, + can_create_projects, can_access_ssh_keys +) +SELECT + gen_random_uuid(), + a.created_at, + a.token, + a.email, + true, + 'user', + a.created_at, + u.can_create_projects, + u.can_access_ssh_keys +FROM auth a +JOIN users u ON a.id = u.auth_id +WHERE a.role = 'user'; + +-- Crear accounts para usuarios +INSERT INTO account (id, account_id, provider_id, user_id, password, created_at, updated_at) +SELECT + gen_random_uuid(), + a.id, + 'credentials', + u.id, + a.password, + a.created_at, + a.created_at +FROM auth a +JOIN "user" u ON a.email = u.email; + +-- Asociar usuarios a organizaciones de sus admins +INSERT INTO member (id, organization_id, user_id, role, created_at) +SELECT + gen_random_uuid(), + ao.org_id, + u.id, + 'user', + u.created_at +FROM "user" u +JOIN users old_u ON u.email = old_u.email +JOIN auth a ON old_u.auth_id = a.id +JOIN admin_orgs ao ON ao.owner_id = a.id; + +-- Eliminar tablas obsoletas +DROP TABLE IF EXISTS admins; +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS auth; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json new file mode 100644 index 000000000..83cdfe7e3 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -0,0 +1,4940 @@ +{ + "id": "5963096c-1a97-4e26-9f3f-f8613a359596", + "prevId": "1240ec96-1751-4de3-b64f-cef9cb716786", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": {}, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_id_fk": { + "name": "project_userId_user_id_fk", + "tableFrom": "project", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_id_fk": { + "name": "destination_userId_user_id_fk", + "tableFrom": "destination", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_id_fk": { + "name": "certificate_userId_user_id_fk", + "tableFrom": "certificate", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_id_fk": { + "name": "registry_userId_user_id_fk", + "tableFrom": "registry", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_userId_user_id_fk": { + "name": "notification_userId_user_id_fk", + "tableFrom": "notification", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_id_fk": { + "name": "ssh-key_userId_user_id_fk", + "tableFrom": "ssh-key", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_id_fk": { + "name": "git_provider_userId_user_id_fk", + "tableFrom": "git_provider", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_id_fk": { + "name": "server_userId_user_id_fk", + "tableFrom": "server", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_id_fk": { + "name": "organization_owner_id_user_id_fk", + "tableFrom": "organization", + "tableTo": "user", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index bd626bf9d..8c06b6375 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -463,7 +463,13 @@ "when": 1739087857244, "tag": "0065_daily_zaladane", "breakpoints": true + }, + { + "idx": 66, + "version": "7", + "when": 1739168611366, + "tag": "0066_calm_vengeance", + "breakpoints": true } - ] } \ No newline at end of file diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 10e9965b5..8b09e3366 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -64,6 +64,9 @@ export const organization = pgTable("organization", { logo: text("logo"), createdAt: timestamp("created_at").notNull(), metadata: text("metadata"), + ownerId: text("owner_id") + .notNull() + .references(() => user.id), }); export const member = pgTable("member", { diff --git a/packages/server/package.json b/packages/server/package.json index 228344210..a8cf97c66 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,6 +28,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "drizzle-dbml-generator":"0.10.0", "better-auth":"1.1.16", "rotating-file-stream": "3.2.3", "@faker-js/faker": "^8.4.1", diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 38c630275..ce963a0d3 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -40,6 +40,9 @@ export const organization = pgTable("organization", { logo: text("logo"), createdAt: timestamp("created_at").notNull(), metadata: text("metadata"), + ownerId: text("owner_id") + .notNull() + .references(() => user.id), }); export const member = pgTable("member", { diff --git a/packages/server/src/db/schema/dbml.ts b/packages/server/src/db/schema/dbml.ts new file mode 100644 index 000000000..72a758146 --- /dev/null +++ b/packages/server/src/db/schema/dbml.ts @@ -0,0 +1,7 @@ +import { pgGenerate } from "drizzle-dbml-generator"; // Using Postgres for this example +import * as schema from "./index"; + +const out = "./schema.dbml"; +const relational = true; + +pgGenerate({ schema, out, relational }); diff --git a/packages/server/src/db/schema/schema.dbml b/packages/server/src/db/schema/schema.dbml new file mode 100644 index 000000000..ce2b5abca --- /dev/null +++ b/packages/server/src/db/schema/schema.dbml @@ -0,0 +1,872 @@ +enum applicationStatus { + idle + running + done + error +} + +enum buildType { + dockerfile + heroku_buildpacks + paketo_buildpacks + nixpacks + static +} + +enum certificateType { + letsencrypt + none +} + +enum composeType { + "docker-compose" + stack +} + +enum databaseType { + postgres + mariadb + mysql + mongo +} + +enum deploymentStatus { + running + done + error +} + +enum domainType { + compose + application + preview +} + +enum gitProviderType { + github + gitlab + bitbucket +} + +enum mountType { + bind + volume + file +} + +enum notificationType { + slack + telegram + discord + email + gotify +} + +enum protocolType { + tcp + udp +} + +enum RegistryType { + selfHosted + cloud +} + +enum Roles { + admin + user +} + +enum serverStatus { + active + inactive +} + +enum serviceType { + application + postgres + mysql + mariadb + mongo + redis + compose +} + +enum sourceType { + docker + git + github + gitlab + bitbucket + drop +} + +enum sourceTypeCompose { + git + github + gitlab + bitbucket + raw +} + +table account { + id text [pk, not null] + account_id text [not null] + provider_id text [not null] + user_id text [not null] + access_token text + refresh_token text + id_token text + access_token_expires_at timestamp + refresh_token_expires_at timestamp + scope text + password text + is2FAEnabled boolean [not null, default: false] + created_at timestamp [not null] + updated_at timestamp [not null] + resetPasswordToken text + resetPasswordExpiresAt text + confirmationToken text + confirmationExpiresAt text +} + +table admin { +} + +table application { + applicationId text [pk, not null] + name text [not null] + appName text [not null, unique] + description text + env text + previewEnv text + previewBuildArgs text + previewWildcard text + previewPort integer [default: 3000] + previewHttps boolean [not null, default: false] + previewPath text [default: '/'] + certificateType certificateType [not null, default: 'none'] + previewLimit integer [default: 3] + isPreviewDeploymentsActive boolean [default: false] + buildArgs text + memoryReservation text + memoryLimit text + cpuReservation text + cpuLimit text + title text + enabled boolean + subtitle text + command text + refreshToken text + sourceType sourceType [not null, default: 'github'] + repository text + owner text + branch text + buildPath text [default: '/'] + autoDeploy boolean + gitlabProjectId integer + gitlabRepository text + gitlabOwner text + gitlabBranch text + gitlabBuildPath text [default: '/'] + gitlabPathNamespace text + bitbucketRepository text + bitbucketOwner text + bitbucketBranch text + bitbucketBuildPath text [default: '/'] + username text + password text + dockerImage text + registryUrl text + customGitUrl text + customGitBranch text + customGitBuildPath text + customGitSSHKeyId text + dockerfile text + dockerContextPath text + dockerBuildStage text + dropBuildPath text + healthCheckSwarm json + restartPolicySwarm json + placementSwarm json + updateConfigSwarm json + rollbackConfigSwarm json + modeSwarm json + labelsSwarm json + networkSwarm json + replicas integer [not null, default: 1] + applicationStatus applicationStatus [not null, default: 'idle'] + buildType buildType [not null, default: 'nixpacks'] + herokuVersion text [default: '24'] + publishDirectory text + createdAt text [not null] + registryId text + projectId text [not null] + githubId text + gitlabId text + bitbucketId text + serverId text +} + +table auth { + id text [pk, not null] + email text [not null, unique] + password text [not null] + rol Roles [not null] + image text + secret text + token text + is2FAEnabled boolean [not null, default: false] + createdAt text [not null] + resetPasswordToken text + resetPasswordExpiresAt text + confirmationToken text + confirmationExpiresAt text +} + +table backup { + backupId text [pk, not null] + schedule text [not null] + enabled boolean + database text [not null] + prefix text [not null] + destinationId text [not null] + databaseType databaseType [not null] + postgresId text + mariadbId text + mysqlId text + mongoId text +} + +table bitbucket { + bitbucketId text [pk, not null] + bitbucketUsername text + appPassword text + bitbucketWorkspaceName text + gitProviderId text [not null] +} + +table certificate { + certificateId text [pk, not null] + name text [not null] + certificateData text [not null] + privateKey text [not null] + certificatePath text [not null, unique] + autoRenew boolean + userId text + serverId text +} + +table compose { + composeId text [pk, not null] + name text [not null] + appName text [not null] + description text + env text + composeFile text [not null, default: ''] + refreshToken text + sourceType sourceTypeCompose [not null, default: 'github'] + composeType composeType [not null, default: 'docker-compose'] + repository text + owner text + branch text + autoDeploy boolean + gitlabProjectId integer + gitlabRepository text + gitlabOwner text + gitlabBranch text + gitlabPathNamespace text + bitbucketRepository text + bitbucketOwner text + bitbucketBranch text + customGitUrl text + customGitBranch text + customGitSSHKeyId text + command text [not null, default: ''] + composePath text [not null, default: './docker-compose.yml'] + suffix text [not null, default: ''] + randomize boolean [not null, default: false] + isolatedDeployment boolean [not null, default: false] + composeStatus applicationStatus [not null, default: 'idle'] + projectId text [not null] + createdAt text [not null] + githubId text + gitlabId text + bitbucketId text + serverId text +} + +table deployment { + deploymentId text [pk, not null] + title text [not null] + description text + status deploymentStatus [default: 'running'] + logPath text [not null] + applicationId text + composeId text + serverId text + isPreviewDeployment boolean [default: false] + previewDeploymentId text + createdAt text [not null] + errorMessage text +} + +table destination { + destinationId text [pk, not null] + name text [not null] + provider text + accessKey text [not null] + secretAccessKey text [not null] + bucket text [not null] + region text [not null] + endpoint text [not null] + userId text [not null] +} + +table discord { + discordId text [pk, not null] + webhookUrl text [not null] + decoration boolean +} + +table domain { + domainId text [pk, not null] + host text [not null] + https boolean [not null, default: false] + port integer [default: 3000] + path text [default: '/'] + serviceName text + domainType domainType [default: 'application'] + uniqueConfigKey serial [not null, increment] + createdAt text [not null] + composeId text + applicationId text + previewDeploymentId text + certificateType certificateType [not null, default: 'none'] +} + +table email { + emailId text [pk, not null] + smtpServer text [not null] + smtpPort integer [not null] + username text [not null] + password text [not null] + fromAddress text [not null] + toAddress text[] [not null] +} + +table git_provider { + gitProviderId text [pk, not null] + name text [not null] + providerType gitProviderType [not null, default: 'github'] + createdAt text [not null] + userId text +} + +table github { + githubId text [pk, not null] + githubAppName text + githubAppId integer + githubClientId text + githubClientSecret text + githubInstallationId text + githubPrivateKey text + githubWebhookSecret text + gitProviderId text [not null] +} + +table gitlab { + gitlabId text [pk, not null] + gitlabUrl text [not null, default: 'https://gitlab.com'] + application_id text + redirect_uri text + secret text + access_token text + refresh_token text + group_name text + expires_at integer + gitProviderId text [not null] +} + +table gotify { + gotifyId text [pk, not null] + serverUrl text [not null] + appToken text [not null] + priority integer [not null, default: 5] + decoration boolean +} + +table invitation { + id text [pk, not null] + organization_id text [not null] + email text [not null] + role text + status text [not null] + expires_at timestamp [not null] + inviter_id text [not null] +} + +table mariadb { + mariadbId text [pk, not null] + name text [not null] + appName text [not null, unique] + description text + databaseName text [not null] + databaseUser text [not null] + databasePassword text [not null] + rootPassword text [not null] + dockerImage text [not null] + command text + env text + memoryReservation text + memoryLimit text + cpuReservation text + cpuLimit text + externalPort integer + applicationStatus applicationStatus [not null, default: 'idle'] + createdAt text [not null] + projectId text [not null] + serverId text +} + +table member { + id text [pk, not null] + organization_id text [not null] + user_id text [not null] + role text [not null] + created_at timestamp [not null] +} + +table mongo { + mongoId text [pk, not null] + name text [not null] + appName text [not null, unique] + description text + databaseUser text [not null] + databasePassword text [not null] + dockerImage text [not null] + command text + env text + memoryReservation text + memoryLimit text + cpuReservation text + cpuLimit text + externalPort integer + applicationStatus applicationStatus [not null, default: 'idle'] + createdAt text [not null] + projectId text [not null] + serverId text + replicaSets boolean [default: false] +} + +table mount { + mountId text [pk, not null] + type mountType [not null] + hostPath text + volumeName text + filePath text + content text + serviceType serviceType [not null, default: 'application'] + mountPath text [not null] + applicationId text + postgresId text + mariadbId text + mongoId text + mysqlId text + redisId text + composeId text +} + +table mysql { + mysqlId text [pk, not null] + name text [not null] + appName text [not null, unique] + description text + databaseName text [not null] + databaseUser text [not null] + databasePassword text [not null] + rootPassword text [not null] + dockerImage text [not null] + command text + env text + memoryReservation text + memoryLimit text + cpuReservation text + cpuLimit text + externalPort integer + applicationStatus applicationStatus [not null, default: 'idle'] + createdAt text [not null] + projectId text [not null] + serverId text +} + +table notification { + notificationId text [pk, not null] + name text [not null] + appDeploy boolean [not null, default: false] + appBuildError boolean [not null, default: false] + databaseBackup boolean [not null, default: false] + dokployRestart boolean [not null, default: false] + dockerCleanup boolean [not null, default: false] + serverThreshold boolean [not null, default: false] + notificationType notificationType [not null] + createdAt text [not null] + slackId text + telegramId text + discordId text + emailId text + gotifyId text + userId text +} + +table organization { + id text [pk, not null] + name text [not null] + slug text [unique] + logo text + created_at timestamp [not null] + metadata text + owner_id text [not null] +} + +table port { + portId text [pk, not null] + publishedPort integer [not null] + targetPort integer [not null] + protocol protocolType [not null] + applicationId text [not null] +} + +table postgres { + postgresId text [pk, not null] + name text [not null] + appName text [not null, unique] + databaseName text [not null] + databaseUser text [not null] + databasePassword text [not null] + description text + dockerImage text [not null] + command text + env text + memoryReservation text + externalPort integer + memoryLimit text + cpuReservation text + cpuLimit text + applicationStatus applicationStatus [not null, default: 'idle'] + createdAt text [not null] + projectId text [not null] + serverId text +} + +table preview_deployments { + previewDeploymentId text [pk, not null] + branch text [not null] + pullRequestId text [not null] + pullRequestNumber text [not null] + pullRequestURL text [not null] + pullRequestTitle text [not null] + pullRequestCommentId text [not null] + previewStatus applicationStatus [not null, default: 'idle'] + appName text [not null, unique] + applicationId text [not null] + domainId text + createdAt text [not null] + expiresAt text +} + +table project { + projectId text [pk, not null] + name text [not null] + description text + createdAt text [not null] + userId text [not null] + env text [not null, default: ''] +} + +table redirect { + redirectId text [pk, not null] + regex text [not null] + replacement text [not null] + permanent boolean [not null, default: false] + uniqueConfigKey serial [not null, increment] + createdAt text [not null] + applicationId text [not null] +} + +table redis { + redisId text [pk, not null] + name text [not null] + appName text [not null, unique] + description text + password text [not null] + dockerImage text [not null] + command text + env text + memoryReservation text + memoryLimit text + cpuReservation text + cpuLimit text + externalPort integer + createdAt text [not null] + applicationStatus applicationStatus [not null, default: 'idle'] + projectId text [not null] + serverId text +} + +table registry { + registryId text [pk, not null] + registryName text [not null] + imagePrefix text + username text [not null] + password text [not null] + registryUrl text [not null, default: ''] + createdAt text [not null] + selfHosted RegistryType [not null, default: 'cloud'] + userId text [not null] +} + +table security { + securityId text [pk, not null] + username text [not null] + password text [not null] + createdAt text [not null] + applicationId text [not null] + + indexes { + (username, applicationId) [name: 'security_username_applicationId_unique', unique] + } +} + +table server { + serverId text [pk, not null] + name text [not null] + description text + ipAddress text [not null] + port integer [not null] + username text [not null, default: 'root'] + appName text [not null] + enableDockerCleanup boolean [not null, default: false] + createdAt text [not null] + userId text [not null] + serverStatus serverStatus [not null, default: 'active'] + command text [not null, default: ''] + sshKeyId text + metricsConfig jsonb [not null, default: `{"server":{"type":"Remote","refreshRate":60,"port":4500,"token":"","urlCallback":"","cronJob":"","retentionDays":2,"thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`] +} + +table session { + id text [pk, not null] + expires_at timestamp [not null] + token text [not null, unique] + created_at timestamp [not null] + updated_at timestamp [not null] + ip_address text + user_agent text + user_id text [not null] + impersonated_by text + active_organization_id text +} + +table slack { + slackId text [pk, not null] + webhookUrl text [not null] + channel text +} + +table "ssh-key" { + sshKeyId text [pk, not null] + privateKey text [not null, default: ''] + publicKey text [not null] + name text [not null] + description text + createdAt text [not null] + lastUsedAt text + userId text +} + +table telegram { + telegramId text [pk, not null] + botToken text [not null] + chatId text [not null] +} + +table user { + id text [pk, not null] + name text [not null, default: ''] + token text [not null] + isRegistered boolean [not null, default: false] + expirationDate text [not null] + createdAt text [not null] + canCreateProjects boolean [not null, default: false] + canAccessToSSHKeys boolean [not null, default: false] + canCreateServices boolean [not null, default: false] + canDeleteProjects boolean [not null, default: false] + canDeleteServices boolean [not null, default: false] + canAccessToDocker boolean [not null, default: false] + canAccessToAPI boolean [not null, default: false] + canAccessToGitProviders boolean [not null, default: false] + canAccessToTraefikFiles boolean [not null, default: false] + accesedProjects text[] [not null, default: `ARRAY[]::text[]`] + accesedServices text[] [not null, default: `ARRAY[]::text[]`] + email text [not null, unique] + email_verified boolean [not null] + image text + role text + banned boolean + ban_reason text + ban_expires timestamp + updated_at timestamp [not null] + serverIp text + certificateType certificateType [not null, default: 'none'] + host text + letsEncryptEmail text + sshPrivateKey text + enableDockerCleanup boolean [not null, default: false] + enableLogRotation boolean [not null, default: false] + enablePaidFeatures boolean [not null, default: false] + metricsConfig jsonb [not null, default: `{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}`] + cleanupCacheApplications boolean [not null, default: false] + cleanupCacheOnPreviews boolean [not null, default: false] + cleanupCacheOnCompose boolean [not null, default: false] + stripeCustomerId text + stripeSubscriptionId text + serversQuantity integer [not null, default: 0] +} + +table verification { + id text [pk, not null] + identifier text [not null] + value text [not null] + expires_at timestamp [not null] + created_at timestamp + updated_at timestamp +} + +ref: mount.applicationId > application.applicationId + +ref: mount.postgresId > postgres.postgresId + +ref: mount.mariadbId > mariadb.mariadbId + +ref: mount.mongoId > mongo.mongoId + +ref: mount.mysqlId > mysql.mysqlId + +ref: mount.redisId > redis.redisId + +ref: mount.composeId > compose.composeId + +ref: application.projectId > project.projectId + +ref: application.customGitSSHKeyId > "ssh-key".sshKeyId + +ref: application.registryId > registry.registryId + +ref: application.githubId - github.githubId + +ref: application.gitlabId - gitlab.gitlabId + +ref: application.bitbucketId - bitbucket.bitbucketId + +ref: application.serverId > server.serverId + +ref: backup.destinationId > destination.destinationId + +ref: backup.postgresId > postgres.postgresId + +ref: backup.mariadbId > mariadb.mariadbId + +ref: backup.mysqlId > mysql.mysqlId + +ref: backup.mongoId > mongo.mongoId + +ref: git_provider.gitProviderId - bitbucket.gitProviderId + +ref: certificate.serverId > server.serverId + +ref: certificate.userId - user.id + +ref: compose.projectId > project.projectId + +ref: compose.customGitSSHKeyId > "ssh-key".sshKeyId + +ref: compose.githubId - github.githubId + +ref: compose.gitlabId - gitlab.gitlabId + +ref: compose.bitbucketId - bitbucket.bitbucketId + +ref: compose.serverId > server.serverId + +ref: deployment.applicationId > application.applicationId + +ref: deployment.composeId > compose.composeId + +ref: deployment.serverId > server.serverId + +ref: deployment.previewDeploymentId > preview_deployments.previewDeploymentId + +ref: destination.userId - user.id + +ref: domain.applicationId > application.applicationId + +ref: domain.composeId > compose.composeId + +ref: preview_deployments.domainId - domain.domainId + +ref: github.gitProviderId - git_provider.gitProviderId + +ref: gitlab.gitProviderId - git_provider.gitProviderId + +ref: git_provider.userId - user.id + +ref: mariadb.projectId > project.projectId + +ref: mariadb.serverId > server.serverId + +ref: mongo.projectId > project.projectId + +ref: mongo.serverId > server.serverId + +ref: mysql.projectId > project.projectId + +ref: mysql.serverId > server.serverId + +ref: notification.slackId - slack.slackId + +ref: notification.telegramId - telegram.telegramId + +ref: notification.discordId - discord.discordId + +ref: notification.emailId - email.emailId + +ref: notification.gotifyId - gotify.gotifyId + +ref: notification.userId - user.id + +ref: port.applicationId > application.applicationId + +ref: postgres.projectId > project.projectId + +ref: postgres.serverId > server.serverId + +ref: preview_deployments.applicationId > application.applicationId + +ref: project.userId - user.id + +ref: redirect.applicationId > application.applicationId + +ref: redis.projectId > project.projectId + +ref: redis.serverId > server.serverId + +ref: registry.userId - user.id + +ref: security.applicationId > application.applicationId + +ref: server.userId - user.id + +ref: server.sshKeyId > "ssh-key".sshKeyId + +ref: "ssh-key".userId - user.id \ No newline at end of file diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index bb06f8bc0..886cedbb3 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -1,5 +1,12 @@ import { relations, sql } from "drizzle-orm"; -import { boolean, jsonb, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { + boolean, + integer, + jsonb, + pgTable, + text, + timestamp, +} from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; @@ -22,18 +29,12 @@ export const user = pgTable("user", { name: text("name").notNull().default(""), token: text("token").notNull(), isRegistered: boolean("isRegistered").notNull().default(false), - expirationDate: timestamp("expirationDate", { - precision: 3, - mode: "date", - }) + expirationDate: text("expirationDate") .notNull() - .$defaultFn(() => new Date()), - createdAt: timestamp("createdAt", { - precision: 3, - mode: "date", - }) + .$defaultFn(() => new Date().toISOString()), + createdAt: text("createdAt") .notNull() - .$defaultFn(() => new Date()), + .$defaultFn(() => new Date().toISOString()), canCreateProjects: boolean("canCreateProjects").notNull().default(false), canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), canCreateServices: boolean("canCreateServices").notNull().default(false), @@ -132,6 +133,9 @@ export const user = pgTable("user", { cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") .notNull() .default(false), + stripeCustomerId: text("stripeCustomerId"), + stripeSubscriptionId: text("stripeSubscriptionId"), + serversQuantity: integer("serversQuantity").notNull().default(0), }); export const usersRelations = relations(user, ({ one }) => ({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 555822409..bb49174f4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -588,6 +588,9 @@ importers: dotenv: specifier: 16.4.5 version: 16.4.5 + drizzle-dbml-generator: + specifier: 0.10.0 + version: 0.10.0(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)) drizzle-orm: specifier: ^0.39.1 version: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) @@ -4446,6 +4449,11 @@ packages: resolution: {integrity: sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==} engines: {node: '>=4'} + drizzle-dbml-generator@0.10.0: + resolution: {integrity: sha512-cMZq9E3U3RlmE0uBeXyc6oWJ0royOkC6HiTlc9LDeMe+W87poZTzKoNYUyAxZrs4Q1RQtob+cGKiefV4ZoI8HA==} + peerDependencies: + drizzle-orm: '>=0.36.0' + drizzle-kit@0.30.4: resolution: {integrity: sha512-B2oJN5UkvwwNHscPWXDG5KqAixu7AUzZ3qbe++KU9SsQ+cZWR4DXEPYcvWplyFAno0dhRJECNEhNxiDmFaPGyQ==} hasBin: true @@ -11119,6 +11127,10 @@ snapshots: drange@1.1.1: {} + drizzle-dbml-generator@0.10.0(drizzle-orm@0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7)): + dependencies: + drizzle-orm: 0.39.1(@types/react@18.3.5)(kysely@0.27.5)(postgres@3.4.4)(react@18.2.0)(sqlite3@5.1.7) + drizzle-kit@0.30.4: dependencies: '@drizzle-team/brocli': 0.10.2 From 6179cef1ee19a32da0178a81ca9d183266484467 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 10 Feb 2025 02:13:52 -0600 Subject: [PATCH 007/126] refactor: update name --- apps/dokploy/drizzle/0066_calm_vengeance.sql | 367 ------------------ apps/dokploy/drizzle/0066_soft_kronos.sql | 183 +++++++++ apps/dokploy/drizzle/meta/0066_snapshot.json | 100 ++--- apps/dokploy/drizzle/meta/_journal.json | 4 +- packages/server/auth-schema.ts | 10 +- packages/server/src/db/schema/account.ts | 8 +- packages/server/src/db/schema/certificate.ts | 2 +- packages/server/src/db/schema/destination.ts | 2 +- packages/server/src/db/schema/git-provider.ts | 2 +- packages/server/src/db/schema/notification.ts | 2 +- packages/server/src/db/schema/project.ts | 2 +- packages/server/src/db/schema/registry.ts | 2 +- packages/server/src/db/schema/server.ts | 2 +- packages/server/src/db/schema/session.ts | 5 +- packages/server/src/db/schema/ssh-key.ts | 2 +- packages/server/src/db/schema/user.ts | 2 +- 16 files changed, 249 insertions(+), 446 deletions(-) delete mode 100644 apps/dokploy/drizzle/0066_calm_vengeance.sql create mode 100644 apps/dokploy/drizzle/0066_soft_kronos.sql diff --git a/apps/dokploy/drizzle/0066_calm_vengeance.sql b/apps/dokploy/drizzle/0066_calm_vengeance.sql deleted file mode 100644 index 3901740b3..000000000 --- a/apps/dokploy/drizzle/0066_calm_vengeance.sql +++ /dev/null @@ -1,367 +0,0 @@ --- Primero ejecutar todas las modificaciones estructurales -ALTER TABLE "user" RENAME COLUMN "userId" TO "id"; ---> statement-breakpoint -ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "ssh-key" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId"; ---> statement-breakpoint -ALTER TABLE "user" DROP CONSTRAINT "user_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "user" DROP CONSTRAINT "user_authId_auth_id_fk"; ---> statement-breakpoint -ALTER TABLE "admin" DROP CONSTRAINT "admin_authId_auth_id_fk"; ---> statement-breakpoint -ALTER TABLE "project" DROP CONSTRAINT "project_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "destination" DROP CONSTRAINT "destination_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "certificate" DROP CONSTRAINT "certificate_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "session" DROP CONSTRAINT "session_user_id_auth_id_fk"; ---> statement-breakpoint -ALTER TABLE "registry" DROP CONSTRAINT "registry_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "notification" DROP CONSTRAINT "notification_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "ssh-key" DROP CONSTRAINT "ssh-key_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "server" DROP CONSTRAINT "server_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "user" ALTER COLUMN "expirationDate" SET DATA TYPE text; ---> statement-breakpoint -ALTER TABLE "session" ALTER COLUMN "expires_at" SET DATA TYPE timestamp; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "name" text DEFAULT '' NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "email" text NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "email_verified" boolean NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "image" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "role" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "banned" boolean; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "ban_reason" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "updated_at" timestamp NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "serverIp" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "certificateType" "certificateType" DEFAULT 'none' NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "host" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "letsEncryptEmail" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "sshPrivateKey" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "enableDockerCleanup" boolean DEFAULT false NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "enableLogRotation" boolean DEFAULT false NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "enablePaidFeatures" boolean DEFAULT false NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "cleanupCacheApplications" boolean DEFAULT false NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "cleanupCacheOnCompose" boolean DEFAULT false NOT NULL; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "stripeCustomerId" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "stripeSubscriptionId" text; ---> statement-breakpoint -ALTER TABLE "user" ADD COLUMN "serversQuantity" integer DEFAULT 0 NOT NULL; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "token" text NOT NULL; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "created_at" timestamp NOT NULL; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "updated_at" timestamp NOT NULL; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "ip_address" text; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "user_agent" text; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "impersonated_by" text; ---> statement-breakpoint -ALTER TABLE "session" ADD COLUMN "active_organization_id" text; ---> statement-breakpoint -CREATE TABLE "account" ( - "id" text PRIMARY KEY NOT NULL, - "account_id" text NOT NULL, - "provider_id" text NOT NULL, - "user_id" text NOT NULL, - "access_token" text, - "refresh_token" text, - "id_token" text, - "access_token_expires_at" timestamp, - "refresh_token_expires_at" timestamp, - "scope" text, - "password" text, - "is2FAEnabled" boolean DEFAULT false NOT NULL, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL, - "resetPasswordToken" text, - "resetPasswordExpiresAt" text, - "confirmationToken" text, - "confirmationExpiresAt" text -); ---> statement-breakpoint -CREATE TABLE "invitation" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "email" text NOT NULL, - "role" text, - "status" text NOT NULL, - "expires_at" timestamp NOT NULL, - "inviter_id" text NOT NULL -); ---> statement-breakpoint -CREATE TABLE "member" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "user_id" text NOT NULL, - "role" text NOT NULL, - "created_at" timestamp NOT NULL -); ---> statement-breakpoint -CREATE TABLE "organization" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "slug" text, - "logo" text, - "created_at" timestamp NOT NULL, - "metadata" text, - "owner_id" text NOT NULL, - CONSTRAINT "organization_slug_unique" UNIQUE("slug") -); ---> statement-breakpoint -CREATE TABLE "verification" ( - "id" text PRIMARY KEY NOT NULL, - "identifier" text NOT NULL, - "value" text NOT NULL, - "expires_at" timestamp NOT NULL, - "created_at" timestamp, - "updated_at" timestamp -); ---> statement-breakpoint -ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; ---> statement-breakpoint -ALTER TABLE "user" DROP COLUMN "adminId"; ---> statement-breakpoint -ALTER TABLE "user" DROP COLUMN "authId"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "adminId"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "serverIp"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "certificateType"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "host"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "letsEncryptEmail"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "sshPrivateKey"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "enableDockerCleanup"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "enableLogRotation"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "authId"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "createdAt"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "stripeCustomerId"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "stripeSubscriptionId"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "serversQuantity"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "enablePaidFeatures"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "metricsConfig"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "cleanupCacheApplications"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "cleanupCacheOnPreviews"; ---> statement-breakpoint -ALTER TABLE "admin" DROP COLUMN "cleanupCacheOnCompose"; ---> statement-breakpoint -ALTER TABLE "user" ADD CONSTRAINT "user_email_unique" UNIQUE("email"); ---> statement-breakpoint - --- Primero quitar NOT NULL temporalmente -ALTER TABLE "session" ALTER COLUMN "token" DROP NOT NULL; ---> statement-breakpoint - --- Actualizar tokens existentes -UPDATE "session" SET - token = gen_random_uuid(), - created_at = COALESCE(created_at, NOW() - interval '1 day'), - updated_at = NOW() -WHERE token IS NULL; - --- Restablecer restricciones -ALTER TABLE "session" ALTER COLUMN "token" SET NOT NULL; -ALTER TABLE "session" ADD CONSTRAINT "session_token_unique" UNIQUE (token); - --- Luego realizar la migración de datos --- Migración de datos para Admins -WITH admin_users AS ( - INSERT INTO "user" ( - id, created_at, token, email, email_verified, role, updated_at, - certificate_type, server_ip, host, lets_encrypt_email, ssh_private_key, - enable_docker_cleanup, enable_log_rotation, enable_paid_features, - metrics_config, cleanup_cache_applications, cleanup_cache_on_previews, - cleanup_cache_on_compose, stripe_customer_id, stripe_subscription_id, - servers_quantity - ) - SELECT - gen_random_uuid(), - a.created_at, - a.token, - a.email, - true, - 'admin', - a.created_at, - ad.certificate_type, - ad.server_ip, - ad.host, - ad.lets_encrypt_email, - ad.ssh_private_key, - ad.enable_docker_cleanup, - ad.enable_log_rotation, - ad.enable_paid_features, - ad.metrics_config, - ad.cleanup_cache_applications, - ad.cleanup_cache_on_previews, - ad.cleanup_cache_on_compose, - ad.stripe_customer_id, - ad.stripe_subscription_id, - ad.servers_quantity - FROM auth a - JOIN admins ad ON a.id = ad.auth_id - RETURNING id AS user_id, created_at, email -) -INSERT INTO account (id, account_id, provider_id, user_id, password, created_at, updated_at) -SELECT - gen_random_uuid(), - a.id, - 'credentials', - au.user_id, - a.password, - au.created_at, - au.created_at -FROM auth a -JOIN admin_users au ON a.email = au.email; - --- Crear organizaciones para admins -WITH admin_orgs AS ( - INSERT INTO organization (id, name, slug, created_at, owner_id) - SELECT - gen_random_uuid(), - 'My Organization', - 'org/' || au.user_id, - au.created_at, - au.user_id - FROM admin_users au - RETURNING id AS org_id, owner_id -) --- Migrar usuarios regulares y asociar a organizaciones -INSERT INTO "user" ( - id, created_at, token, email, email_verified, role, updated_at, - can_create_projects, can_access_ssh_keys -) -SELECT - gen_random_uuid(), - a.created_at, - a.token, - a.email, - true, - 'user', - a.created_at, - u.can_create_projects, - u.can_access_ssh_keys -FROM auth a -JOIN users u ON a.id = u.auth_id -WHERE a.role = 'user'; - --- Crear accounts para usuarios -INSERT INTO account (id, account_id, provider_id, user_id, password, created_at, updated_at) -SELECT - gen_random_uuid(), - a.id, - 'credentials', - u.id, - a.password, - a.created_at, - a.created_at -FROM auth a -JOIN "user" u ON a.email = u.email; - --- Asociar usuarios a organizaciones de sus admins -INSERT INTO member (id, organization_id, user_id, role, created_at) -SELECT - gen_random_uuid(), - ao.org_id, - u.id, - 'user', - u.created_at -FROM "user" u -JOIN users old_u ON u.email = old_u.email -JOIN auth a ON old_u.auth_id = a.id -JOIN admin_orgs ao ON ao.owner_id = a.id; - --- Eliminar tablas obsoletas -DROP TABLE IF EXISTS admins; -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS auth; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0066_soft_kronos.sql b/apps/dokploy/drizzle/0066_soft_kronos.sql new file mode 100644 index 000000000..e75a6b247 --- /dev/null +++ b/apps/dokploy/drizzle/0066_soft_kronos.sql @@ -0,0 +1,183 @@ +-- Create new tables +CREATE TABLE IF NOT EXISTS "account" ( + "id" text PRIMARY KEY NOT NULL, + "accountId" text NOT NULL, + "providerId" text NOT NULL, + "userId" text NOT NULL REFERENCES "user"("userId"), + "accessToken" text, + "refreshToken" text, + "idToken" text, + "accessTokenExpiresAt" timestamp, + "refreshTokenExpiresAt" timestamp, + "scope" text, + "password" text, + "is2FAEnabled" boolean DEFAULT false NOT NULL, + "createdAt" timestamp NOT NULL, + "updatedAt" timestamp NOT NULL, + "resetPasswordToken" text, + "resetPasswordExpiresAt" text, + "confirmationToken" text, + "confirmationExpiresAt" text +); + +CREATE TABLE IF NOT EXISTS "organization" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "slug" text, + "logo" text, + "createdAt" timestamp NOT NULL, + "metadata" text, + "ownerId" text NOT NULL REFERENCES "user"("userId"), + CONSTRAINT "organization_slug_unique" UNIQUE("slug") +); + +CREATE TABLE IF NOT EXISTS "member" ( + "id" text PRIMARY KEY NOT NULL, + "organizationId" text NOT NULL REFERENCES "organization"("id"), + "userId" text NOT NULL REFERENCES "user"("userId"), + "role" text NOT NULL, + "createdAt" timestamp NOT NULL +); + +CREATE TABLE IF NOT EXISTS "invitation" ( + "id" text PRIMARY KEY NOT NULL, + "organizationId" text NOT NULL, + "email" text NOT NULL, + "role" text, + "status" text NOT NULL, + "expiresAt" timestamp NOT NULL, + "inviterId" text NOT NULL +); + +CREATE TABLE IF NOT EXISTS "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expiresAt" timestamp NOT NULL, + "createdAt" timestamp, + "updatedAt" timestamp +); + +-- Alter existing user table to add new columns +ALTER TABLE "user" +ADD COLUMN IF NOT EXISTS "email" text, +ADD COLUMN IF NOT EXISTS "emailVerified" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "role" text, +ADD COLUMN IF NOT EXISTS "certificateType" text DEFAULT 'none', +ADD COLUMN IF NOT EXISTS "serverIp" text, +ADD COLUMN IF NOT EXISTS "host" text, +ADD COLUMN IF NOT EXISTS "letsEncryptEmail" text, +ADD COLUMN IF NOT EXISTS "sshPrivateKey" text, +ADD COLUMN IF NOT EXISTS "enableDockerCleanup" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "enableLogRotation" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "enablePaidFeatures" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "metricsConfig" jsonb DEFAULT '{}', +ADD COLUMN IF NOT EXISTS "cleanupCacheApplications" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "cleanupCacheOnPreviews" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "cleanupCacheOnCompose" boolean DEFAULT false, +ADD COLUMN IF NOT EXISTS "stripeCustomerId" text, +ADD COLUMN IF NOT EXISTS "stripeSubscriptionId" text, +ADD COLUMN IF NOT EXISTS "serversQuantity" integer DEFAULT 0; + +-- Migrate admin users +WITH admin_users AS ( + UPDATE "user" u + SET + "email" = a."email", + "emailVerified" = true, + "role" = 'admin', + "token" = a."token", + "certificateType" = adm."certificateType", + "serverIp" = adm."serverIp", + "host" = adm."host", + "letsEncryptEmail" = adm."letsEncryptEmail", + "sshPrivateKey" = adm."sshPrivateKey", + "enableDockerCleanup" = adm."enableDockerCleanup", + "enableLogRotation" = adm."enableLogRotation", + "enablePaidFeatures" = adm."enablePaidFeatures", + "metricsConfig" = adm."metricsConfig", + "cleanupCacheApplications" = adm."cleanupCacheApplications", + "cleanupCacheOnPreviews" = adm."cleanupCacheOnPreviews", + "cleanupCacheOnCompose" = adm."cleanupCacheOnCompose", + "stripeCustomerId" = adm."stripeCustomerId", + "stripeSubscriptionId" = adm."stripeSubscriptionId", + "serversQuantity" = adm."serversQuantity" + FROM "auth" a + INNER JOIN "admin" adm ON a."id" = adm."adminId" + WHERE a."id" = u."userId" + RETURNING u."userId", a."email" +) +INSERT INTO "account" ("id", "accountId", "providerId", "password", "userId", "createdAt", "updatedAt") +SELECT + gen_random_uuid(), + a."id", + 'credentials', + a."password", + au."userId", + NOW(), + NOW() +FROM "auth" a +INNER JOIN admin_users au ON a."email" = au."email"; + +-- Create organizations for admin users +WITH admin_orgs AS ( + INSERT INTO "organization" ("id", "name", "slug", "createdAt", "ownerId") + SELECT + gen_random_uuid(), + 'My Organization', + concat('org/', u."userId"), + NOW(), + u."userId" + FROM "user" u + WHERE u."role" = 'admin' + RETURNING * +) +-- Migrate regular users +UPDATE "user" u +SET + "email" = a."email", + "emailVerified" = true, + "role" = 'user', + "token" = a."token", + "canCreateProjects" = usr."canCreateProjects", + "canAccessToSSHKeys" = usr."canAccessToSSHKeys" +FROM "auth" a +INNER JOIN "user" usr ON a."id" = usr."userId" +WHERE a."id" = u."userId" +AND NOT EXISTS ( + SELECT 1 FROM "admin" adm WHERE a."id" = adm."adminId" +); + +-- Create accounts for regular users +INSERT INTO "account" ("id", "accountId", "providerId", "password", "userId", "createdAt", "updatedAt") +SELECT + gen_random_uuid(), + a."id", + 'credentials', + a."password", + u."userId", + NOW(), + NOW() +FROM "auth" a +INNER JOIN "user" u ON a."email" = u."email" +WHERE u."role" = 'user'; + +-- Create member relationships +INSERT INTO "member" ("id", "organizationId", "role", "userId", "createdAt") +SELECT + gen_random_uuid(), + o."id", + 'user', + u."userId", + NOW() +FROM "user" usr +INNER JOIN "user" u ON usr."userId" = u."userId" +INNER JOIN "admin" adm ON usr."adminId" = adm."adminId" +INNER JOIN "user" admin_u ON adm."adminId" = admin_u."userId" +INNER JOIN "organization" o ON o."ownerId" = admin_u."userId" +WHERE u."role" = 'user'; + +-- Drop old tables (after all data is migrated) +DROP TABLE IF EXISTS "sessionTable" CASCADE; +DROP TABLE IF EXISTS "admin" CASCADE; +DROP TABLE IF EXISTS "auth" CASCADE; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json index 83cdfe7e3..3ffb0dbf0 100644 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -1,5 +1,5 @@ { - "id": "5963096c-1a97-4e26-9f3f-f8613a359596", + "id": "d76ab830-b647-4e53-b6cc-0cf515968758", "prevId": "1240ec96-1751-4de3-b64f-cef9cb716786", "version": "7", "dialect": "postgresql", @@ -735,8 +735,8 @@ "name": "user", "schema": "", "columns": { - "id": { - "name": "id", + "userId": { + "name": "userId", "type": "text", "primaryKey": true, "notNull": true @@ -1172,15 +1172,15 @@ }, "indexes": {}, "foreignKeys": { - "project_userId_user_id_fk": { - "name": "project_userId_user_id_fk", + "project_userId_user_userId_fk": { + "name": "project_userId_user_userId_fk", "tableFrom": "project", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2042,15 +2042,15 @@ }, "indexes": {}, "foreignKeys": { - "destination_userId_user_id_fk": { - "name": "destination_userId_user_id_fk", + "destination_userId_user_userId_fk": { + "name": "destination_userId_user_userId_fk", "tableFrom": "destination", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2456,15 +2456,15 @@ }, "indexes": {}, "foreignKeys": { - "certificate_userId_user_id_fk": { - "name": "certificate_userId_user_id_fk", + "certificate_userId_user_userId_fk": { + "name": "certificate_userId_user_userId_fk", "tableFrom": "certificate", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2513,12 +2513,6 @@ "primaryKey": false, "notNull": true }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true - }, "created_at": { "name": "created_at", "type": "timestamp", @@ -2564,30 +2558,22 @@ }, "indexes": {}, "foreignKeys": { - "session_user_id_user_id_fk": { - "name": "session_user_id_user_id_fk", + "session_user_id_user_userId_fk": { + "name": "session_user_id_user_userId_fk", "tableFrom": "session", "tableTo": "user", "columnsFrom": [ "user_id" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "no action", "onUpdate": "no action" } }, "compositePrimaryKeys": {}, - "uniqueConstraints": { - "session_token_unique": { - "name": "session_token_unique", - "nullsNotDistinct": false, - "columns": [ - "token" - ] - } - }, + "uniqueConstraints": {}, "policies": {}, "checkConstraints": {}, "isRLSEnabled": false @@ -3322,15 +3308,15 @@ }, "indexes": {}, "foreignKeys": { - "registry_userId_user_id_fk": { - "name": "registry_userId_user_id_fk", + "registry_userId_user_userId_fk": { + "name": "registry_userId_user_userId_fk", "tableFrom": "registry", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3647,15 +3633,15 @@ "onDelete": "cascade", "onUpdate": "no action" }, - "notification_userId_user_id_fk": { - "name": "notification_userId_user_id_fk", + "notification_userId_user_userId_fk": { + "name": "notification_userId_user_userId_fk", "tableFrom": "notification", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3785,15 +3771,15 @@ }, "indexes": {}, "foreignKeys": { - "ssh-key_userId_user_id_fk": { - "name": "ssh-key_userId_user_id_fk", + "ssh-key_userId_user_userId_fk": { + "name": "ssh-key_userId_user_userId_fk", "tableFrom": "ssh-key", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3844,15 +3830,15 @@ }, "indexes": {}, "foreignKeys": { - "git_provider_userId_user_id_fk": { - "name": "git_provider_userId_user_id_fk", + "git_provider_userId_user_userId_fk": { + "name": "git_provider_userId_user_userId_fk", "tableFrom": "git_provider", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -4187,15 +4173,15 @@ }, "indexes": {}, "foreignKeys": { - "server_userId_user_id_fk": { - "name": "server_userId_user_id_fk", + "server_userId_user_userId_fk": { + "name": "server_userId_user_userId_fk", "tableFrom": "server", "tableTo": "user", "columnsFrom": [ "userId" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -4464,15 +4450,15 @@ }, "indexes": {}, "foreignKeys": { - "account_user_id_user_id_fk": { - "name": "account_user_id_user_id_fk", + "account_user_id_user_userId_fk": { + "name": "account_user_id_user_userId_fk", "tableFrom": "account", "tableTo": "user", "columnsFrom": [ "user_id" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "no action", "onUpdate": "no action" @@ -4546,15 +4532,15 @@ "onDelete": "no action", "onUpdate": "no action" }, - "invitation_inviter_id_user_id_fk": { - "name": "invitation_inviter_id_user_id_fk", + "invitation_inviter_id_user_userId_fk": { + "name": "invitation_inviter_id_user_userId_fk", "tableFrom": "invitation", "tableTo": "user", "columnsFrom": [ "inviter_id" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "no action", "onUpdate": "no action" @@ -4616,15 +4602,15 @@ "onDelete": "no action", "onUpdate": "no action" }, - "member_user_id_user_id_fk": { - "name": "member_user_id_user_id_fk", + "member_user_id_user_userId_fk": { + "name": "member_user_id_user_userId_fk", "tableFrom": "member", "tableTo": "user", "columnsFrom": [ "user_id" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "no action", "onUpdate": "no action" @@ -4685,15 +4671,15 @@ }, "indexes": {}, "foreignKeys": { - "organization_owner_id_user_id_fk": { - "name": "organization_owner_id_user_id_fk", + "organization_owner_id_user_userId_fk": { + "name": "organization_owner_id_user_userId_fk", "tableFrom": "organization", "tableTo": "user", "columnsFrom": [ "owner_id" ], "columnsTo": [ - "id" + "userId" ], "onDelete": "no action", "onUpdate": "no action" diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 8c06b6375..d3de75645 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -467,8 +467,8 @@ { "idx": 66, "version": "7", - "when": 1739168611366, - "tag": "0066_calm_vengeance", + "when": 1739173929725, + "tag": "0066_soft_kronos", "breakpoints": true } ] diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 8b09e3366..3e19bebe0 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -26,7 +26,7 @@ export const session = pgTable("session", { userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => user.id), + .references(() => user.userId), activeOrganizationId: text("active_organization_id"), }); @@ -36,7 +36,7 @@ export const account = pgTable("account", { providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => user.id), + .references(() => user.userId), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), @@ -66,7 +66,7 @@ export const organization = pgTable("organization", { metadata: text("metadata"), ownerId: text("owner_id") .notNull() - .references(() => user.id), + .references(() => user.userId), }); export const member = pgTable("member", { @@ -76,7 +76,7 @@ export const member = pgTable("member", { .references(() => organization.id), userId: text("user_id") .notNull() - .references(() => user.id), + .references(() => user.userId), role: text("role").notNull(), createdAt: timestamp("created_at").notNull(), }); @@ -92,5 +92,5 @@ export const invitation = pgTable("invitation", { expiresAt: timestamp("expires_at").notNull(), inviterId: text("inviter_id") .notNull() - .references(() => user.id), + .references(() => user.userId), }); diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index ce963a0d3..24350c55c 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -7,7 +7,7 @@ export const account = pgTable("account", { providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => user.id), + .references(() => user.userId), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), @@ -42,7 +42,7 @@ export const organization = pgTable("organization", { metadata: text("metadata"), ownerId: text("owner_id") .notNull() - .references(() => user.id), + .references(() => user.userId), }); export const member = pgTable("member", { @@ -52,7 +52,7 @@ export const member = pgTable("member", { .references(() => organization.id), userId: text("user_id") .notNull() - .references(() => user.id), + .references(() => user.userId), role: text("role").notNull(), createdAt: timestamp("created_at").notNull(), }); @@ -68,5 +68,5 @@ export const invitation = pgTable("invitation", { expiresAt: timestamp("expires_at").notNull(), inviterId: text("inviter_id") .notNull() - .references(() => user.id), + .references(() => user.userId), }); diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index 3a7ec596d..c1a57a5ac 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -20,7 +20,7 @@ export const certificates = pgTable("certificate", { .$defaultFn(() => generateAppName("certificate")) .unique(), autoRenew: boolean("autoRenew"), - userId: text("userId").references(() => user.id, { + userId: text("userId").references(() => user.userId, { onDelete: "cascade", }), serverId: text("serverId").references(() => server.serverId, { diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index 37faf0fa4..1fe48a346 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -22,7 +22,7 @@ export const destinations = pgTable("destination", { endpoint: text("endpoint").notNull(), userId: text("userId") .notNull() - .references(() => user.id, { onDelete: "cascade" }), + .references(() => user.userId, { onDelete: "cascade" }), }); export const destinationsRelations = relations( diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index f044b0a09..3a3bade81 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -25,7 +25,7 @@ export const gitProvider = pgTable("git_provider", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - userId: text("userId").references(() => user.id, { + userId: text("userId").references(() => user.userId, { onDelete: "cascade", }), }); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 5174c17c1..1771a92af 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -45,7 +45,7 @@ export const notifications = pgTable("notification", { gotifyId: text("gotifyId").references(() => gotify.gotifyId, { onDelete: "cascade", }), - userId: text("userId").references(() => user.id, { + userId: text("userId").references(() => user.userId, { onDelete: "cascade", }), }); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index 2602a8ec0..d83936a38 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -26,7 +26,7 @@ export const projects = pgTable("project", { .$defaultFn(() => new Date().toISOString()), userId: text("userId") .notNull() - .references(() => user.id, { onDelete: "cascade" }), + .references(() => user.userId, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 166168c5f..936f65fc8 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -30,7 +30,7 @@ export const registry = pgTable("registry", { registryType: registryType("selfHosted").notNull().default("cloud"), userId: text("userId") .notNull() - .references(() => user.id, { onDelete: "cascade" }), + .references(() => user.userId, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index e3a14f953..4e238e23b 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -46,7 +46,7 @@ export const server = pgTable("server", { .$defaultFn(() => new Date().toISOString()), userId: text("userId") .notNull() - .references(() => user.id, { onDelete: "cascade" }), + .references(() => user.userId, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index 66a410b30..d5932764a 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,3 +1,4 @@ +import { sql } from "drizzle-orm"; import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { user } from "./user"; @@ -5,14 +6,14 @@ import { user } from "./user"; export const session = pgTable("session", { id: text("id").primaryKey(), expiresAt: timestamp("expires_at").notNull(), - token: text("token").notNull().unique(), + // token: text("token").notNull().unique().default(sql`gen_random_uuid()`), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => user.id), + .references(() => user.userId), impersonatedBy: text("impersonated_by"), activeOrganizationId: text("active_organization_id"), }); diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index 6c5ba0a7b..9bb69fee4 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -22,7 +22,7 @@ export const sshKeys = pgTable("ssh-key", { .notNull() .$defaultFn(() => new Date().toISOString()), lastUsedAt: text("lastUsedAt"), - userId: text("userId").references(() => user.id, { + userId: text("userId").references(() => user.userId, { onDelete: "cascade", }), }); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 886cedbb3..00e16679b 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -22,7 +22,7 @@ import { certificateType } from "./shared"; // OLD TABLE export const user = pgTable("user", { - id: text("id") + userId: text("userId") .notNull() .primaryKey() .$defaultFn(() => nanoid()), From 6ea2a82bb085accf1b3f357a501d34a0ad23a8a5 Mon Sep 17 00:00:00 2001 From: Jefferson Carlos <31220819+jeffersoncbd@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:21:23 -0300 Subject: [PATCH 008/126] Create index.ts for Trilium app --- apps/dokploy/templates/trilium/index.ts | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 apps/dokploy/templates/trilium/index.ts diff --git a/apps/dokploy/templates/trilium/index.ts b/apps/dokploy/templates/trilium/index.ts new file mode 100644 index 000000000..acac98413 --- /dev/null +++ b/apps/dokploy/templates/trilium/index.ts @@ -0,0 +1,22 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const triliumDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: triliumDomain, + port: 8080, + serviceName: "trilium", + }, + ]; + + return { + domains, + }; +} From 6688b14753d3cff75e346e8e2bb89cbccdb4371f Mon Sep 17 00:00:00 2001 From: Jefferson Carlos <31220819+jeffersoncbd@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:22:18 -0300 Subject: [PATCH 009/126] Create docker-compose.yml --- apps/dokploy/templates/trilium/docker-compose.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 apps/dokploy/templates/trilium/docker-compose.yml diff --git a/apps/dokploy/templates/trilium/docker-compose.yml b/apps/dokploy/templates/trilium/docker-compose.yml new file mode 100644 index 000000000..c80c2acf1 --- /dev/null +++ b/apps/dokploy/templates/trilium/docker-compose.yml @@ -0,0 +1,14 @@ +services: + trilium: + image: zadam/trilium:latest + ports: + - 8080:8080 + networks: + - dokploy-network + restart: always + volumes: + - /root/trilium-backups:/home/node/trilium-data/backup + +networks: + dokploy-network: + external: true From 2c65fc22b0da814fe7224b4b77c13c1011fc40ae Mon Sep 17 00:00:00 2001 From: Jefferson Carlos <31220819+jeffersoncbd@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:32:45 -0300 Subject: [PATCH 010/126] Update templates.ts --- apps/dokploy/templates/templates.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index a2d28d157..0a37c473a 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1483,4 +1483,19 @@ export const templates: TemplateData[] = [ tags: ["forms", "analytics"], load: () => import("./formbricks/index").then((m) => m.generate), }, + { + id: "trilium", + name: "Trilium", + description: + "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", + logo: "trilium.png", + version: "latest", + links: { + github: "https://github.com/zadam/trilium", + website: "https://github.com/zadam/trilium", + docs: "https://github.com/zadam/trilium/wiki/", + }, + tags: ["self-hosted", "productivity", "personal-use"], + load: () => import("./trilium/index").then((m) => m.generate), + }, ]; From 9de3bf3c32766bf59296cfdef92cdc602efd2d79 Mon Sep 17 00:00:00 2001 From: Jefferson Carlos <31220819+jeffersoncbd@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:33:11 -0300 Subject: [PATCH 011/126] Add files via upload --- apps/dokploy/public/templates/trilium.png | Bin 0 -> 24381 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/dokploy/public/templates/trilium.png diff --git a/apps/dokploy/public/templates/trilium.png b/apps/dokploy/public/templates/trilium.png new file mode 100644 index 0000000000000000000000000000000000000000..f6afe82f803216b790a16658874b018ee7931447 GIT binary patch literal 24381 zcmeFYbyQr-)-T$PySvk9g1fsl?jGE|gS)#sfh0(9Cj=6LHV`ZX4H8^~y9a%pz4tk1 zpLf6a-S>@g?|+9e7~QMZtY20ArqrCZR>x_mDPW?Jp#cB@OhvG)761T*eFXwgkYFD# z+$(GW0Ln*y9Rp7-3m+PYyPK_pvki@>AH;^n#@7LM(|5l4wL|s`0r#jUD?(GaGH3%u z;iOOILjB3*Hb;Hi@rryWV`tfz*~QJ}``-OP>dxRJGXd0<<);T}vz5qhpQ{ay z{u}?G$J^WcR&(brvmGbIXMslt*R#l-?3wGc@n_rHm(A}cjURqaT-~;MJo;BY+(?v@ zC1h#vglz3NvAsPuzW9tcRvWc^boZ5_`0zGS^jiHopS!=InVaRT?z`xCV7{ZDtl zV!F=nsM$U1{TUHfItSvtZS`u5KDZW_yu)HC zCF(w@sN8aFo-#5%^lF-N|Db2&QhUkoe=r36KC?Tp?0;+p`QDv{TtL-sl}Kur=Qq## zVU+)lBH_%?Z|-?@-N#!pk)uuir1s&uw!_W=)EF({Uq7Ce=X>9h=4iF;ZaIBg^j|pi z2^^c(*9#e$KhC|IHAFWvOyJNQI#qbc=QFLlTwn-a{gTPUP2@qdp-Wm=SnR%KJSgeDhX`(Yq;u2>*PHw{>fF2D_RoutTFdLlxgJVOAs`R6$~!iXr&pgO z2Iq-LYjWPtMjateGtH+6yy3Sbe>d@Tu79mBar5L`lr~+uktvN8xF1t9;t_gg(XoW~ zF-1p|f^55ql7poETDT-q$Zz9wt+BXk_F?ONhyP!#}Pxhx_0WPXWn5yQ|N=G|-1@JKuEec23p zeD2c{tgWBmzW2iM!fPz4GONjT$F6p9RQ&GBghoz@dGwLUSpD-km5*ICt5xpjoG*k} zhmBB`gKWBjlaTfJi<*q53(=7M)bN`Nk1#HT4A#Ew+aB{@F!u z_~p-mBOOW_oZFHT+C2Y)JIdsjmGUtF z3BdrB1-DE7vBLTFPIBX{Qr~^i{ermNG`R9EYO{!<@;WfiplB){myMGEVD&l1XO`;R z>u&0c-JQ@SJ6f)9vh=VK1*d;wn~qEF>WR{6Bk zs&sMrcb}^njIHy&GFYQ99!cyFz=^G5`?G12WSL*|HqTeS{a8Aoo9L%Q&gV9N9a>F; zi1$tXIX|v;8lEL9{-WukVX+U5@3+$Z+I8HhT~X&*6ga&DR$Rkza^0xWFS?5G$2M7q|b~@}1VKN5ThJw*G1yNs@_Sy0Rr<9qJ zn+d-nv`?nzrel@k)&9uDYFS7KTep7*I;WcM9dwoHwC0O=^`7FrNRdg#;H}c<=`dbo z2TdT=eHAJ)SAZ}ZM->lZJ4tMa=;HCd9Q`KhR@F%98AAxH=xhj%-3wbb~w|lAfX;nOG5Vl5wU%2yF(M}5!g!b zLHlMsU7D!YCDevaspkxZ4*v{^Dzp_bsGj6}G%sC{$Sz%xbqawH5sI~F%1)uK;=>x? z<3`^m+q^mSB4;@dUNcIlvH!fkszv^06h|MP87x^o-EM0mcmHNLoo;ZAFt>4v{h10S z;+*S`%$4j(v{*I+esxI#9k#f4!sJLGO?$9<=z<^r+wFIZ$dV4cJjocK4fu^Xtl9%6 zLTAA@I?@_INk8&cy|HG3K!g@gDVw7QTg*&HuW=(iX^#Adpm$;bOcly zI{pTvbP-hgY5C{F_F0tIlp!!KHde5Gr8Q#zL1~b*u3`DZ!V8D*;U0Kl+@kmG#)b`u zgx_CJr8}av6bEwDvF>doGs`w6@JqQSW*5s!YkyaJL43P0rI#N&jes zQrje&DXY*L(E#XCtF#QuYQB-SSqZjIL`AJI!b%5P!ug4DCbSx!yd)82nHF5H>RT$b z3-6*M#9QlE9<@m4dluDXh&ck>lnT(eB7pdup-*a$$>Jq4C2h2DLj&U{zQ3)kn~KX% zO=PjL#;t`b(Uf`@5Xu-jfk0c>DvY+Qf&09c{H-Nu{pl)2SVRm&DM*Z5IsywnEWjftJg$a~U%4Jocpe zMeCu(l)H>k-h>4svtk6s}zj#ld?Gz7AQ&x~pGDbn&Yzmsk5DH4!w3>ffCH(L^Q@ z!uwvpg=(>@^0D zOZl%)@@dTCb%;XX^1YYRTb+Z3fh_Dk#g{jzr@sA6g3LhW3rb6I&sH;Vk}+!ieKBLn zj>dCx)-Q-wv%>aqgC=?FOIlp)y8xkA=p#xkRPFOdSv6^1VO>P`t;r=@QEczWQD#G> zfe=j>svqDFYXbL9jf+v`L47EZTvjo{tH307%r zH-|1RN|Q;Yy$4w&>fj~bhOR1pPth;H!4?;k@+IW^Jn@Yyg~~wKN0p#&2dhs+n8HX` zg{od^VO?xf7Fmt5KfLjwL{k2h$vtn2$1mG1`tqM$jO|2)I?E`LLn^*R2?T7us5H)& zR@kS`yr7}5xgriDhMBs>)o>cWi8)Uw)-=3GXkSMU0LmL`Uy+2+(O%ixLdmQ9+TwiN zebJtVn3s}gIjmkd&)zT1Dx+E`m)eshKG}|08IPC7T059uwRdE$gaFCld%gjKx(wFh zC0kh4v?P%ce;EGlO5~oSH;9wvWh+CF-;3U$kNHKp^j~C;i>Is{>+F#{PRQ&uOXal1 z@xUSk7`TtXuNX}s6$q-PlN*v;dQDspVbKvPD+#W+m>A^+zAvlFlsy|T{1cbE11YCv z3CMLOBg&9)AZAfxxN{!hlhxU<*%K+6`__zkMOHh@pc7NBHFA=F?aTY^wYLYFnm35My;z@GaqTPr&OKk5y zptg`?E|wW!>sdm@&F9MGkyS90@O=pq=)Q(_f0usTc;n`@$4#KaY{~X*@sU3;zB}al z1j3~oO#Cxy+xuoq9)xN7BW={ex!W&m`3C>l8=G6vGjz(7ldf|#F ziqQL1yfhNq4)nM!GV)|{ZwgvV`Q#CKO_76ljN)M5~|A(C!e zIeQX2KohA z@*OR0Y*6_VEl(!T+LU#NIc=1|7go{~UP(lqNH+6*lN4HONH-7+0u11;&RbrB zop2*l1M#n9Ym>mn=SD~$aj`SnNGIxwyd?yn-u?0m*Jw*x520~6sld)(1V0mho+k91 z67!)l#95zy_<|f68pHIwR~-e9k6^-wH$6nEoRo2SnhwLV#1Hxm7qrC{@mb+*X#8Od zkx@}mv*!J-hC-p3Z3mICFyKR87xo3E7RJdguzfqOh3#9$bQpGu`Qhn1YEkye?#&y% zDsYRmPz?QCgeg#41-IM5^789c;@!KeG%8&YpF5rjq+9q`s(dz6gFROo#&INrPfjv6 zokfz86A`IoZca4C$URGEKQ0`rk#{#(CIJENF|87NqYd>YU3@drb@~`MaKJ@nG9!f= zqzftYD*4I5Y-Qp#W0t204|zpC1zAHRr(Susd=&yLda0X3K*vtXb4bsiqpmxz%R1)K zbj|wNb&C)5bX{)MKD)6}Dai{H+0f!nUP*(11V=?G@x@p4$d_vkHTSGY@*i5_g3Yu+ z;r=meX(;6;%hMQNq~CiVh+ZM7C*POE!%4eqJoX2XMW=sco8|38CQ+Mn-SS1Py&+(! za&eg_J_UU$oS3m6pP(Y!;LDCQ9FIf5e3}|JuN(!1MwL8a-bXDOplp_5-`dTgU#2@?78el{Bvw<(pnCFY+%nk1SCrXZ192+lW`3DBWQ z=(PNqp8dTE+b)sQI51Iuq9&{d<5&6{?$~-&oJ2kgy3;Z_n{w3z-qg=`Lxh>F#_z22 z`?nTnG%00_oFo^h&c|$+QX?02p=BWpEM&<7EzI3TS?=%nk@$zT%5oJEUwlDBE0h#- z%mI^;TO8_Yk!{)6QQ2Dz609C=;a-kg1!_~dOcezW-1-E(d>bS3BYE*uGIV6HmPS03 z<7;H|{%WX(GGGN#^Of(XWN}_{bg0+K6(nm;Z$^{$rRAqAVg zBrkAN_ie;lSn3k7gF*|G9*`LQN`FIUrzWbAZwDc2Y*llYc9XlTh)sq?^VP1EB%dKm zxS8dplAst{o%zPrTIzApTbzAzR}JU64NQhGF1;zf9^yEitC=}=vwbZ+-!Z6XfcM;!Y z8T21zKG{E!>4NsjZpN8kclIG-V;*B%BFxgwIu+lYij}~*D83#n-V6OM_3T|9U@CTb zI7!kTzFtw32%AKOo|&!Wt;vfx`4CqJZE~!3B&12Fd3j>1auCZ;)Jx*{BB~i)9 z=x;v?FmZRwyq`DQAEWidjMNlnHrpLh(=FaiBeZt`Jrw zcoOy{6^HgY&)}qm3{DlVh!*kt?ow>IMAcs;!}2~GER>-9a=Kv6?@Yk_Qo~qgM+SOM zg1e-xNs@+yN_tk_$(U*jMqkQYm4*?+R?>=@d^AzGPox@+^YW~Yp;FTt& zp|xK)6HT^#I8C9yr1ypuNlKz$rw@Ke*kLy=)@!gRLg&9t{_1#Jg;|`j^yO`2X^`rn zo@S-n-K{#!lAGDmHL=E^Ia$BMvSyhX(hJFyP#MJv(zl@TNGB-K6sao(j;IItGhlTn zN$x0llvZQE`B<*8Pm1iVHWsn|;@AK&Q$A&Bu&eWF_$#g?kTH)y9!7;6yaNT@z8eZ2 z35GYdnTY?kldV%^%Y0wU&kXY96wboT+F}bSb{V_kJWQ1^h>oy#4SLEiWvY5=N^0;b zpoh0zjI)wn7hj#q(Ufc+L3MyK9fbn5LM>ZM7sZ}t6JPf!G1NOaR5IBuZ#; zE{dG)Pp(}wUh5^i(W%9$abh56i~*-A0Tx(?n@I7}^W`*BC%`Tpbb_#iA}F;06b?5W z|3KD{nJr!%OU{b%`8sLLC@*Qo^O>oq*wNNmBq_0$7c_Dd)3Lt{6A$x( zHwHr?U;!;1`qiqkFKS@__jF(M)JyBLCXZS?*}Z|e6mXnoBCoe{OJQZ?XG zfTh)zW<*?gfCVwqF@>)AeI6nbdp3VWn&)|Y7HBU`Sx51e9MoiSfASQLk`Tbw zELYXB$!_0^p~=1t600Z~@<3;*B!!EN%7RitLPuv36IHQxSYFgz<(|;j=ogcHL&kX~ zS6K<68ZQtfxRG-+$FwTIOx$(r*=<5y4E}cZc&2~7fZifrMMTcy;K7E!8&k+at~2e2 z2TvvNW`7Nb8B))!mBe5uPcDB4*_(=!}qMwijO=t8s=q&DLPrVLi$}Z!N z;ie)+J@j4AWw?F-x3Cq2^Xq$x{!9{ZPApFa3MQuUS2YwG2I$JP`SEhO;6t%vq=9Bl zr}NB%nO^PB7~ay^SNSHDoN~d36xD7#r_dTmR>`8%S3$#bJ`(<-a_rPwA4=1qcbJc| z3(p?i3Psck06S8CrL!fugs;xB7mc|hEcQlL_8xYk`R4Hr<>u;z+cY~$U%X;;=Ei0R zg8YI4os+uVZu=`n@%b_7j1txn`I&6`1tqe{({?bCm=Q-1h`s$vLB+0lFS_x|BXb7r zL~#Tfv$LT36cNQUX6@IV@V}Ct?~2`pMaG$szd}vteZ(t;Ly=rXATLUruPdW-)c^6w z8ed%)Ko+vb{2k_}ZXBVzan>qITaZ+!TMx_R5ogHZ2RmQ13eP7Po0Pn4ASW7F4 zBng&;d$DR7sOV-Bp7JYtYbfk_BG{|BlrgxZI0lGZA)Et)3&ZGyl9-vT^5hu~UQ3-k zI8_DH)AABFf11YcwSDVWVH8GMqd&ew%5?;hlTjXbFnc3HWfXz!!X5Bd40qa7m+@z} zl69$1rhbd6LJVIcVh|S|mJ8ait@;Q&8nXTUSMvu$3&q|Ik3Oi7CuVcWtrqo1ZIHk?&C33U$$V@|p#@SVaaClqyYJ z(r~dROV$j-Q>1rF?Gp@*K$Yw)JBw<=mz}m21H4oUssbb>&3;G zq}lmWCMuU)#QH|wIoZ$eUl^XFXR>nRJD`_MDr-^Wcp_bIN{#DOg#7Y*8zt%Y{i_Mn zSMB;bpI%S?Z~KDfG-T@{cNk8&TP4*y_rz0GJZ~$WvN{j<`a$Wul;6Vc5*FqOqP}(O z;L@8k{~QHsZ*petH{(+d@EBnS5;ts1v@!IFlOt2^yfnWgOlw~9i;Q&Z^sKQRD_CO> zZ+P?%JV@zk4Y`gs>so)W<0Cxn;UW;4pf^tS^QB>8QE|NRap4>|wE3b4 zzKd21#+Dmg&XK(W31{K=_;{1x?sDI+FXa?OQQ-QNLd;Gn1=@sf65~~NjmPd-{C{n; z9XzTXD;!Qu<^XVrEcr3NPV7Ww)Ak9s$*`vOB2cKETmV0=ZkC38EDf5@E)OZ6(n#ly zF(}oVm?I<;h#Vm|^Lkh7!F?4E!Ot%U&z!nG zY>M8nn=k=5ZtuR&Sq9BMiXT3COI}lV?m_M36(Z(I>^UMb^n>wzg&DDaeR<>uH7G9Q zV)#8@pQ`nd7R&5X{5EPd|8~-_MA(y)icy}US=)66pck+SZx@>j$4CikF2<42d!&>q zISMw(QPoiOy{?<;i`al8P1M^T3mS6%S>KF(2=m3>$t{5h$k92>MvPw<5n8 zLBpjQf)^s4p?{ty%v;4VUl*lHeuYQ3pE}jFJ+Al}y>VpKOOUZx$*zo$@>tXps0)AcH2-4?18Y|fB7WX0pe-(`$@1^d^ct&CkFgeZaA)% zhz7UX^i_8X@*R4$w4jpnFU#2n&xP!D&it@J5~@5i$z3eHKKb!xM49~k8M(F{0wB4M z)mJnVY_q;5;_GL`ZrhG@1+XX4kq$C48j3P9|9JQe8|Y0?ni#lOlA_P-yV8UG%Z7+p zndm)hDV;Bx)f$m#MSAwr-J6pf_LHe4ErMuuh{zH|)mZGKt->v6;j758uU}`pOUS(G zDj5gOL~I1Nw_UdcAfsGzf3JZG7Vdt`Rr=DW;;oC(`3@O)Ath;Go6W_fsOU*yc71Ys z$=?4#xZutsa{5glpa3j7jPadunI?a^33K%B3$9+K?z+c1R|Y%ye$cCLLtqp!2QiwBf&I?8(kd#lnU!-=Wb^4Q|4GVRXPQ-h!FeTFRhhUfYBQAKY$8^3ww^@7cDl~ZY? zr3e`^E$HN{)#9TF4hKYTVqSE~vPZpO;B;p#gu`syM}CS}Su*-Hgv#-Z0J+taV%rJ< z*2w4rWoHG6z0NqV5&6XtAVNLb`D@)~`LoS) z=pAX0HW5h+qW^6Ba^9U_5MXjj_Y6r;5J4V2vtxN}Y3Z=zA)st-1$&g+-3I%hbPNE% zPH=#|3}B$DB5dX6!fs*hW@*Fj>jHt<1^^%`;R~^_ahYC;q=l z|AVhT=JE%x!m@5wUcU!blobR0p0BXAo0WsL@E>2ft@*gP_^fT%Yy`N4*m#93x!Htl zY%JJ>gt%?EZMZB1IR&l%2Bqlg;c4M&W%C;f2F~sP!?6++uo1H5;bpV32i{5K{SCp-6_leV@JmWLr)z>Lko z#lp^p1LA7;N0;Af5r!=WMy$ne-+&?i(GFXSu#CHng{Palj+>ja80hz0X?{cg;ZiiB ze>sb=iksCR9sdxvvHtCEe|em=g&oHqm!cg1o$&vHq;2o!IkbhNSZ{O@u8E8%}4X~ANhho`%r;{Rq*|1UYwKg|^k>+9z3_qXx2ZC?I)^=CkG zcKCxT8k#?XfUt$tpY(fJc-#DGt1uq_yk%u?;c90Ci?)Bc+CSPI{tGka<+A1D=jP&J z6R@>}*`A=46`Li@GuZenta+`t1bD5iY%Ts!@ITQ#+-yC4EZl9R>|izk(=*Jq{?IcG z^B>mt?BAt*>}_B+!otZd%*hG*Q@u2z9KQqVKb9x@JLIUU3jdt|(cd9SSn;>zX?sB+ z&JH&2|4z(54CVhr?r;77GRps5_rHt%*;~d9;s8&?mv z|8DAk7x|NxzwJA)Ise%P+s|OzBga4Y$G^z~1Vg{rwM=W`6ApYee=`RFy;iiHb}hf+_QH{de6?>M3X7DdXny zyJ!OZm7df1IC$FA{Lba|!?1_}02+Xztdx%L{NY=_H^yGm{arp~-@RKuWb(Vf*?btx zYz~F$M5_X&gHg939 zQ!Ezg;XV2Vup?P!sj1N(=b{#D>9FGP!;B!~fhju*~q zy6uOaSmL3UXaa8QG$hot5ZM4R5QP=E3|xo8nbS597*L|>@e7dvNuJv@yCGcx#IY&L z`UR#UQi6$5tu7IroTwY!{PJ-bZAB5l}+1o>*b0YLG6bL2On1bz= zI)b+qMjz*+1GE`zx}qV`nMmlV=4+Q}<`(GIQ3_+hlKjcn>uw>z z_mwz*(*_w*|8sNx6Ay@KEW);34Xbn0zBTjs&FioNU^j@JxjHNb$BWvy2`Q!sS_icQ zcniB$>zjTKQcUp!AaNwENagv}6$*}s4rLu6nhP)hWR485MNwWH^lbQ)U68&~wqZs< zh9f4NxS0iF1A}=U!#NQq3|dCQjw`T!YrSwf1P_AB`5qht2@Al%lnfy>tMY=P$G>_J%E6gpF_!3oOM3kAVWfmFiVH&sx*h z0-?WR=g0hIwITZau{p^l_wPSmf}fyKFn%L0I>NeB9q)@nseu9e@y+zT$4@cqm*@5H zb<)pKKh<(Zm?5;s#(ImEx<1f*Rd*pARLs_-hwnRuR);0fb?+$ThQlMtJnuxIsAbFK~bJRP7qIwz(!CVIvi?t!Nh8b zU=B7}@fGd9bq(o<*a1@0>D zDqBg3H?r)vO@pP`l#29)&nP$SSd{S1XvSpYsb4JiX38w{1;pU`~HZ(D*OK)sVFH+~KgG>cN{8c~2ZSvP88|n5Do1RK$x-F?cldr(brk>n4&JgdLwhZ&!~P9< zG#R1T1L+Ql4glY}cl3H~I|EpN2gP`JvRpMODR?kX67fya-CKVmMCIkxCi_%P_V~zyirNbGgo7UW zQP#bA_8bdfCQ090u6OGF1MvuM6c)POwr*|dLjCz44cV~R%i*Yp&jJr35c<8@CT9(h zzD;I@v?>&k${1O=A;5g7cwY-+C8k7Jz1qDOxia?#9ij<~7jMRpr&xhsWW$pBh)@#JCv~5i2v@#>sp8nHT|qfO*ff3 zZ7)ws5$7C)7&UaMM*h^XO5QX;Vc6C`@k8opk0W+yJwh<*i`>1rDK^+Jos4`(E@2Mf z_x)QX`&l^IaL28J=j*g9#ZVq-+attMmF-3Sl_48u>n(QEc0SsH z+{F7R6NY_{ANS&?=*ws$`*mweu2LBuGUE=qYt3e6|aarKG~p%eR_VY;o- z7%ny@@O$dVsi4&4a@YbbTVQU+`ckG=r+Fy}O9d z;hp;6eKAR2an^!abm{Pb5_p2rCLzko55 zZhaXFRF@v zXq*|3#@xPqcyb|WDcZ8>m|>C)pSa^8dEy5XuRYQJ{Mvlx%oKVadfv|e7{3AsQ>|?Y zt7mxU$El8A8uSLUNeKHy`@s;@HxAK?42Z0fa0u*)VrHrFOKC@@3h<0{Oy!b#Fis;D1|Kf!$7R_^bTI&gS;%Ao84;8 zY3i{NY`vO@;s$qnmuv#dTItc>2&3zkdzDj9f9xa0qJEQ^jLk>@yY4GBHQ(dpzCTF- zbLKH`g}p)JyhZW?V#|8|ET>DmmmaLd9@_1SY;w&O#%VIOf+xQ1jr(*bbX*&TeZTgl z3a{=l37j!JL?{Uefv)Yh2)6cw(EjvM4%-i1vE~MtT)u4*4Jl?PLf#>=!=`UB0M|c~ zF;voPx+G1d3d#k#Zh;(Cuwkmd?CV5TBgG7%PydMSxZ1CNIP&Zj`kO;+ru<>ZBNMIP zF7-`s+_kbSV1c%W$XFlR{7a9X6qEdvl&f8x}sH>2>ag9TAs*})CA%SR$u>V^o7fSCd29%|y$ zwoBw=8*LxpHLqzCJom!M2eJiH(=Yqu2?zpDaElbE@+)kYNhDg*z0?uk;w6;zA1$Vk zy%FnDVgk!yR6Lb@{=6Ty5rzxX^y^2+`dd)lWA91lTp7-+zh?YtM{*PVO15H$d;pOx z&2HCJcd@Et$pXlxbExnHj%H?g0#5-!J_i)vv443VLpL9UpM~?uHp2b50u<2CKKXP) zy>z=s44VSWY!XDt{6;2?f6ODTJ1xuphJ6x!TthZx6LnQNuZo`P>@ZT+FJn^w8fbS# z%ySY7@RklQ#(%!*9|CBCtylk|&4mh8KIiJZ<8|uRTjf;1%g*ar)qo@A=7+=h6mSa*l~&b_sxCVhD!T*8sJ!FE5lL)K~VtNx|i%z$>HIE?tvh z%5mSnhq2{c0(-ZC(DlCZXj5$ub+MAYcpIGy((3)ZZd(6&G9Z````5=a-%ILu1t!n> zxX`lzAA+`rz#0bux)e-g?-rKL=sKRaHTyK_WWK5>uOUHKb*EVDNM$~<&~dBpezD3$ zZ?e@q+zv=vO5Uw%2tx9?L|UZ?5q#f62X{gz_K;Qh;fX+NnqOe}+65SlNFnW#RUJ}z z^kf$Lxis;L_$`39)~Au%yAa8BMkZG2z;AP=f}3O!;YH!m+Q98gN)CATs~G%!QJT=+ zw5?qCezV(D!;qCYgyMReUv+nM5v*5*-tUnnj@u|($YX7l{PaeMXsFIyQkYv!b?N%C z@mfT|CZ9fuJ|578i6KKM$AD__F^qmvlLf3FPV3^#a@>f^gWm#$$?Ws;-W8xbS2m%kbZ5R5Sm%D?#9eWpGM zObqcTx!cPja1#$KHAk2@n&TM z3(CeOeD}~l!F93CE=#!m-ZHg4yPjO)1sKXE8^pM!w)|zo=g>DBKRtoS1%@}niQ|hay-K*N%H?7%=0*a!>~g5^AUt8MD|n%PTlg|bE`2B(TcvMvt|r@ zHh~ejPHU#D224yQLVhO4Zu9(5Z1r_rFWat`i9>%#tACo+{(+D`zG6hDHmX?}E%L^S zdQ#V`jiMR4KCY$MgH!huYetr(Y?A z`v_DMdxGPRXSu*GJ+%?{cM}BaV-l1JSv1G5#LlA<`$H~j957ps6u~nU%X*Jg^oIg% z>7AK8C>s)RL%M3GJFkLT{TcO-tlxfoeRgMhi8QklrR)z+z4Lfx64d84?MH18*DL&D z0O%7-_CP|9*4bPa$p%fnJsvq z7?8oQKfxA#Zy$(?;>e)I%2zG1F^jou0WLD=AeD#}o>G54a`ORCQgl)x`Z zv!9MKus~1Ul5f)O(NT0WfUNR!2<~E}5~No8Vuu=*>?DB+{1xa2eNyovQoh}~y#^}4 z_SkY*fb)vYn>fKMxSoZ6rra$ai>^%-lziVHe|;9R$FW+TKlm6M{j@2}`&g3w6q9xH z^G$H#6OF8h>jK$lR41<#GP%8DYM`k*BwH1$YrE`iK*rJQa=H(Db~|2RzLJ47+j99; zQ}Zsof)w<7j5o)3-#bI{*4|>>=dW7>k$%p6^l3UsBuF?kjOl+uILRtGlG4S2t?Lc} zG}5Gvx$-Kj)!(}cC=r3;>1wTlF5QWwuQ$<=23QQ+!(~IpI*Y;oo!x#0hiO!{%`C%- zza=2q9k^kiB5Z}dk|M~n@U@JsL!c^02i2e~q1%=K_CB=VY9^TT)|r94CzW@jAX3L> zy74r^8q0fjmK0hIeK<|!fD_%uX3~RGA#!<&f||f$xv4Dwp1Jke_*E>y{V;%nS*otDirU)xrgAw{+fH^o z#v1o%;$hzdmhiiTgiv7rnopyp|E_cotw-)$$7T|~r44E zX3v7Yghcs!U|0EEahZ%OU1D@0ywPOqACn}O+kaG-vhMg;CjMTBx?(@<^CRS)2)YAR zRA$SOp){xkS#X(J?ZVkQGTo;OcpM#C1FI6x!pIC@l_0-Ncm*<@!*|NGQ3VbjJa#R* zMh%lAHIqXV*~bH4G1p3>`t9k%koG*PqizB?A#nVm%F>-$g*M0_Zu6lYDEtAZI~%+L zo%JHuJ;;x{4wHdpMdxyip~h3qBMys(u(AlHgxll!WKKhMKL;btbm&McliRlwQO1Ox z8BpayDY(9yd~aDHZQef#G&3kDH)@h@c>+?Fiz!K3PGMO%2$tvn| z?P_u6r0;gGoG46B%+euupG_C&q^2|C!Fkv~g;IY8VToe_{{!jk2PRnF+SXWeh6zp^ zb2rbL6+0r+p-8ajIZ7_Ey4F&+^vykbGLl_9NA4q8FnN!VdS)&_`|Fe^GwKtJ#Ms@B zt4OfCfH~p1i5f!7O*FF4JB7FUyGpUlTZ>HNUJ9Iux!8ZAd9CR*e(cCUrQaSY+q(e< z&M36zF|`TYT|~L2k$IwX5;&ZY#mF9gnmF7ZZS7H)CVTUhGV>9j_|o)u9j|-dYkU%= z1Ns54=`9rhtvKI_G6uvgKH#7d`4f_MQ3=vD=e_SJW~gv=6u-9Mf=I(QC;Eg_ zf&H&f)50yEmXtgtYG)pT8GUUFVOBg#t@ce{;96sq9PDT-M~MqHpaDZkG4mEkZV8Lx|-}Y&|LKQG>RvuW>0rHuXbU%om9ol;x}ct2eIn;RQAFuH-!_w_tPX-Fuu@YuHT7V8fzvTAQ{F_xqa9 z(3kKnk8Lzk)nm&yAG*?qB`{4#0X{O}G1_VOCkFJ)+0Gj-((%cT zMYj#aH%upDCYN_@{Nk&~;BMVc>1r{k9js#NeI_rAi;FPCu-rf)$OSu+v!+%#iE8^7j}a`vm8#qa|gMIi<3WHiJDwguKEVkNdm`u55HZ-fGb9NbOyY z$%ZS=wbR42xEND{Mm;nzVFXD#?poHSC-Djk;SrqPG+o;7Oz*pSoknwf#pmvpO-q(y zhL8+9WD=OoIL#S>*XZ?0trW13jjk?2!hCv3kK=2mWkvJzUZM1fH3}skmLM|Gk@+7A zImhw)P?F6Fo=IR2b)`_Pc03{3Vx9;T$VF;a5e^A{cLEDtA3{A~$v}*7Un)11h>*3l zW0J}DYp=^rTnDmSw{w+yP(eg5!K&bDgYV$A@#>LE9z3k3I)oV;l9n8?HDn&6m>XyJ zHQ4FA2{^b{1v^uqAKp7w*t$=u=n0ri@I+gVuXx^d0FC(&p_wDBAcVTgQ1b10>q+%ZY0x=e3Gk=OuvcWx2_lpZVw}uHnPRXh&Fb&rYk;D$Z>xkJa1MtZ-bq zc#vqq22y8Mk9gt5rIzkgj!oip?roIs4kIw=E2E?us;AIjl`e!Du$BLMo(IsPg1vUl zk6UuHWU}w2n@AhqNIP%fOnpqLf;0T+^-)iMrC|Q|@ zT>j;8itKVZEoCUY$+lOkP0Jv)T6;kh0y}bNwvEUMGl*b5!xm~xr<<$I-EPMZI-Ooh zfrSOV!$`7#tGf$vZ%5Wu2MDZw{(YSPRR(S()>p0~(l}gv$*L=+;-IsvZc9s=ExAT_m`L?YG)sbB@dI> zy_?j3vVv{q@P>L%Rycn3xO~pYPSY$mumgsmew4x&^fXe9L23S>?lwp-_$$Wg&4##a zG508WE7Ju0?kO|i@awI49~6wj{~{uWPZm+F zl(gr*vs_1%J-KR!9buHKX}<3hm^ZyIY;Bc*wtbv)?52e_SABc4dn+5}pzw3KgX{_$an7jOE?vyaV5Iu-63PWTum1>hkZfW3KK+$SffFA35OHNUb1^?S{Q;TI zL>WHH*|+e4X7xi~!%(3~w73tyz|e-S=@2w69}VT{Ty}1TTkF*WSd>MI-rH+Uhzab) zH3{=bWP4Aw7kyuzpT6`N@JTtYohtuimP@^-dq?qub_Qj|+v$auPi27lKQpc9PQaZI(w`0?Uj} z=aR*Wat3$xBZ-^#EtKkquD}bxatS%x^*PP|r$6AJw$~G8eXMAs;zv27Cyk7H~bIyIP z?R{PE`&`!?5fvFf>J!dRpm?Kc!0ei-!c49Fv7J8{xqF^ej}Xcij=C>Xue%nkC1yf) zwEaOk4gX(}2W-jYol+&RLEq~!?BEo;N31$a-1NVzR)X7c<4k!4tWG;qpEeMK{BGA2t__K_pVjUiQPnDZhtjj||R z0SmimQh8%?!zdbzrYUwY+*Vd@+4jHQ*|mV`T_u4hegc5KQV?jqK?9j+VKaEL^tW zNA3(QiPBYk@J8df%!cENYcd3aTHT)okVl2$w+x1g*XdcZ!yH9a)8_)xWtVaI%zM`#3ve4=3Lutkw%bXzSjvkW0H9D$dgMNzEM7 zDTuPiR}nNdg>dSFg9f7uuY}0*!JTHvbkV45czzS5N!6Iaz3YNd`)F_=ZXgn)_4kew zMYbZolOFj4dx@jvix)$6)e~4C_9S}GpZYR5RNV+ABxBjCV^t6lCf8@~3;e_fi^Co^ za$~3+I{QfOwA$Fe%N;mdqo(|lxh_tdats+I098u2Y)_T%)TLc>=2W%Wd+rVXY44K6H>oUU z^q}*A2AB`o`e(O(G^ga``M7oym?pJ+L&t2XN^}^?i^M?DTE%P;vV&m*n?7=3!{qu< z4fv?hB*>8&gfl$|X(=Bc-HCsIfEZNkc3YUf)+jdD7xkc`!p63e384GTG&_lXYRNHi zy}-bt6wG7XuiSbFv-uKgxkjWTsWL_ObapM}YhWw{pb^1RV+_qKE}8x7dk0`fr@f@9r>LINWO3I$lGjE<&+95-%UoJ$A!aC5 zv8yBHoP>KsUSe`Z#e(cKbJkwC+NgQEo~GC4?-(s)Lcbb|G1E*@d1H+7>SEpD?B$$} zuc(mWB)LtGEwXz1f@^_kM3`}z+xMWBfXLq&Q-8S9LvQRTvs6855NPd8K# zcp>Z-AD{n~QJQ*IEK=_(!M;~b7wL~->GfsS4NopserSOvLS_$ydhz8ud36w&|4KgP z8+Kx7d5kfn8Ckv?8)hw8-|!k!rH zm!sQKT zCxtG%GVXfJcB^`nnm>nOzWE+j$?SHa6wY$jy%|@#7_e^Yd3?fM0hm27K|JvNNu~82 zBo0GDJb{#dX*QAM8m$*1TwlG;3l3n{k6^4qq;t6*nReWPc9ZTlt8OWk~f z>AMA@r52X!Iv0?)D^@2p_&)aRkxd?MRU*m-u>+5vVl`;eswk`AtkBfXPxdswKTvQn zHWsE2yAGmq48OVAODY%`;&*u=V}5v0eB+}wxju#X$z^;GPGxlvfuLgWr7IgB;}t3nWxjB-?J&t$1wJZ3Y&%tdkdya~)xWNh^=fhXPAn zl-)Rw@&p8hgQ9VQuUd6*Fgu&a#b)9_1WK!l@kF}aL)f28_Xc?50d?Z66(q(zsd#4v zeCLD>O^+SVaLSxWVh*A3|19-M=e_&)Zx}zoMGG#lh_|JlrUp>pN*Ra!kpC&`{8ET^ z>EW|Swh-RiVdDYIvVlYZI7@3PD{MoU!9yL~v(f`^Z-Z5y#gZLm!WR#;yBhckm*&uK zVzuLBL6`irnRYq8|ut7MrHX zu>Xcu7rcS~Thy3Uzbmpr@>~Ok(EH)157XdMvU6hZMaV&2@P9gL9)6G)td1Rhvw{5Z zVrr|Cng96;$c%PQleYS-i`00paaHF;KL~JrIL+sp(YRVl*>!--aBKvFjmimP{*IDJ z#qZ4gRbSOb4(t9NS9g%E7;kb{7BW!AY&dJ1An=*8{x$3;GzaB@uv_QqoVnc|$u|U}8!?UI8|kQx z$LzgxjCWdjTbZJ^C^`XQ=jfCsQmUrQlZ~Nhi)~jCrs5kxh1|OkTdhEk-sg9`2%Cr?0%rprC zids}~d%aQQfpf+h2Cd=2&K;WLsw;OqyR(JJP?CUC`ef+ftoK;TWc>*PP@(wZ#L_P& ze{DKGZwjpAIBCjwX;=w)w2T`Zqn-a|WOz^iJ7hDzq*Id%9KI$82%5}v=aS#q*}kd! zYAN=YA&vY zl$H96wXp4dzNP?sL6q#Os!jYM0M>8ugI|CIr?;VVM&x>TxyJZ+IysK~PbaBKq9WFg zN1BIu?={z60pCHuWN8 z2#dgd;-R8AQfA@yd@(33dS5=!<_{VSCex-;x@Z{UnG8JC(Tqoo9e<_-mT}}EY9UF|_HaTB&2eQwbaK^E@ zNdbb9ZpxNAuh{w4-=) zg5H>AtgTsWiZt{o*JyUxY~*Y8eSW|z!x2F#XM29yrUi-`%@ja!4hg@Ai$PjplEZ-6 z26Db|-cSKEZkioOth~{TFOr850Jy#0zYckB1QxJ&WC*qbYfR&d>I@?FMjNjoLRw^+ zxrOg;g_3Y~Xv3S9K%tnO}h7?#E1euueeM}Yi6RAhkn=*PGGKEzs4~%axw&HPH6-Q zjx6g0eY?bpExoxZ{18>aspZxGHIY_OzB|0nQY0cqR>t$6nauL+8v19&A-4}wwR(z_ zp1Zc$e(D4#;8zeKl*UKr${+QaGBTQovFTjKWc>k(k*{L45V@&#o?OE&oLgJLe;sNK>a24VTLrk59QP#^^psl5f1Cv`^z?6npr0L}!-g*4%H} zGqx?1xzRwng9?-Z+Cf@O`sI69(Hesb_rWUVils;uVS@vt4~dtr0dfw0GF0pVb*Y9w zl^qhl+zIyu(~yAMXK_74dMcc|`t?j0snvZSJ^`@eR9WOUqT$FeI1P3fXqF|zmmx@Hwfc<4 zPloAgTrcKF>I%^8u8h&LK{;#`EL@r-A7>fa4JXchR{%SM*93)Wz~||49=yq?%rje; zl$?fryPd8z2mIi(RG5bT0jy1yGpGmGv=m=A6mMu8^w>{Z4M})ChHJtR)EQ15jMmCu zgFlaR;#`VEfLme@AB;j0V5RyjNdmI;0K9732r2z0$!gz@{3(>^zgtxhP6cf+EAj_Q zy{WPXmqvriedA&m7?;ht*OSN?3kig|M3?}9wmZxpZicb-u{tr+r!}I_E literal 0 HcmV?d00001 From 60eee55f2d760866ae374dc6d19269e6e920d892 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Wed, 12 Feb 2025 23:41:04 -0600 Subject: [PATCH 012/126] refactor: test migrastion --- apps/dokploy/drizzle/0066_rapid_morbius.sql | 153 ++++++ apps/dokploy/drizzle/0066_soft_kronos.sql | 187 ------- apps/dokploy/drizzle/meta/0066_snapshot.json | 486 +++++++++++++++--- apps/dokploy/drizzle/meta/_journal.json | 4 +- apps/dokploy/migrate.ts | 117 +++++ apps/dokploy/package.json | 1 + packages/server/auth-schema.ts | 4 +- packages/server/src/db/schema/account.ts | 27 +- packages/server/src/db/schema/admin.ts | 195 +++---- packages/server/src/db/schema/auth.ts | 4 +- packages/server/src/db/schema/certificate.ts | 21 +- packages/server/src/db/schema/destination.ts | 16 +- packages/server/src/db/schema/git-provider.ts | 20 +- packages/server/src/db/schema/notification.ts | 20 +- packages/server/src/db/schema/project.ts | 16 +- packages/server/src/db/schema/registry.ts | 16 +- packages/server/src/db/schema/server.ts | 21 +- packages/server/src/db/schema/session.ts | 20 +- packages/server/src/db/schema/ssh-key.ts | 19 +- packages/server/src/db/schema/user.ts | 74 ++- 20 files changed, 978 insertions(+), 443 deletions(-) create mode 100644 apps/dokploy/drizzle/0066_rapid_morbius.sql delete mode 100644 apps/dokploy/drizzle/0066_soft_kronos.sql create mode 100644 apps/dokploy/migrate.ts diff --git a/apps/dokploy/drizzle/0066_rapid_morbius.sql b/apps/dokploy/drizzle/0066_rapid_morbius.sql new file mode 100644 index 000000000..9deea8818 --- /dev/null +++ b/apps/dokploy/drizzle/0066_rapid_morbius.sql @@ -0,0 +1,153 @@ +CREATE TABLE "user_temp" ( + "id" text PRIMARY KEY NOT NULL, + "name" text DEFAULT '' NOT NULL, + "token" text NOT NULL, + "isRegistered" boolean DEFAULT false NOT NULL, + "expirationDate" text NOT NULL, + "createdAt" text NOT NULL, + "canCreateProjects" boolean DEFAULT false NOT NULL, + "canAccessToSSHKeys" boolean DEFAULT false NOT NULL, + "canCreateServices" boolean DEFAULT false NOT NULL, + "canDeleteProjects" boolean DEFAULT false NOT NULL, + "canDeleteServices" boolean DEFAULT false NOT NULL, + "canAccessToDocker" boolean DEFAULT false NOT NULL, + "canAccessToAPI" boolean DEFAULT false NOT NULL, + "canAccessToGitProviders" boolean DEFAULT false NOT NULL, + "canAccessToTraefikFiles" boolean DEFAULT false NOT NULL, + "accesedProjects" text[] DEFAULT ARRAY[]::text[] NOT NULL, + "accesedServices" text[] DEFAULT ARRAY[]::text[] NOT NULL, + "email" text NOT NULL, + "email_verified" boolean NOT NULL, + "image" text, + "role" text, + "banned" boolean, + "ban_reason" text, + "ban_expires" timestamp, + "updated_at" timestamp NOT NULL, + "serverIp" text, + "certificateType" "certificateType" DEFAULT 'none' NOT NULL, + "host" text, + "letsEncryptEmail" text, + "sshPrivateKey" text, + "enableDockerCleanup" boolean DEFAULT false NOT NULL, + "enableLogRotation" boolean DEFAULT false NOT NULL, + "enablePaidFeatures" boolean DEFAULT false NOT NULL, + "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb NOT NULL, + "cleanupCacheApplications" boolean DEFAULT false NOT NULL, + "cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL, + "cleanupCacheOnCompose" boolean DEFAULT false NOT NULL, + "stripeCustomerId" text, + "stripeSubscriptionId" text, + "serversQuantity" integer DEFAULT 0 NOT NULL, + CONSTRAINT "user_temp_email_unique" UNIQUE("email") +); +--> statement-breakpoint +CREATE TABLE "session_temp" ( + "id" text PRIMARY KEY NOT NULL, + "expires_at" timestamp NOT NULL, + "token" text NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "ip_address" text, + "user_agent" text, + "user_id" text NOT NULL, + "impersonated_by" text, + "active_organization_id" text, + CONSTRAINT "session_temp_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "account" ( + "id" text PRIMARY KEY NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamp, + "refresh_token_expires_at" timestamp, + "scope" text, + "password" text, + "is2FAEnabled" boolean DEFAULT false NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "resetPasswordToken" text, + "resetPasswordExpiresAt" text, + "confirmationToken" text, + "confirmationExpiresAt" text +); +--> statement-breakpoint +CREATE TABLE "invitation" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "email" text NOT NULL, + "role" text, + "status" text NOT NULL, + "expires_at" timestamp NOT NULL, + "inviter_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "member" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "user_id" text NOT NULL, + "role" text NOT NULL, + "created_at" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE "organization" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "slug" text, + "logo" text, + "created_at" timestamp NOT NULL, + "metadata" text, + "owner_id" text NOT NULL, + CONSTRAINT "organization_slug_unique" UNIQUE("slug") +); +--> statement-breakpoint +CREATE TABLE "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp, + "updated_at" timestamp +); +--> statement-breakpoint +ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "project" DROP CONSTRAINT "project_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "destination" DROP CONSTRAINT "destination_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "certificate" DROP CONSTRAINT "certificate_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "registry" DROP CONSTRAINT "registry_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "notification" DROP CONSTRAINT "notification_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "server" DROP CONSTRAINT "server_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "ssh-key" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "session_temp" ADD CONSTRAINT "session_temp_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_temp_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0066_soft_kronos.sql b/apps/dokploy/drizzle/0066_soft_kronos.sql deleted file mode 100644 index cc627fa8e..000000000 --- a/apps/dokploy/drizzle/0066_soft_kronos.sql +++ /dev/null @@ -1,187 +0,0 @@ --- Create new tables -CREATE TABLE IF NOT EXISTS "account" ( - "id" text PRIMARY KEY NOT NULL, - "accountId" text NOT NULL, - "providerId" text NOT NULL, - "userId" text NOT NULL REFERENCES "user"("userId"), - "accessToken" text, - "refreshToken" text, - "idToken" text, - "accessTokenExpiresAt" timestamp, - "refreshTokenExpiresAt" timestamp, - "scope" text, - "password" text, - "is2FAEnabled" boolean DEFAULT false NOT NULL, - "createdAt" timestamp NOT NULL, - "updatedAt" timestamp NOT NULL, - "resetPasswordToken" text, - "resetPasswordExpiresAt" text, - "confirmationToken" text, - "confirmationExpiresAt" text -); - -CREATE TABLE IF NOT EXISTS "organization" ( - "id" text PRIMARY KEY NOT NULL,mn cj. - "name" text NOT NULL, - "slug" text, - "logo" text, - "createdAt" timestamp NOT NULL, - "metadata" text, - "ownerId" text NOT NULL REFERENCES "user"("userId"), - CONSTRAINT "organization_slug_unique" UNIQUE("slug") -); - -CREATE TABLE IF NOT EXISTS "member" ( - "id" text PRIMARY KEY NOT NULL, - "organizationId" text NOT NULL REFERENCES "organization"("id"), - "userId" text NOT NULL REFERENCES "user"("userId"), - "role" text NOT NULL, - "createdAt" timestamp NOT NULL -); - -CREATE TABLE IF NOT EXISTS "invitation" ( - "id" text PRIMARY KEY NOT NULL, - "organizationId" text NOT NULL, - "email" text NOT NULL, - "role" text, - "status" text NOT NULL, - "expiresAt" timestamp NOT NULL, - "inviterId" text NOT NULL -); - -CREATE TABLE IF NOT EXISTS "verification" ( - "id" text PRIMARY KEY NOT NULL, - "identifier" text NOT NULL, - "value" text NOT NULL, - "expiresAt" timestamp NOT NULL, - "createdAt" timestamp, - "updatedAt" timestamp -); - --- Alter existing user table to add new columns -ALTER TABLE "user" -ADD COLUMN IF NOT EXISTS "email" text, -ADD COLUMN IF NOT EXISTS "emailVerified" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "role" text, -ADD COLUMN IF NOT EXISTS "certificateType" text DEFAULT 'none', -ADD COLUMN IF NOT EXISTS "serverIp" text, -ADD COLUMN IF NOT EXISTS "host" text, -ADD COLUMN IF NOT EXISTS "letsEncryptEmail" text, -ADD COLUMN IF NOT EXISTS "sshPrivateKey" text, -ADD COLUMN IF NOT EXISTS "enableDockerCleanup" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "enableLogRotation" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "enablePaidFeatures" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "metricsConfig" jsonb DEFAULT '{}', -ADD COLUMN IF NOT EXISTS "cleanupCacheApplications" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "cleanupCacheOnPreviews" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "cleanupCacheOnCompose" boolean DEFAULT false, -ADD COLUMN IF NOT EXISTS "stripeCustomerId" text, -ADD COLUMN IF NOT EXISTS "stripeSubscriptionId" text, -ADD COLUMN IF NOT EXISTS "serversQuantity" integer DEFAULT 0; - --- Migrate email from auth table to user table -UPDATE "user" u -SET "email" = a."email" -FROM "auth" a -WHERE a."id" = u."userId"; - --- Migrate admin users -WITH admin_users AS ( - UPDATE "user" u - SET - "emailVerified" = true, - "role" = 'admin', - "token" = a."token", - "certificateType" = adm."certificateType", - "serverIp" = adm."serverIp", - "host" = adm."host", - "letsEncryptEmail" = adm."letsEncryptEmail", - "sshPrivateKey" = adm."sshPrivateKey", - "enableDockerCleanup" = adm."enableDockerCleanup", - "enableLogRotation" = adm."enableLogRotation", - "enablePaidFeatures" = adm."enablePaidFeatures", - "metricsConfig" = adm."metricsConfig", - "cleanupCacheApplications" = adm."cleanupCacheApplications", - "cleanupCacheOnPreviews" = adm."cleanupCacheOnPreviews", - "cleanupCacheOnCompose" = adm."cleanupCacheOnCompose", - "stripeCustomerId" = adm."stripeCustomerId", - "stripeSubscriptionId" = adm."stripeSubscriptionId", - "serversQuantity" = adm."serversQuantity" - FROM "auth" a - INNER JOIN "admin" adm ON a."id" = adm."adminId" - WHERE a."id" = u."userId" - RETURNING u."userId", u."email" -) -INSERT INTO "account" ("id", "accountId", "providerId", "password", "userId", "createdAt", "updatedAt") -SELECT - gen_random_uuid(), - a."id", - 'credentials', - a."password", - au."userId", - NOW(), - NOW() -FROM "auth" a -INNER JOIN admin_users au ON a."email" = au."email"; - --- Create organizations for admin users -WITH admin_orgs AS ( - INSERT INTO "organization" ("id", "name", "slug", "createdAt", "ownerId") - SELECT - gen_random_uuid(), - 'My Organization', - concat('org/', u."userId"), - NOW(), - u."userId" - FROM "user" u - WHERE u."role" = 'admin' - RETURNING * -) --- Migrate regular users -UPDATE "user" u -SET - "emailVerified" = true, - "role" = 'user', - "token" = a."token", - "canCreateProjects" = usr."canCreateProjects", - "canAccessToSSHKeys" = usr."canAccessToSSHKeys" -FROM "auth" a -INNER JOIN "user" usr ON a."id" = usr."userId" -WHERE a."id" = u."userId" -AND NOT EXISTS ( - SELECT 1 FROM "admin" adm WHERE a."id" = adm."adminId" -); - --- Create accounts for regular users -INSERT INTO "account" ("id", "accountId", "providerId", "password", "userId", "createdAt", "updatedAt") -SELECT - gen_random_uuid(), - a."id", - 'credentials', - a."password", - u."userId", - NOW(), - NOW() -FROM "auth" a -INNER JOIN "user" u ON a."email" = u."email" -WHERE u."role" = 'user'; - --- Create member relationships -INSERT INTO "member" ("id", "organizationId", "role", "userId", "createdAt") -SELECT - gen_random_uuid(), - o."id", - 'user', - u."userId", - NOW() -FROM "user" usr -INNER JOIN "user" u ON usr."userId" = u."userId" -INNER JOIN "admin" adm ON usr."adminId" = adm."adminId" -INNER JOIN "user" admin_u ON adm."adminId" = admin_u."userId" -INNER JOIN "organization" o ON o."ownerId" = admin_u."userId" -WHERE u."role" = 'user'; - --- Drop old tables (after all data is migrated) -DROP TABLE IF EXISTS "sessionTable" CASCADE; -DROP TABLE IF EXISTS "admin" CASCADE; -DROP TABLE IF EXISTS "auth" CASCADE; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json index 3ffb0dbf0..1bf9811a3 100644 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -1,5 +1,5 @@ { - "id": "d76ab830-b647-4e53-b6cc-0cf515968758", + "id": "de382c48-6f10-4578-a307-884fecb4baa3", "prevId": "1240ec96-1751-4de3-b64f-cef9cb716786", "version": "7", "dialect": "postgresql", @@ -741,6 +741,166 @@ "primaryKey": true, "notNull": true }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, "name": { "name": "name", "type": "text", @@ -1003,8 +1163,8 @@ "foreignKeys": {}, "compositePrimaryKeys": {}, "uniqueConstraints": { - "user_email_unique": { - "name": "user_email_unique", + "user_temp_email_unique": { + "name": "user_temp_email_unique", "nullsNotDistinct": false, "columns": [ "email" @@ -1018,9 +1178,142 @@ "public.admin": { "name": "admin", "schema": "", - "columns": {}, + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, "indexes": {}, - "foreignKeys": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, @@ -1172,15 +1465,15 @@ }, "indexes": {}, "foreignKeys": { - "project_userId_user_userId_fk": { - "name": "project_userId_user_userId_fk", + "project_userId_user_temp_id_fk": { + "name": "project_userId_user_temp_id_fk", "tableFrom": "project", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2042,15 +2335,15 @@ }, "indexes": {}, "foreignKeys": { - "destination_userId_user_userId_fk": { - "name": "destination_userId_user_userId_fk", + "destination_userId_user_temp_id_fk": { + "name": "destination_userId_user_temp_id_fk", "tableFrom": "destination", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2445,7 +2738,7 @@ "name": "userId", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true }, "serverId": { "name": "serverId", @@ -2456,15 +2749,15 @@ }, "indexes": {}, "foreignKeys": { - "certificate_userId_user_userId_fk": { - "name": "certificate_userId_user_userId_fk", + "certificate_userId_user_temp_id_fk": { + "name": "certificate_userId_user_temp_id_fk", "tableFrom": "certificate", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2497,8 +2790,8 @@ "checkConstraints": {}, "isRLSEnabled": false }, - "public.session": { - "name": "session", + "public.session_temp": { + "name": "session_temp", "schema": "", "columns": { "id": { @@ -2513,6 +2806,12 @@ "primaryKey": false, "notNull": true }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, "created_at": { "name": "created_at", "type": "timestamp", @@ -2558,21 +2857,74 @@ }, "indexes": {}, "foreignKeys": { - "session_user_id_user_userId_fk": { - "name": "session_user_id_user_userId_fk", - "tableFrom": "session", - "tableTo": "user", + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", "columnsFrom": [ "user_id" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "no action", "onUpdate": "no action" } }, "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, "uniqueConstraints": {}, "policies": {}, "checkConstraints": {}, @@ -3308,15 +3660,15 @@ }, "indexes": {}, "foreignKeys": { - "registry_userId_user_userId_fk": { - "name": "registry_userId_user_userId_fk", + "registry_userId_user_temp_id_fk": { + "name": "registry_userId_user_temp_id_fk", "tableFrom": "registry", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3563,7 +3915,7 @@ "name": "userId", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true } }, "indexes": {}, @@ -3633,15 +3985,15 @@ "onDelete": "cascade", "onUpdate": "no action" }, - "notification_userId_user_userId_fk": { - "name": "notification_userId_user_userId_fk", + "notification_userId_user_temp_id_fk": { + "name": "notification_userId_user_temp_id_fk", "tableFrom": "notification", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3762,24 +4114,24 @@ "primaryKey": false, "notNull": false }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true } }, "indexes": {}, "foreignKeys": { - "ssh-key_userId_user_userId_fk": { - "name": "ssh-key_userId_user_userId_fk", + "ssh-key_adminId_admin_adminId_fk": { + "name": "ssh-key_adminId_admin_adminId_fk", "tableFrom": "ssh-key", - "tableTo": "user", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "userId" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3825,20 +4177,20 @@ "name": "userId", "type": "text", "primaryKey": false, - "notNull": false + "notNull": true } }, "indexes": {}, "foreignKeys": { - "git_provider_userId_user_userId_fk": { - "name": "git_provider_userId_user_userId_fk", + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", "tableFrom": "git_provider", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -4173,15 +4525,15 @@ }, "indexes": {}, "foreignKeys": { - "server_userId_user_userId_fk": { - "name": "server_userId_user_userId_fk", + "server_userId_user_temp_id_fk": { + "name": "server_userId_user_temp_id_fk", "tableFrom": "server", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "userId" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "cascade", "onUpdate": "no action" @@ -4450,15 +4802,15 @@ }, "indexes": {}, "foreignKeys": { - "account_user_id_user_userId_fk": { - "name": "account_user_id_user_userId_fk", + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", "tableFrom": "account", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "user_id" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "no action", "onUpdate": "no action" @@ -4532,15 +4884,15 @@ "onDelete": "no action", "onUpdate": "no action" }, - "invitation_inviter_id_user_userId_fk": { - "name": "invitation_inviter_id_user_userId_fk", + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", "tableFrom": "invitation", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "inviter_id" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "no action", "onUpdate": "no action" @@ -4602,15 +4954,15 @@ "onDelete": "no action", "onUpdate": "no action" }, - "member_user_id_user_userId_fk": { - "name": "member_user_id_user_userId_fk", + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", "tableFrom": "member", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "user_id" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "no action", "onUpdate": "no action" @@ -4671,15 +5023,15 @@ }, "indexes": {}, "foreignKeys": { - "organization_owner_id_user_userId_fk": { - "name": "organization_owner_id_user_userId_fk", + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", "tableFrom": "organization", - "tableTo": "user", + "tableTo": "user_temp", "columnsFrom": [ "owner_id" ], "columnsTo": [ - "userId" + "id" ], "onDelete": "no action", "onUpdate": "no action" diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index d3de75645..188a97009 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -467,8 +467,8 @@ { "idx": 66, "version": "7", - "when": 1739173929725, - "tag": "0066_soft_kronos", + "when": 1739425241338, + "tag": "0066_rapid_morbius", "breakpoints": true } ] diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts new file mode 100644 index 000000000..72bbf2ab0 --- /dev/null +++ b/apps/dokploy/migrate.ts @@ -0,0 +1,117 @@ +import { drizzle } from "drizzle-orm/postgres-js"; +import { migrate } from "drizzle-orm/postgres-js/migrator"; +import postgres from "postgres"; +import * as schema from "./server/db/schema"; +import { nanoid } from "nanoid"; + +const connectionString = process.env.DATABASE_URL!; + +const sql = postgres(connectionString, { max: 1 }); +const db = drizzle(sql, { + schema, +}); + +await db + .transaction(async (db) => { + const admins = await db.query.admins.findMany({ + with: { + auth: true, + users: { + with: { + auth: true, + }, + }, + }, + }); + for (const admin of admins) { + const user = await db + .insert(schema.users_temp) + .values({ + id: admin.adminId, + email: admin.auth.email, + token: admin.auth.token || "", + emailVerified: true, + updatedAt: new Date(), + role: "admin", + serverIp: admin.serverIp, + image: admin.auth.image, + certificateType: admin.certificateType, + host: admin.host, + letsEncryptEmail: admin.letsEncryptEmail, + sshPrivateKey: admin.sshPrivateKey, + enableDockerCleanup: admin.enableDockerCleanup, + enableLogRotation: admin.enableLogRotation, + enablePaidFeatures: admin.enablePaidFeatures, + metricsConfig: admin.metricsConfig, + cleanupCacheApplications: admin.cleanupCacheApplications, + cleanupCacheOnPreviews: admin.cleanupCacheOnPreviews, + cleanupCacheOnCompose: admin.cleanupCacheOnCompose, + stripeCustomerId: admin.stripeCustomerId, + stripeSubscriptionId: admin.stripeSubscriptionId, + serversQuantity: admin.serversQuantity, + }) + .returning() + .then((user) => user[0]); + + await db.insert(schema.account).values({ + providerId: "credentials", + userId: user?.id || "", + password: admin.auth.password, + is2FAEnabled: admin.auth.is2FAEnabled || false, + createdAt: new Date(admin.auth.createdAt) || new Date(), + updatedAt: new Date(admin.auth.createdAt) || new Date(), + }); + + const organization = await db + .insert(schema.organization) + .values({ + name: "My Organization", + slug: nanoid(), + ownerId: user?.id || "", + createdAt: new Date(admin.createdAt) || new Date(), + }) + .returning() + .then((organization) => organization[0]); + + for (const member of admin.users) { + const userTemp = await db + .insert(schema.users_temp) + .values({ + id: member.userId, + email: member.auth.email, + token: member.token || "", + emailVerified: true, + updatedAt: new Date(admin.createdAt) || new Date(), + role: "user", + image: member.auth.image, + createdAt: admin.createdAt, + canAccessToAPI: member.canAccessToAPI || false, + canAccessToDocker: member.canAccessToDocker || false, + canAccessToGitProviders: member.canAccessToGitProviders || false, + canAccessToSSHKeys: member.canAccessToSSHKeys || false, + canAccessToTraefikFiles: member.canAccessToTraefikFiles || false, + canCreateProjects: member.canCreateProjects || false, + canCreateServices: member.canCreateServices || false, + canDeleteProjects: member.canDeleteProjects || false, + canDeleteServices: member.canDeleteServices || false, + accessedProjects: member.accessedProjects || [], + accessedServices: member.accessedServices || [], + }) + .returning() + .then((userTemp) => userTemp[0]); + + await db.insert(schema.member).values({ + organizationId: organization?.id || "", + userId: userTemp?.id || "", + role: "admin", + createdAt: new Date(member.createdAt) || new Date(), + }); + } + } + }) + .then(() => { + console.log("Migration finished"); + }) + .catch((error) => { + console.error(error); + }); diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 9e531cf8c..f68978c36 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -16,6 +16,7 @@ "studio": "drizzle-kit studio --config ./server/db/drizzle.config.ts", "migration:generate": "drizzle-kit generate --config ./server/db/drizzle.config.ts", "migration:run": "tsx -r dotenv/config migration.ts", + "manual-migration:run": "tsx -r dotenv/config migrate.ts", "migration:up": "drizzle-kit up --config ./server/db/drizzle.config.ts", "migration:drop": "drizzle-kit drop --config ./server/db/drizzle.config.ts", "db:push": "drizzle-kit push --config ./server/db/drizzle.config.ts", diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 3e19bebe0..bfb03d2e4 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -26,7 +26,7 @@ export const session = pgTable("session", { userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => user.userId), + .references(() => user.id), activeOrganizationId: text("active_organization_id"), }); @@ -36,7 +36,7 @@ export const account = pgTable("account", { providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => user.userId), + .references(() => user.id), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 24350c55c..349428fd8 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,13 +1,18 @@ import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { user } from "./user"; +import { users_temp } from "./user"; +import { nanoid } from "nanoid"; export const account = pgTable("account", { - id: text("id").primaryKey(), - accountId: text("account_id").notNull(), + id: text("id") + .primaryKey() + .$defaultFn(() => nanoid()), + accountId: text("account_id") + .notNull() + .$defaultFn(() => nanoid()), providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => user.userId), + .references(() => users_temp.id), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), @@ -34,7 +39,9 @@ export const verification = pgTable("verification", { }); export const organization = pgTable("organization", { - id: text("id").primaryKey(), + id: text("id") + .primaryKey() + .$defaultFn(() => nanoid()), name: text("name").notNull(), slug: text("slug").unique(), logo: text("logo"), @@ -42,17 +49,19 @@ export const organization = pgTable("organization", { metadata: text("metadata"), ownerId: text("owner_id") .notNull() - .references(() => user.userId), + .references(() => users_temp.id), }); export const member = pgTable("member", { - id: text("id").primaryKey(), + id: text("id") + .primaryKey() + .$defaultFn(() => nanoid()), organizationId: text("organization_id") .notNull() .references(() => organization.id), userId: text("user_id") .notNull() - .references(() => user.userId), + .references(() => users_temp.id), role: text("role").notNull(), createdAt: timestamp("created_at").notNull(), }); @@ -68,5 +77,5 @@ export const invitation = pgTable("invitation", { expiresAt: timestamp("expires_at").notNull(), inviterId: text("inviter_id") .notNull() - .references(() => user.userId), + .references(() => users_temp.id), }); diff --git a/packages/server/src/db/schema/admin.ts b/packages/server/src/db/schema/admin.ts index c842bd7a3..983f99fd6 100644 --- a/packages/server/src/db/schema/admin.ts +++ b/packages/server/src/db/schema/admin.ts @@ -18,127 +18,128 @@ import { sshKeys } from "./ssh-key"; import { users } from "./user"; export const admins = pgTable("admin", { - // adminId: text("adminId") - // .notNull() - // .primaryKey() - // .$defaultFn(() => nanoid()), - // serverIp: text("serverIp"), - // certificateType: certificateType("certificateType").notNull().default("none"), - // host: text("host"), - // letsEncryptEmail: text("letsEncryptEmail"), - // sshPrivateKey: text("sshPrivateKey"), - // enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), - // enableLogRotation: boolean("enableLogRotation").notNull().default(false), - // authId: text("authId") - // .notNull() - // .references(() => auth.id, { onDelete: "cascade" }), - // createdAt: text("createdAt") - // .notNull() - // .$defaultFn(() => new Date().toISOString()), - // stripeCustomerId: text("stripeCustomerId"), - // stripeSubscriptionId: text("stripeSubscriptionId"), - // serversQuantity: integer("serversQuantity").notNull().default(0), - // // Metrics - // enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), - // metricsConfig: jsonb("metricsConfig") - // .$type<{ - // server: { - // type: "Dokploy" | "Remote"; - // refreshRate: number; - // port: number; - // token: string; - // urlCallback: string; - // retentionDays: number; - // cronJob: string; - // thresholds: { - // cpu: number; - // memory: number; - // }; - // }; - // containers: { - // refreshRate: number; - // services: { - // include: string[]; - // exclude: string[]; - // }; - // }; - // }>() - // .notNull() - // .default({ - // server: { - // type: "Dokploy", - // refreshRate: 60, - // port: 4500, - // token: "", - // retentionDays: 2, - // cronJob: "", - // urlCallback: "", - // thresholds: { - // cpu: 0, - // memory: 0, - // }, - // }, - // containers: { - // refreshRate: 60, - // services: { - // include: [], - // exclude: [], - // }, - // }, - // }), - // cleanupCacheApplications: boolean("cleanupCacheApplications") - // .notNull() - // .default(false), - // cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") - // .notNull() - // .default(false), - // cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") - // .notNull() - // .default(false), + adminId: text("adminId") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), + serverIp: text("serverIp"), + certificateType: certificateType("certificateType").notNull().default("none"), + host: text("host"), + letsEncryptEmail: text("letsEncryptEmail"), + sshPrivateKey: text("sshPrivateKey"), + enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), + enableLogRotation: boolean("enableLogRotation").notNull().default(false), + authId: text("authId") + .notNull() + .references(() => auth.id, { onDelete: "cascade" }), + createdAt: text("createdAt") + .notNull() + .$defaultFn(() => new Date().toISOString()), + stripeCustomerId: text("stripeCustomerId"), + stripeSubscriptionId: text("stripeSubscriptionId"), + serversQuantity: integer("serversQuantity").notNull().default(0), + + // Metrics + enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), + metricsConfig: jsonb("metricsConfig") + .$type<{ + server: { + type: "Dokploy" | "Remote"; + refreshRate: number; + port: number; + token: string; + urlCallback: string; + retentionDays: number; + cronJob: string; + thresholds: { + cpu: number; + memory: number; + }; + }; + containers: { + refreshRate: number; + services: { + include: string[]; + exclude: string[]; + }; + }; + }>() + .notNull() + .default({ + server: { + type: "Dokploy", + refreshRate: 60, + port: 4500, + token: "", + retentionDays: 2, + cronJob: "", + urlCallback: "", + thresholds: { + cpu: 0, + memory: 0, + }, + }, + containers: { + refreshRate: 60, + services: { + include: [], + exclude: [], + }, + }, + }), + cleanupCacheApplications: boolean("cleanupCacheApplications") + .notNull() + .default(false), + cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") + .notNull() + .default(false), + cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") + .notNull() + .default(false), }); export const adminsRelations = relations(admins, ({ one, many }) => ({ - // auth: one(auth, { - // fields: [admins.authId], - // references: [auth.id], - // }), - // users: many(users), - // registry: many(registry), - // sshKeys: many(sshKeys), - // certificates: many(certificates), + auth: one(auth, { + fields: [admins.authId], + references: [auth.id], + }), + users: many(users), + registry: many(registry), + sshKeys: many(sshKeys), + certificates: many(certificates), })); const createSchema = createInsertSchema(admins, { - // adminId: z.string(), - // enableDockerCleanup: z.boolean().optional(), - // sshPrivateKey: z.string().optional(), - // certificateType: z.enum(["letsencrypt", "none"]).default("none"), - // serverIp: z.string().optional(), - // letsEncryptEmail: z.string().optional(), + adminId: z.string(), + enableDockerCleanup: z.boolean().optional(), + sshPrivateKey: z.string().optional(), + certificateType: z.enum(["letsencrypt", "none"]).default("none"), + serverIp: z.string().optional(), + letsEncryptEmail: z.string().optional(), }); export const apiUpdateAdmin = createSchema.partial(); export const apiSaveSSHKey = createSchema .pick({ - // sshPrivateKey: true, + sshPrivateKey: true, }) .required(); export const apiAssignDomain = createSchema .pick({ - // host: true, - // certificateType: true, - // letsEncryptEmail: true, + host: true, + certificateType: true, + letsEncryptEmail: true, }) .required() .partial({ - // letsEncryptEmail: true, + letsEncryptEmail: true, }); export const apiUpdateDockerCleanup = createSchema .pick({ - // enableDockerCleanup: true, + enableDockerCleanup: true, }) .required() .extend({ diff --git a/packages/server/src/db/schema/auth.ts b/packages/server/src/db/schema/auth.ts index 35f4dc853..7093a40c3 100644 --- a/packages/server/src/db/schema/auth.ts +++ b/packages/server/src/db/schema/auth.ts @@ -5,7 +5,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; // import { admins } from "./admin"; -import { user } from "./user"; +import { users } from "./user"; const randomImages = [ "/avatars/avatar-1.png", @@ -56,7 +56,7 @@ export const auth = pgTable("auth", { export const authRelations = relations(auth, ({ many }) => ({ // admins: many(admins), - users: many(user), + users: many(users), })); const createSchema = createInsertSchema(auth, { email: z.string().email(), diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index c1a57a5ac..c72d189c2 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -4,8 +4,10 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { server } from "./server"; -import { user } from "./user"; +// import { user } from "./user"; import { generateAppName } from "./utils"; +import { admins } from "./admin"; +import { users_temp } from "./user"; export const certificates = pgTable("certificate", { certificateId: text("certificateId") @@ -20,9 +22,12 @@ export const certificates = pgTable("certificate", { .$defaultFn(() => generateAppName("certificate")) .unique(), autoRenew: boolean("autoRenew"), - userId: text("userId").references(() => user.userId, { - onDelete: "cascade", - }), + // userId: text("userId").references(() => user.userId, { + // onDelete: "cascade", + // }), + userId: text("userId") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), @@ -35,10 +40,10 @@ export const certificatesRelations = relations( fields: [certificates.serverId], references: [server.serverId], }), - user: one(user, { - fields: [certificates.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [certificates.userId], + // references: [user.id], + // }), }), ); diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index 1fe48a346..6b9ea5d93 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -5,7 +5,8 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { backups } from "./backups"; -import { user } from "./user"; +import { users_temp } from "./user"; +// import { user } from "./user"; export const destinations = pgTable("destination", { destinationId: text("destinationId") @@ -20,19 +21,22 @@ export const destinations = pgTable("destination", { region: text("region").notNull(), // maybe it can be null endpoint: text("endpoint").notNull(), + // userId: text("userId") + // .notNull() + // .references(() => user.userId, { onDelete: "cascade" }), userId: text("userId") .notNull() - .references(() => user.userId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const destinationsRelations = relations( destinations, ({ many, one }) => ({ backups: many(backups), - user: one(user, { - fields: [destinations.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [destinations.userId], + // references: [user.id], + // }), }), ); diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index 3a3bade81..4d154ee33 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -7,7 +7,8 @@ import { admins } from "./admin"; import { bitbucket } from "./bitbucket"; import { github } from "./github"; import { gitlab } from "./gitlab"; -import { user } from "./user"; +import { users_temp } from "./user"; +// import { user } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ "github", @@ -25,9 +26,12 @@ export const gitProvider = pgTable("git_provider", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - userId: text("userId").references(() => user.userId, { - onDelete: "cascade", - }), + // userId: text("userId").references(() => user.userId, { + // onDelete: "cascade", + // }), + userId: text("userId") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ @@ -43,10 +47,10 @@ export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ fields: [gitProvider.gitProviderId], references: [bitbucket.gitProviderId], }), - user: one(user, { - fields: [gitProvider.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [gitProvider.userId], + // references: [user.id], + // }), })); const createSchema = createInsertSchema(gitProvider); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 1771a92af..3ab253396 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -4,7 +4,8 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; -import { user } from "./user"; +import { users_temp } from "./user"; +// import { user } from "./user"; export const notificationType = pgEnum("notificationType", [ "slack", @@ -45,9 +46,12 @@ export const notifications = pgTable("notification", { gotifyId: text("gotifyId").references(() => gotify.gotifyId, { onDelete: "cascade", }), - userId: text("userId").references(() => user.userId, { - onDelete: "cascade", - }), + // userId: text("userId").references(() => user.userId, { + // onDelete: "cascade", + // }), + userId: text("userId") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const slack = pgTable("slack", { @@ -122,10 +126,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.gotifyId], references: [gotify.gotifyId], }), - user: one(user, { - fields: [notifications.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [notifications.userId], + // references: [user.id], + // }), })); export const notificationsSchema = createInsertSchema(notifications); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index d83936a38..efde3c34b 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -12,7 +12,8 @@ import { mongo } from "./mongo"; import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; -import { user } from "./user"; +import { users, users_temp } from "./user"; +import { admins } from "./admin"; export const projects = pgTable("project", { projectId: text("projectId") @@ -24,9 +25,12 @@ export const projects = pgTable("project", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), + // userId: text("userId") + // .notNull() + // .references(() => user.userId, { onDelete: "cascade" }), userId: text("userId") .notNull() - .references(() => user.userId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); @@ -38,10 +42,10 @@ export const projectRelations = relations(projects, ({ many, one }) => ({ mongo: many(mongo), redis: many(redis), compose: many(compose), - user: one(user, { - fields: [projects.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [projects.userId], + // references: [user.id], + // }), })); const createSchema = createInsertSchema(projects, { diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 936f65fc8..62c2b2d7d 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -5,7 +5,8 @@ import { nanoid } from "nanoid"; import { z } from "zod"; import { admins } from "./admin"; import { applications } from "./application"; -import { user } from "./user"; +import { users_temp } from "./user"; +// import { user } from "./user"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -28,16 +29,19 @@ export const registry = pgTable("registry", { .notNull() .$defaultFn(() => new Date().toISOString()), registryType: registryType("selfHosted").notNull().default("cloud"), + // userId: text("userId") + // .notNull() + // .references(() => user.userId, { onDelete: "cascade" }), userId: text("userId") .notNull() - .references(() => user.userId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ - user: one(user, { - fields: [registry.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [registry.userId], + // references: [user.id], + // }), applications: many(applications), })); diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index 4e238e23b..b6b77dbcd 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -22,8 +22,9 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { sshKeys } from "./ssh-key"; -import { user } from "./user"; +// import { user } from "./user"; import { generateAppName } from "./utils"; +import { users_temp } from "./user"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); @@ -41,12 +42,14 @@ export const server = pgTable("server", { .notNull() .$defaultFn(() => generateAppName("server")), enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), + createdAt: text("createdAt").notNull(), + // .$defaultFn(() => new Date().toISOString()), + // userId: text("userId") + // .notNull() + // .references(() => user.userId, { onDelete: "cascade" }), userId: text("userId") .notNull() - .references(() => user.userId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { @@ -101,10 +104,10 @@ export const server = pgTable("server", { }); export const serverRelations = relations(server, ({ one, many }) => ({ - user: one(user, { - fields: [server.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [server.userId], + // references: [user.id], + // }), deployments: many(deployments), sshKey: one(sshKeys, { fields: [server.sshKeyId], diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index d5932764a..03a70c41a 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,19 +1,31 @@ import { sql } from "drizzle-orm"; import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { user } from "./user"; +import { users_temp } from "./user"; +import { auth } from "./auth"; // OLD TABLE -export const session = pgTable("session", { +export const session = pgTable("session_temp", { id: text("id").primaryKey(), expiresAt: timestamp("expires_at").notNull(), - // token: text("token").notNull().unique().default(sql`gen_random_uuid()`), + token: text("token").notNull().unique(), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), ipAddress: text("ip_address"), userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => user.userId), + .references(() => users_temp.id), impersonatedBy: text("impersonated_by"), activeOrganizationId: text("active_organization_id"), }); + +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(), +}); diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index 9bb69fee4..cca9259bc 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -7,7 +7,7 @@ import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; -import { user } from "./user"; +// import { user } from "./user"; export const sshKeys = pgTable("ssh-key", { sshKeyId: text("sshKeyId") @@ -22,19 +22,22 @@ export const sshKeys = pgTable("ssh-key", { .notNull() .$defaultFn(() => new Date().toISOString()), lastUsedAt: text("lastUsedAt"), - userId: text("userId").references(() => user.userId, { - onDelete: "cascade", - }), + // userId: text("userId").references(() => user.userId, { + // onDelete: "cascade", + // }), + adminId: text("adminId") + .notNull() + .references(() => admins.adminId, { onDelete: "cascade" }), }); export const sshKeysRelations = relations(sshKeys, ({ many, one }) => ({ applications: many(applications), compose: many(compose), servers: many(server), - user: one(user, { - fields: [sshKeys.userId], - references: [user.id], - }), + // user: one(user, { + // fields: [sshKeys.userId], + // references: [user.id], + // }), })); const createSchema = createInsertSchema( diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 00e16679b..5f2a14e91 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -21,11 +21,57 @@ import { certificateType } from "./shared"; */ // OLD TABLE -export const user = pgTable("user", { + +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), + canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), + canCreateServices: boolean("canCreateServices").notNull().default(false), + canDeleteProjects: boolean("canDeleteProjects").notNull().default(false), + canDeleteServices: boolean("canDeleteServices").notNull().default(false), + canAccessToDocker: boolean("canAccessToDocker").notNull().default(false), + canAccessToAPI: boolean("canAccessToAPI").notNull().default(false), + canAccessToGitProviders: boolean("canAccessToGitProviders") + .notNull() + .default(false), + canAccessToTraefikFiles: boolean("canAccessToTraefikFiles") + .notNull() + .default(false), + accessedProjects: text("accesedProjects") + .array() + .notNull() + .default(sql`ARRAY[]::text[]`), + accessedServices: text("accesedServices") + .array() + .notNull() + .default(sql`ARRAY[]::text[]`), + adminId: text("adminId") + .notNull() + .references(() => admins.adminId, { onDelete: "cascade" }), + authId: text("authId") + .notNull() + .references(() => auth.id, { onDelete: "cascade" }), +}); + +// TEMP +export const users_temp = pgTable("user_temp", { + id: text("id") + .notNull() + .primaryKey() + .$defaultFn(() => nanoid()), name: text("name").notNull().default(""), token: text("token").notNull(), isRegistered: boolean("isRegistered").notNull().default(false), @@ -138,19 +184,19 @@ export const user = pgTable("user", { serversQuantity: integer("serversQuantity").notNull().default(0), }); -export const usersRelations = relations(user, ({ one }) => ({ - // auth: one(auth, { - // fields: [users.authId], - // references: [auth.id], - // }), - // admin: one(admins, { - // fields: [users.adminId], - // references: [admins.adminId], - // }), +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(user, { - id: z.string().min(1), +const createSchema = createInsertSchema(users, { + userId: z.string().min(1), // authId: z.string().min(1), token: z.string().min(1), isRegistered: z.boolean().optional(), @@ -183,7 +229,7 @@ export const apiFindOneToken = createSchema export const apiAssignPermissions = createSchema .pick({ - id: true, + userId: true, canCreateProjects: true, canCreateServices: true, canDeleteProjects: true, @@ -200,7 +246,7 @@ export const apiAssignPermissions = createSchema export const apiFindOneUser = createSchema .pick({ - id: true, + userId: true, }) .required(); From 23f1ce17de1f18e65aa9bd8610822968b14c7c34 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:38:22 -0600 Subject: [PATCH 013/126] refactor: add migration --- ...d_morbius.sql => 0066_smiling_warlock.sql} | 0 apps/dokploy/drizzle/0066_yielding_echo.sql | 128 + apps/dokploy/drizzle/0067_migrate-data.sql | 170 + apps/dokploy/drizzle/meta/0066_snapshot.json | 100 +- apps/dokploy/drizzle/meta/0067_snapshot.json | 5278 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 11 +- packages/server/src/db/schema/certificate.ts | 12 +- packages/server/src/db/schema/destination.ts | 4 +- packages/server/src/db/schema/git-provider.ts | 12 +- packages/server/src/db/schema/notification.ts | 12 +- packages/server/src/db/schema/project.ts | 4 +- packages/server/src/db/schema/registry.ts | 4 +- packages/server/src/db/schema/server.ts | 4 +- 13 files changed, 5661 insertions(+), 78 deletions(-) rename apps/dokploy/drizzle/{0066_rapid_morbius.sql => 0066_smiling_warlock.sql} (100%) create mode 100644 apps/dokploy/drizzle/0066_yielding_echo.sql create mode 100644 apps/dokploy/drizzle/0067_migrate-data.sql create mode 100644 apps/dokploy/drizzle/meta/0067_snapshot.json diff --git a/apps/dokploy/drizzle/0066_rapid_morbius.sql b/apps/dokploy/drizzle/0066_smiling_warlock.sql similarity index 100% rename from apps/dokploy/drizzle/0066_rapid_morbius.sql rename to apps/dokploy/drizzle/0066_smiling_warlock.sql diff --git a/apps/dokploy/drizzle/0066_yielding_echo.sql b/apps/dokploy/drizzle/0066_yielding_echo.sql new file mode 100644 index 000000000..20f9d07b9 --- /dev/null +++ b/apps/dokploy/drizzle/0066_yielding_echo.sql @@ -0,0 +1,128 @@ +CREATE TABLE "user_temp" ( + "id" text PRIMARY KEY NOT NULL, + "name" text DEFAULT '' NOT NULL, + "token" text NOT NULL, + "isRegistered" boolean DEFAULT false NOT NULL, + "expirationDate" text NOT NULL, + "createdAt" text NOT NULL, + "canCreateProjects" boolean DEFAULT false NOT NULL, + "canAccessToSSHKeys" boolean DEFAULT false NOT NULL, + "canCreateServices" boolean DEFAULT false NOT NULL, + "canDeleteProjects" boolean DEFAULT false NOT NULL, + "canDeleteServices" boolean DEFAULT false NOT NULL, + "canAccessToDocker" boolean DEFAULT false NOT NULL, + "canAccessToAPI" boolean DEFAULT false NOT NULL, + "canAccessToGitProviders" boolean DEFAULT false NOT NULL, + "canAccessToTraefikFiles" boolean DEFAULT false NOT NULL, + "accesedProjects" text[] DEFAULT ARRAY[]::text[] NOT NULL, + "accesedServices" text[] DEFAULT ARRAY[]::text[] NOT NULL, + "email" text NOT NULL, + "email_verified" boolean NOT NULL, + "image" text, + "role" text, + "banned" boolean, + "ban_reason" text, + "ban_expires" timestamp, + "updated_at" timestamp NOT NULL, + "serverIp" text, + "certificateType" "certificateType" DEFAULT 'none' NOT NULL, + "host" text, + "letsEncryptEmail" text, + "sshPrivateKey" text, + "enableDockerCleanup" boolean DEFAULT false NOT NULL, + "enableLogRotation" boolean DEFAULT false NOT NULL, + "enablePaidFeatures" boolean DEFAULT false NOT NULL, + "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb NOT NULL, + "cleanupCacheApplications" boolean DEFAULT false NOT NULL, + "cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL, + "cleanupCacheOnCompose" boolean DEFAULT false NOT NULL, + "stripeCustomerId" text, + "stripeSubscriptionId" text, + "serversQuantity" integer DEFAULT 0 NOT NULL, + CONSTRAINT "user_temp_email_unique" UNIQUE("email") +); +--> statement-breakpoint +CREATE TABLE "session_temp" ( + "id" text PRIMARY KEY NOT NULL, + "expires_at" timestamp NOT NULL, + "token" text NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "ip_address" text, + "user_agent" text, + "user_id" text NOT NULL, + "impersonated_by" text, + "active_organization_id" text, + CONSTRAINT "session_temp_token_unique" UNIQUE("token") +); +--> statement-breakpoint +CREATE TABLE "account" ( + "id" text PRIMARY KEY NOT NULL, + "account_id" text NOT NULL, + "provider_id" text NOT NULL, + "user_id" text NOT NULL, + "access_token" text, + "refresh_token" text, + "id_token" text, + "access_token_expires_at" timestamp, + "refresh_token_expires_at" timestamp, + "scope" text, + "password" text, + "is2FAEnabled" boolean DEFAULT false NOT NULL, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "resetPasswordToken" text, + "resetPasswordExpiresAt" text, + "confirmationToken" text, + "confirmationExpiresAt" text +); +--> statement-breakpoint +CREATE TABLE "invitation" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "email" text NOT NULL, + "role" text, + "status" text NOT NULL, + "expires_at" timestamp NOT NULL, + "inviter_id" text NOT NULL +); +--> statement-breakpoint +CREATE TABLE "member" ( + "id" text PRIMARY KEY NOT NULL, + "organization_id" text NOT NULL, + "user_id" text NOT NULL, + "role" text NOT NULL, + "created_at" timestamp NOT NULL +); +--> statement-breakpoint +CREATE TABLE "organization" ( + "id" text PRIMARY KEY NOT NULL, + "name" text NOT NULL, + "slug" text, + "logo" text, + "created_at" timestamp NOT NULL, + "metadata" text, + "owner_id" text NOT NULL, + CONSTRAINT "organization_slug_unique" UNIQUE("slug") +); +--> statement-breakpoint +CREATE TABLE "verification" ( + "id" text PRIMARY KEY NOT NULL, + "identifier" text NOT NULL, + "value" text NOT NULL, + "expires_at" timestamp NOT NULL, + "created_at" timestamp, + "updated_at" timestamp +); +--> statement-breakpoint +ALTER TABLE "certificate" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "notification" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "ssh-key" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "git_provider" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "session_temp" ADD CONSTRAINT "session_temp_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_temp_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql new file mode 100644 index 000000000..62604d32c --- /dev/null +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -0,0 +1,170 @@ +-- Custom SQL migration file, put your code below! -- + +WITH inserted_users AS ( + -- Insertar usuarios desde admins + INSERT INTO user_temp ( + id, + email, + token, + "email_verified", + "updated_at", + role, + "serverIp", + image, + "certificateType", + host, + "letsEncryptEmail", + "sshPrivateKey", + "enableDockerCleanup", + "enableLogRotation", + "enablePaidFeatures", + "metricsConfig", + "cleanupCacheApplications", + "cleanupCacheOnPreviews", + "cleanupCacheOnCompose", + "stripeCustomerId", + "stripeSubscriptionId", + "serversQuantity", + "expirationDate", + "createdAt" + ) + SELECT + a."adminId", + auth.email, + COALESCE(auth.token, ''), + true, + CURRENT_TIMESTAMP, + 'admin', + a."serverIp", + auth.image, + a."certificateType", + a.host, + a."letsEncryptEmail", + a."sshPrivateKey", + a."enableDockerCleanup", + a."enableLogRotation", + a."enablePaidFeatures", + a."metricsConfig", + a."cleanupCacheApplications", + a."cleanupCacheOnPreviews", + a."cleanupCacheOnCompose", + a."stripeCustomerId", + a."stripeSubscriptionId", + a."serversQuantity", + NOW() + INTERVAL '1 year', + NOW() + FROM admin a + JOIN auth ON auth.id = a."authId" + RETURNING * +), +inserted_accounts AS ( + -- Insertar cuentas para los admins + INSERT INTO account ( + id, + "account_id", + "provider_id", + "user_id", + password, + "is2FAEnabled", + "created_at", + "updated_at" + ) + SELECT + gen_random_uuid(), + gen_random_uuid(), + 'credentials', + a."adminId", + auth.password, + COALESCE(auth."is2FAEnabled", false), + NOW(), + NOW() + FROM admin a + JOIN auth ON auth.id = a."authId" + RETURNING * +), +inserted_orgs AS ( + -- Crear organizaciones para cada admin + INSERT INTO organization ( + id, + name, + slug, + "owner_id", + "created_at" + ) + SELECT + gen_random_uuid(), + 'My Organization', + -- Generamos un slug único usando una función de hash + encode(sha256((a."adminId" || CURRENT_TIMESTAMP)::bytea), 'hex'), + a."adminId", + NOW() + FROM admin a + RETURNING * +), +inserted_members AS ( + -- Insertar usuarios miembros + INSERT INTO user_temp ( + id, + email, + token, + "email_verified", + "updated_at", + role, + image, + "createdAt", + "canAccessToAPI", + "canAccessToDocker", + "canAccessToGitProviders", + "canAccessToSSHKeys", + "canAccessToTraefikFiles", + "canCreateProjects", + "canCreateServices", + "canDeleteProjects", + "canDeleteServices", + "accesedProjects", + "accesedServices", + "expirationDate" + ) + SELECT + u."userId", + auth.email, + COALESCE(u.token, ''), + true, + CURRENT_TIMESTAMP, + 'user', + auth.image, + NOW(), + COALESCE(u."canAccessToAPI", false), + COALESCE(u."canAccessToDocker", false), + COALESCE(u."canAccessToGitProviders", false), + COALESCE(u."canAccessToSSHKeys", false), + COALESCE(u."canAccessToTraefikFiles", false), + COALESCE(u."canCreateProjects", false), + COALESCE(u."canCreateServices", false), + COALESCE(u."canDeleteProjects", false), + COALESCE(u."canDeleteServices", false), + COALESCE(u."accesedProjects", '{}'), + COALESCE(u."accesedServices", '{}'), + NOW() + INTERVAL '1 year' + FROM "user" u + JOIN admin a ON u."adminId" = a."adminId" + JOIN auth ON auth.id = u."authId" + RETURNING * +) +-- Insertar miembros en las organizaciones +INSERT INTO member ( + id, + "organization_id", + "user_id", + role, + "created_at" +) +SELECT + gen_random_uuid(), + o.id, + u."userId", + 'admin', + NOW() +FROM "user" u +JOIN admin a ON u."adminId" = a."adminId" +JOIN inserted_orgs o ON o."owner_id" = a."adminId"; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json index 1bf9811a3..c51e03851 100644 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -1,5 +1,5 @@ { - "id": "de382c48-6f10-4578-a307-884fecb4baa3", + "id": "67140673-fcd1-4c33-8dd1-bb7a34bdae23", "prevId": "1240ec96-1751-4de3-b64f-cef9cb716786", "version": "7", "dialect": "postgresql", @@ -1449,8 +1449,8 @@ "primaryKey": false, "notNull": true }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -1465,15 +1465,15 @@ }, "indexes": {}, "foreignKeys": { - "project_userId_user_temp_id_fk": { - "name": "project_userId_user_temp_id_fk", + "project_adminId_admin_adminId_fk": { + "name": "project_adminId_admin_adminId_fk", "tableFrom": "project", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2326,8 +2326,8 @@ "primaryKey": false, "notNull": true }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -2335,15 +2335,15 @@ }, "indexes": {}, "foreignKeys": { - "destination_userId_user_temp_id_fk": { - "name": "destination_userId_user_temp_id_fk", + "destination_adminId_admin_adminId_fk": { + "name": "destination_adminId_admin_adminId_fk", "tableFrom": "destination", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -2734,8 +2734,8 @@ "primaryKey": false, "notNull": false }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -2749,15 +2749,15 @@ }, "indexes": {}, "foreignKeys": { - "certificate_userId_user_temp_id_fk": { - "name": "certificate_userId_user_temp_id_fk", + "certificate_adminId_admin_adminId_fk": { + "name": "certificate_adminId_admin_adminId_fk", "tableFrom": "certificate", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3651,8 +3651,8 @@ "notNull": true, "default": "'cloud'" }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -3660,15 +3660,15 @@ }, "indexes": {}, "foreignKeys": { - "registry_userId_user_temp_id_fk": { - "name": "registry_userId_user_temp_id_fk", + "registry_adminId_admin_adminId_fk": { + "name": "registry_adminId_admin_adminId_fk", "tableFrom": "registry", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -3911,8 +3911,8 @@ "primaryKey": false, "notNull": false }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -3985,15 +3985,15 @@ "onDelete": "cascade", "onUpdate": "no action" }, - "notification_userId_user_temp_id_fk": { - "name": "notification_userId_user_temp_id_fk", + "notification_adminId_admin_adminId_fk": { + "name": "notification_adminId_admin_adminId_fk", "tableFrom": "notification", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -4173,8 +4173,8 @@ "primaryKey": false, "notNull": true }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -4182,15 +4182,15 @@ }, "indexes": {}, "foreignKeys": { - "git_provider_userId_user_temp_id_fk": { - "name": "git_provider_userId_user_temp_id_fk", + "git_provider_adminId_admin_adminId_fk": { + "name": "git_provider_adminId_admin_adminId_fk", "tableFrom": "git_provider", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" @@ -4488,8 +4488,8 @@ "primaryKey": false, "notNull": true }, - "userId": { - "name": "userId", + "adminId": { + "name": "adminId", "type": "text", "primaryKey": false, "notNull": true @@ -4525,15 +4525,15 @@ }, "indexes": {}, "foreignKeys": { - "server_userId_user_temp_id_fk": { - "name": "server_userId_user_temp_id_fk", + "server_adminId_admin_adminId_fk": { + "name": "server_adminId_admin_adminId_fk", "tableFrom": "server", - "tableTo": "user_temp", + "tableTo": "admin", "columnsFrom": [ - "userId" + "adminId" ], "columnsTo": [ - "id" + "adminId" ], "onDelete": "cascade", "onUpdate": "no action" diff --git a/apps/dokploy/drizzle/meta/0067_snapshot.json b/apps/dokploy/drizzle/meta/0067_snapshot.json new file mode 100644 index 000000000..33fc48703 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0067_snapshot.json @@ -0,0 +1,5278 @@ +{ + "id": "54ba08a1-8861-438a-a89d-f01d09a690c2", + "prevId": "67140673-fcd1-4c33-8dd1-bb7a34bdae23", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "columnsFrom": [ + "registryId" + ], + "tableTo": "registry", + "columnsTo": [ + "registryId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "columnsFrom": [ + "githubId" + ], + "tableTo": "github", + "columnsTo": [ + "githubId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "columnsFrom": [ + "gitlabId" + ], + "tableTo": "gitlab", + "columnsTo": [ + "gitlabId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "columnsFrom": [ + "bitbucketId" + ], + "tableTo": "bitbucket", + "columnsTo": [ + "bitbucketId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "columnsFrom": [ + "authId" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "columnsFrom": [ + "authId" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_adminId_admin_adminId_fk": { + "name": "project_adminId_admin_adminId_fk", + "tableFrom": "project", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "previewDeploymentId" + ], + "tableTo": "preview_deployments", + "columnsTo": [ + "previewDeploymentId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "destinationId" + ], + "tableTo": "destination", + "columnsTo": [ + "destinationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "postgresId" + ], + "tableTo": "postgres", + "columnsTo": [ + "postgresId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mariadbId" + ], + "tableTo": "mariadb", + "columnsTo": [ + "mariadbId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mysqlId" + ], + "tableTo": "mysql", + "columnsTo": [ + "mysqlId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mongoId" + ], + "tableTo": "mongo", + "columnsTo": [ + "mongoId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_adminId_admin_adminId_fk": { + "name": "destination_adminId_admin_adminId_fk", + "tableFrom": "destination", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "previewDeploymentId" + ], + "tableTo": "preview_deployments", + "columnsTo": [ + "previewDeploymentId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "postgresId" + ], + "tableTo": "postgres", + "columnsTo": [ + "postgresId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mariadbId" + ], + "tableTo": "mariadb", + "columnsTo": [ + "mariadbId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mongoId" + ], + "tableTo": "mongo", + "columnsTo": [ + "mongoId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mysqlId" + ], + "tableTo": "mysql", + "columnsTo": [ + "mysqlId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "redisId" + ], + "tableTo": "redis", + "columnsTo": [ + "redisId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_adminId_admin_adminId_fk": { + "name": "certificate_adminId_admin_adminId_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "columns": [ + "certificatePath" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "columns": [ + "token" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "columnsFrom": [ + "user_id" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "columns": [ + "username", + "applicationId" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "githubId" + ], + "tableTo": "github", + "columnsTo": [ + "githubId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "gitlabId" + ], + "tableTo": "gitlab", + "columnsTo": [ + "gitlabId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "bitbucketId" + ], + "tableTo": "bitbucket", + "columnsTo": [ + "bitbucketId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_adminId_admin_adminId_fk": { + "name": "registry_adminId_admin_adminId_fk", + "tableFrom": "registry", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "slackId" + ], + "tableTo": "slack", + "columnsTo": [ + "slackId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "telegramId" + ], + "tableTo": "telegram", + "columnsTo": [ + "telegramId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "discordId" + ], + "tableTo": "discord", + "columnsTo": [ + "discordId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "emailId" + ], + "tableTo": "email", + "columnsTo": [ + "emailId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "gotifyId" + ], + "tableTo": "gotify", + "columnsTo": [ + "gotifyId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_adminId_admin_adminId_fk": { + "name": "notification_adminId_admin_adminId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_adminId_admin_adminId_fk": { + "name": "ssh-key_adminId_admin_adminId_fk", + "tableFrom": "ssh-key", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_adminId_admin_adminId_fk": { + "name": "git_provider_adminId_admin_adminId_fk", + "tableFrom": "git_provider", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_adminId_admin_adminId_fk": { + "name": "server_adminId_admin_adminId_fk", + "tableFrom": "server", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "columnsFrom": [ + "sshKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "columnsFrom": [ + "domainId" + ], + "tableTo": "domain", + "columnsTo": [ + "domainId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "columnsFrom": [ + "organization_id" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "columnsFrom": [ + "inviter_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "columnsFrom": [ + "organization_id" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "columnsFrom": [ + "owner_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 188a97009..c01cd500c 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -467,8 +467,15 @@ { "idx": 66, "version": "7", - "when": 1739425241338, - "tag": "0066_rapid_morbius", + "when": 1739426913392, + "tag": "0066_yielding_echo", + "breakpoints": true + }, + { + "idx": 67, + "version": "7", + "when": 1739427057545, + "tag": "0067_migrate-data", "breakpoints": true } ] diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index c72d189c2..bbdb1915d 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -25,9 +25,9 @@ export const certificates = pgTable("certificate", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), @@ -40,10 +40,10 @@ export const certificatesRelations = relations( fields: [certificates.serverId], references: [server.serverId], }), - // user: one(user, { - // fields: [certificates.userId], - // references: [user.id], - // }), + admin: one(admins, { + fields: [certificates.adminId], + references: [admins.adminId], + }), }), ); diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index 6b9ea5d93..cbbb4c2d5 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -24,9 +24,9 @@ export const destinations = pgTable("destination", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), }); export const destinationsRelations = relations( diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index 4d154ee33..d6720ce06 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -29,9 +29,9 @@ export const gitProvider = pgTable("git_provider", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), }); export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ @@ -47,10 +47,10 @@ export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ fields: [gitProvider.gitProviderId], references: [bitbucket.gitProviderId], }), - // user: one(user, { - // fields: [gitProvider.userId], - // references: [user.id], - // }), + admin: one(admins, { + fields: [gitProvider.adminId], + references: [admins.adminId], + }), })); const createSchema = createInsertSchema(gitProvider); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 3ab253396..0270bd567 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -49,9 +49,9 @@ export const notifications = pgTable("notification", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), }); export const slack = pgTable("slack", { @@ -126,10 +126,10 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.gotifyId], references: [gotify.gotifyId], }), - // user: one(user, { - // fields: [notifications.userId], - // references: [user.id], - // }), + admin: one(admins, { + fields: [notifications.adminId], + references: [admins.adminId], + }), })); export const notificationsSchema = createInsertSchema(notifications); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index efde3c34b..e352e47fb 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -28,9 +28,9 @@ export const projects = pgTable("project", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 62c2b2d7d..9efd6536f 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -32,9 +32,9 @@ export const registry = pgTable("registry", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index b6b77dbcd..ee1083bd6 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -47,9 +47,9 @@ export const server = pgTable("server", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + adminId: text("adminId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => admins.adminId, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { From 7c624080704134999fb5325ae1739339337b4be6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:38:39 -0600 Subject: [PATCH 014/126] refactor: delete --- apps/dokploy/drizzle/0066_smiling_warlock.sql | 153 ------------------ apps/dokploy/migrate.ts | 2 +- packages/server/src/db/schema/account.ts | 2 +- packages/server/src/db/schema/certificate.ts | 4 +- packages/server/src/db/schema/project.ts | 2 +- packages/server/src/db/schema/server.ts | 2 +- packages/server/src/db/schema/session.ts | 2 +- 7 files changed, 7 insertions(+), 160 deletions(-) delete mode 100644 apps/dokploy/drizzle/0066_smiling_warlock.sql diff --git a/apps/dokploy/drizzle/0066_smiling_warlock.sql b/apps/dokploy/drizzle/0066_smiling_warlock.sql deleted file mode 100644 index 9deea8818..000000000 --- a/apps/dokploy/drizzle/0066_smiling_warlock.sql +++ /dev/null @@ -1,153 +0,0 @@ -CREATE TABLE "user_temp" ( - "id" text PRIMARY KEY NOT NULL, - "name" text DEFAULT '' NOT NULL, - "token" text NOT NULL, - "isRegistered" boolean DEFAULT false NOT NULL, - "expirationDate" text NOT NULL, - "createdAt" text NOT NULL, - "canCreateProjects" boolean DEFAULT false NOT NULL, - "canAccessToSSHKeys" boolean DEFAULT false NOT NULL, - "canCreateServices" boolean DEFAULT false NOT NULL, - "canDeleteProjects" boolean DEFAULT false NOT NULL, - "canDeleteServices" boolean DEFAULT false NOT NULL, - "canAccessToDocker" boolean DEFAULT false NOT NULL, - "canAccessToAPI" boolean DEFAULT false NOT NULL, - "canAccessToGitProviders" boolean DEFAULT false NOT NULL, - "canAccessToTraefikFiles" boolean DEFAULT false NOT NULL, - "accesedProjects" text[] DEFAULT ARRAY[]::text[] NOT NULL, - "accesedServices" text[] DEFAULT ARRAY[]::text[] NOT NULL, - "email" text NOT NULL, - "email_verified" boolean NOT NULL, - "image" text, - "role" text, - "banned" boolean, - "ban_reason" text, - "ban_expires" timestamp, - "updated_at" timestamp NOT NULL, - "serverIp" text, - "certificateType" "certificateType" DEFAULT 'none' NOT NULL, - "host" text, - "letsEncryptEmail" text, - "sshPrivateKey" text, - "enableDockerCleanup" boolean DEFAULT false NOT NULL, - "enableLogRotation" boolean DEFAULT false NOT NULL, - "enablePaidFeatures" boolean DEFAULT false NOT NULL, - "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'::jsonb NOT NULL, - "cleanupCacheApplications" boolean DEFAULT false NOT NULL, - "cleanupCacheOnPreviews" boolean DEFAULT false NOT NULL, - "cleanupCacheOnCompose" boolean DEFAULT false NOT NULL, - "stripeCustomerId" text, - "stripeSubscriptionId" text, - "serversQuantity" integer DEFAULT 0 NOT NULL, - CONSTRAINT "user_temp_email_unique" UNIQUE("email") -); ---> statement-breakpoint -CREATE TABLE "session_temp" ( - "id" text PRIMARY KEY NOT NULL, - "expires_at" timestamp NOT NULL, - "token" text NOT NULL, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL, - "ip_address" text, - "user_agent" text, - "user_id" text NOT NULL, - "impersonated_by" text, - "active_organization_id" text, - CONSTRAINT "session_temp_token_unique" UNIQUE("token") -); ---> statement-breakpoint -CREATE TABLE "account" ( - "id" text PRIMARY KEY NOT NULL, - "account_id" text NOT NULL, - "provider_id" text NOT NULL, - "user_id" text NOT NULL, - "access_token" text, - "refresh_token" text, - "id_token" text, - "access_token_expires_at" timestamp, - "refresh_token_expires_at" timestamp, - "scope" text, - "password" text, - "is2FAEnabled" boolean DEFAULT false NOT NULL, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL, - "resetPasswordToken" text, - "resetPasswordExpiresAt" text, - "confirmationToken" text, - "confirmationExpiresAt" text -); ---> statement-breakpoint -CREATE TABLE "invitation" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "email" text NOT NULL, - "role" text, - "status" text NOT NULL, - "expires_at" timestamp NOT NULL, - "inviter_id" text NOT NULL -); ---> statement-breakpoint -CREATE TABLE "member" ( - "id" text PRIMARY KEY NOT NULL, - "organization_id" text NOT NULL, - "user_id" text NOT NULL, - "role" text NOT NULL, - "created_at" timestamp NOT NULL -); ---> statement-breakpoint -CREATE TABLE "organization" ( - "id" text PRIMARY KEY NOT NULL, - "name" text NOT NULL, - "slug" text, - "logo" text, - "created_at" timestamp NOT NULL, - "metadata" text, - "owner_id" text NOT NULL, - CONSTRAINT "organization_slug_unique" UNIQUE("slug") -); ---> statement-breakpoint -CREATE TABLE "verification" ( - "id" text PRIMARY KEY NOT NULL, - "identifier" text NOT NULL, - "value" text NOT NULL, - "expires_at" timestamp NOT NULL, - "created_at" timestamp, - "updated_at" timestamp -); ---> statement-breakpoint -ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint -ALTER TABLE "project" DROP CONSTRAINT "project_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "destination" DROP CONSTRAINT "destination_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "certificate" DROP CONSTRAINT "certificate_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "registry" DROP CONSTRAINT "registry_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "notification" DROP CONSTRAINT "notification_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "server" DROP CONSTRAINT "server_adminId_admin_adminId_fk"; ---> statement-breakpoint -ALTER TABLE "ssh-key" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint -ALTER TABLE "session_temp" ADD CONSTRAINT "session_temp_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_temp_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index 72bbf2ab0..bcbac2336 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -1,8 +1,8 @@ import { drizzle } from "drizzle-orm/postgres-js"; import { migrate } from "drizzle-orm/postgres-js/migrator"; +import { nanoid } from "nanoid"; import postgres from "postgres"; import * as schema from "./server/db/schema"; -import { nanoid } from "nanoid"; const connectionString = process.env.DATABASE_URL!; diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 349428fd8..3e81fbf69 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,6 +1,6 @@ import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { users_temp } from "./user"; import { nanoid } from "nanoid"; +import { users_temp } from "./user"; export const account = pgTable("account", { id: text("id") diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index bbdb1915d..02b00f65e 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -3,11 +3,11 @@ import { boolean, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { admins } from "./admin"; import { server } from "./server"; +import { users_temp } from "./user"; // import { user } from "./user"; import { generateAppName } from "./utils"; -import { admins } from "./admin"; -import { users_temp } from "./user"; export const certificates = pgTable("certificate", { certificateId: text("certificateId") diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index e352e47fb..48effa81c 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -4,6 +4,7 @@ import { pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { admins } from "./admin"; // import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; @@ -13,7 +14,6 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { users, users_temp } from "./user"; -import { admins } from "./admin"; export const projects = pgTable("project", { projectId: text("projectId") diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index ee1083bd6..c0b44ef97 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -22,9 +22,9 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { sshKeys } from "./ssh-key"; +import { users_temp } from "./user"; // import { user } from "./user"; import { generateAppName } from "./utils"; -import { users_temp } from "./user"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index 03a70c41a..4e9d28835 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,7 +1,7 @@ import { sql } from "drizzle-orm"; import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { users_temp } from "./user"; import { auth } from "./auth"; +import { users_temp } from "./user"; // OLD TABLE export const session = pgTable("session_temp", { From 0d525398a83fff995dd1c4eda8ad4541d24ec11f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:45:29 -0600 Subject: [PATCH 015/126] feat: migrate rest schemas --- .../drizzle/0068_sour_professor_monster.sql | 32 + apps/dokploy/drizzle/meta/0068_snapshot.json | 5278 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + packages/server/src/db/schema/certificate.ts | 10 +- packages/server/src/db/schema/destination.ts | 4 +- packages/server/src/db/schema/git-provider.ts | 4 +- packages/server/src/db/schema/notification.ts | 10 +- packages/server/src/db/schema/project.ts | 4 +- packages/server/src/db/schema/registry.ts | 4 +- packages/server/src/db/schema/server.ts | 4 +- packages/server/src/db/schema/ssh-key.ts | 13 +- packages/server/src/db/schema/user.ts | 8 +- 12 files changed, 5348 insertions(+), 30 deletions(-) create mode 100644 apps/dokploy/drizzle/0068_sour_professor_monster.sql create mode 100644 apps/dokploy/drizzle/meta/0068_snapshot.json diff --git a/apps/dokploy/drizzle/0068_sour_professor_monster.sql b/apps/dokploy/drizzle/0068_sour_professor_monster.sql new file mode 100644 index 000000000..f69152b5d --- /dev/null +++ b/apps/dokploy/drizzle/0068_sour_professor_monster.sql @@ -0,0 +1,32 @@ +ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "ssh-key" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint +ALTER TABLE "project" DROP CONSTRAINT "project_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "destination" DROP CONSTRAINT "destination_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "certificate" DROP CONSTRAINT "certificate_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "registry" DROP CONSTRAINT "registry_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "notification" DROP CONSTRAINT "notification_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "ssh-key" DROP CONSTRAINT "ssh-key_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "server" DROP CONSTRAINT "server_adminId_admin_adminId_fk"; +--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0068_snapshot.json b/apps/dokploy/drizzle/meta/0068_snapshot.json new file mode 100644 index 000000000..c9f50b5ee --- /dev/null +++ b/apps/dokploy/drizzle/meta/0068_snapshot.json @@ -0,0 +1,5278 @@ +{ + "id": "c2e134de-9865-4892-a24d-048aa5be22e8", + "prevId": "54ba08a1-8861-438a-a89d-f01d09a690c2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_temp_id_fk": { + "name": "project_userId_user_temp_id_fk", + "tableFrom": "project", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_temp_id_fk": { + "name": "destination_userId_user_temp_id_fk", + "tableFrom": "destination", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_temp_id_fk": { + "name": "certificate_userId_user_temp_id_fk", + "tableFrom": "certificate", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_temp_id_fk": { + "name": "registry_userId_user_temp_id_fk", + "tableFrom": "registry", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_userId_user_temp_id_fk": { + "name": "notification_userId_user_temp_id_fk", + "tableFrom": "notification", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_temp_id_fk": { + "name": "ssh-key_userId_user_temp_id_fk", + "tableFrom": "ssh-key", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_temp_id_fk": { + "name": "server_userId_user_temp_id_fk", + "tableFrom": "server", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index c01cd500c..e0bdf1c64 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -477,6 +477,13 @@ "when": 1739427057545, "tag": "0067_migrate-data", "breakpoints": true + }, + { + "idx": 68, + "version": "7", + "when": 1739428942964, + "tag": "0068_sour_professor_monster", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index 02b00f65e..d31e5929f 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -25,9 +25,9 @@ export const certificates = pgTable("certificate", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), @@ -40,9 +40,9 @@ export const certificatesRelations = relations( fields: [certificates.serverId], references: [server.serverId], }), - admin: one(admins, { - fields: [certificates.adminId], - references: [admins.adminId], + user: one(users_temp, { + fields: [certificates.userId], + references: [users_temp.id], }), }), ); diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index cbbb4c2d5..6b9ea5d93 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -24,9 +24,9 @@ export const destinations = pgTable("destination", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const destinationsRelations = relations( diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index d6720ce06..a3410af53 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -29,9 +29,9 @@ export const gitProvider = pgTable("git_provider", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 0270bd567..462b67109 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -49,9 +49,9 @@ export const notifications = pgTable("notification", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const slack = pgTable("slack", { @@ -126,9 +126,9 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.gotifyId], references: [gotify.gotifyId], }), - admin: one(admins, { - fields: [notifications.adminId], - references: [admins.adminId], + user: one(users_temp, { + fields: [notifications.userId], + references: [users_temp.id], }), })); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index 48effa81c..60f7842b1 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -28,9 +28,9 @@ export const projects = pgTable("project", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 9efd6536f..62c2b2d7d 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -32,9 +32,9 @@ export const registry = pgTable("registry", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index c0b44ef97..26023e96c 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -47,9 +47,9 @@ export const server = pgTable("server", { // userId: text("userId") // .notNull() // .references(() => user.userId, { onDelete: "cascade" }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index cca9259bc..4daa438ce 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -7,6 +7,7 @@ import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; +import { users_temp } from "./user"; // import { user } from "./user"; export const sshKeys = pgTable("ssh-key", { @@ -25,19 +26,19 @@ export const sshKeys = pgTable("ssh-key", { // userId: text("userId").references(() => user.userId, { // onDelete: "cascade", // }), - adminId: text("adminId") + userId: text("userId") .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const sshKeysRelations = relations(sshKeys, ({ many, one }) => ({ applications: many(applications), compose: many(compose), servers: many(server), - // user: one(user, { - // fields: [sshKeys.userId], - // references: [user.id], - // }), + user: one(users_temp, { + fields: [sshKeys.userId], + references: [users_temp.id], + }), })); const createSchema = createInsertSchema( diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 5f2a14e91..932a0a09c 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -189,10 +189,10 @@ export const usersRelations = relations(users, ({ one }) => ({ fields: [users.authId], references: [auth.id], }), - admin: one(admins, { - fields: [users.adminId], - references: [admins.adminId], - }), + // admin: one(admins, { + // fields: [users.adminId], + // references: [admins.adminId], + // }), })); const createSchema = createInsertSchema(users, { From d1f72a2e20fdc2304a380c684ea7f77e52daa95a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 00:57:22 -0600 Subject: [PATCH 016/126] refactor: update migration --- apps/dokploy/drizzle/0067_migrate-data.sql | 26 ++++++++++++++++++++++ apps/dokploy/migrate.ts | 9 ++++++++ 2 files changed, 35 insertions(+) diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql index 62604d32c..b5cefd7b7 100644 --- a/apps/dokploy/drizzle/0067_migrate-data.sql +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -150,6 +150,32 @@ inserted_members AS ( JOIN admin a ON u."adminId" = a."adminId" JOIN auth ON auth.id = u."authId" RETURNING * +), +inserted_member_accounts AS ( + -- Insertar cuentas para los usuarios miembros + INSERT INTO account ( + id, + "account_id", + "provider_id", + "user_id", + password, + "is2FAEnabled", + "created_at", + "updated_at" + ) + SELECT + gen_random_uuid(), + gen_random_uuid(), + 'credentials', + u."userId", + auth.password, + COALESCE(auth."is2FAEnabled", false), + NOW(), + NOW() + FROM "user" u + JOIN admin a ON u."adminId" = a."adminId" + JOIN auth ON auth.id = u."authId" + RETURNING * ) -- Insertar miembros en las organizaciones INSERT INTO member ( diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index bcbac2336..8a8e0dcbe 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -100,6 +100,15 @@ await db .returning() .then((userTemp) => userTemp[0]); + await db.insert(schema.account).values({ + providerId: "credentials", + userId: member?.userId || "", + password: member.auth.password, + is2FAEnabled: member.auth.is2FAEnabled || false, + createdAt: new Date(member.auth.createdAt) || new Date(), + updatedAt: new Date(member.auth.createdAt) || new Date(), + }); + await db.insert(schema.member).values({ organizationId: organization?.id || "", userId: userTemp?.id || "", From 140a871275a7d61ed74e34d1c6f81c69a89c7fc6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 01:21:49 -0600 Subject: [PATCH 017/126] refactor: update --- packages/server/src/db/schema/git-provider.ts | 6 +++--- packages/server/src/services/admin.ts | 2 +- packages/server/src/services/auth.ts | 2 +- packages/server/src/services/user.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index a3410af53..be2c50001 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -47,9 +47,9 @@ export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ fields: [gitProvider.gitProviderId], references: [bitbucket.gitProviderId], }), - admin: one(admins, { - fields: [gitProvider.adminId], - references: [admins.adminId], + user: one(users_temp, { + fields: [gitProvider.userId], + references: [users_temp.id], }), })); diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 21c8f13e5..5155231b0 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -4,7 +4,7 @@ import { admins, type apiCreateUserInvitation, auth, - user, + users_temp, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index f391d011d..787ceedb8 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -5,7 +5,7 @@ import { type apiCreateAdmin, type apiCreateUser, auth, - user, + users_temp, } from "@dokploy/server/db/schema"; import { getPublicIpWithFallback } from "@dokploy/server/wss/utils"; import { TRPCError } from "@trpc/server"; diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index afc3ec009..5a9898cde 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,9 +1,9 @@ import { db } from "@dokploy/server/db"; -import { user } from "@dokploy/server/db/schema"; +import type { users_temp } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -export type User = typeof user.$inferSelect; +export type User = typeof users_temp.$inferSelect; export const findUserById = async (userId: string) => { const userR = await db.query.user.findFirst({ From 74ee024cf90e6ba66e2d6ef31443d8763adf1718 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 01:24:25 -0600 Subject: [PATCH 018/126] refactor: update temps --- apps/dokploy/pages/index.tsx | 4 ++++ packages/server/src/lib/auth.ts | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index e5cc0500d..210160843 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -94,6 +94,10 @@ export default function Home({ IS_CLOUD }: Props) { }); // router.push("/dashboard/projects"); // } + } else { + toast.error("Error to sign up", { + description: error.message, + }); } console.log(data, error); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 8a3e1781c..2706bceac 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -18,15 +18,16 @@ export const auth = betterAuth({ if (ctx.path.startsWith("/sign-up")) { const newSession = ctx.context.newSession; await db - .update(schema.user) + .update(schema.users_temp) .set({ role: "admin", }) - .where(eq(schema.user.id, newSession?.user?.id || "")); + .where(eq(schema.users_temp.id, newSession?.user?.id || "")); } }), }, user: { + modelName: "users_temp", additionalFields: {}, }, plugins: [organization()], From 7c0d223e1794a2721c6368cca06c284087fdc241 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 01:42:58 -0600 Subject: [PATCH 019/126] refactor: add fields --- apps/dokploy/drizzle/0069_known_aqueduct.sql | 1 + .../drizzle/0070_overrated_the_stranger.sql | 1 + apps/dokploy/drizzle/meta/0069_snapshot.json | 5285 ++++++++++++++++ apps/dokploy/drizzle/meta/0070_snapshot.json | 5286 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 14 + packages/server/package.json | 2 + packages/server/src/auth/auth.ts | 1 + packages/server/src/db/schema/user.ts | 5 +- packages/server/src/lib/auth.ts | 7 + packages/server/src/lib/crypto.ts | 94 + packages/server/src/lib/scrypt/index.ts | 1 + pnpm-lock.yaml | 31 + 12 files changed, 10726 insertions(+), 2 deletions(-) create mode 100644 apps/dokploy/drizzle/0069_known_aqueduct.sql create mode 100644 apps/dokploy/drizzle/0070_overrated_the_stranger.sql create mode 100644 apps/dokploy/drizzle/meta/0069_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0070_snapshot.json create mode 100644 packages/server/src/lib/crypto.ts create mode 100644 packages/server/src/lib/scrypt/index.ts diff --git a/apps/dokploy/drizzle/0069_known_aqueduct.sql b/apps/dokploy/drizzle/0069_known_aqueduct.sql new file mode 100644 index 000000000..d6f4dea76 --- /dev/null +++ b/apps/dokploy/drizzle/0069_known_aqueduct.sql @@ -0,0 +1 @@ +ALTER TABLE "user_temp" ADD COLUMN "created_at" timestamp DEFAULT now(); \ No newline at end of file diff --git a/apps/dokploy/drizzle/0070_overrated_the_stranger.sql b/apps/dokploy/drizzle/0070_overrated_the_stranger.sql new file mode 100644 index 000000000..dab91327b --- /dev/null +++ b/apps/dokploy/drizzle/0070_overrated_the_stranger.sql @@ -0,0 +1 @@ +ALTER TABLE "user_temp" ALTER COLUMN "token" SET DEFAULT ''; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0069_snapshot.json b/apps/dokploy/drizzle/meta/0069_snapshot.json new file mode 100644 index 000000000..084216167 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0069_snapshot.json @@ -0,0 +1,5285 @@ +{ + "id": "82a6a31d-7611-410d-8a32-31a36ab369c5", + "prevId": "c2e134de-9865-4892-a24d-048aa5be22e8", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_temp_id_fk": { + "name": "project_userId_user_temp_id_fk", + "tableFrom": "project", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_temp_id_fk": { + "name": "destination_userId_user_temp_id_fk", + "tableFrom": "destination", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_temp_id_fk": { + "name": "certificate_userId_user_temp_id_fk", + "tableFrom": "certificate", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_temp_id_fk": { + "name": "registry_userId_user_temp_id_fk", + "tableFrom": "registry", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_userId_user_temp_id_fk": { + "name": "notification_userId_user_temp_id_fk", + "tableFrom": "notification", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_temp_id_fk": { + "name": "ssh-key_userId_user_temp_id_fk", + "tableFrom": "ssh-key", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_temp_id_fk": { + "name": "server_userId_user_temp_id_fk", + "tableFrom": "server", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0070_snapshot.json b/apps/dokploy/drizzle/meta/0070_snapshot.json new file mode 100644 index 000000000..b0a1eea7c --- /dev/null +++ b/apps/dokploy/drizzle/meta/0070_snapshot.json @@ -0,0 +1,5286 @@ +{ + "id": "c01cab17-e046-4691-b5ab-43f6a92879d4", + "prevId": "82a6a31d-7611-410d-8a32-31a36ab369c5", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_temp_id_fk": { + "name": "project_userId_user_temp_id_fk", + "tableFrom": "project", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_temp_id_fk": { + "name": "destination_userId_user_temp_id_fk", + "tableFrom": "destination", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_temp_id_fk": { + "name": "certificate_userId_user_temp_id_fk", + "tableFrom": "certificate", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_temp_id_fk": { + "name": "registry_userId_user_temp_id_fk", + "tableFrom": "registry", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_userId_user_temp_id_fk": { + "name": "notification_userId_user_temp_id_fk", + "tableFrom": "notification", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_temp_id_fk": { + "name": "ssh-key_userId_user_temp_id_fk", + "tableFrom": "ssh-key", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_temp_id_fk": { + "name": "server_userId_user_temp_id_fk", + "tableFrom": "server", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index e0bdf1c64..d10d6816e 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -484,6 +484,20 @@ "when": 1739428942964, "tag": "0068_sour_professor_monster", "breakpoints": true + }, + { + "idx": 69, + "version": "7", + "when": 1739432476590, + "tag": "0069_known_aqueduct", + "breakpoints": true + }, + { + "idx": 70, + "version": "7", + "when": 1739432513877, + "tag": "0070_overrated_the_stranger", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/server/package.json b/packages/server/package.json index a8cf97c66..d8f72a868 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,6 +28,8 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@oslojs/encoding":"1.1.0", + "@oslojs/crypto":"1.0.1", "drizzle-dbml-generator":"0.10.0", "better-auth":"1.1.16", "rotating-file-stream": "3.2.3", diff --git a/packages/server/src/auth/auth.ts b/packages/server/src/auth/auth.ts index 423c4333f..7dc26c95f 100644 --- a/packages/server/src/auth/auth.ts +++ b/packages/server/src/auth/auth.ts @@ -16,6 +16,7 @@ export const lucia = new Lucia(adapter, { secure: false, }, }, + sessionExpiresIn: new TimeSpan(1, "d"), getUserAttributes: (attributes) => { return { diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 932a0a09c..d754687cd 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -73,14 +73,15 @@ export const users_temp = pgTable("user_temp", { .primaryKey() .$defaultFn(() => nanoid()), name: text("name").notNull().default(""), - token: text("token").notNull(), + token: text("token").notNull().default(""), isRegistered: boolean("isRegistered").notNull().default(false), expirationDate: text("expirationDate") .notNull() .$defaultFn(() => new Date().toISOString()), - createdAt: text("createdAt") + createdAt2: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), + createdAt: timestamp("created_at").defaultNow(), canCreateProjects: boolean("canCreateProjects").notNull().default(false), canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), canCreateServices: boolean("canCreateServices").notNull().default(false), diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 2706bceac..9555e9b6c 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -5,6 +5,9 @@ import { admin, createAuthMiddleware, organization } from "better-auth/plugins"; import { eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; +import { Scrypt } from "lucia"; +const scrypt = new Scrypt(); + export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", @@ -12,6 +15,10 @@ export const auth = betterAuth({ }), emailAndPassword: { enabled: true, + password: { + hash: scrypt.hash, + verify: scrypt.verify, + }, }, hooks: { after: createAuthMiddleware(async (ctx) => { diff --git a/packages/server/src/lib/crypto.ts b/packages/server/src/lib/crypto.ts new file mode 100644 index 000000000..27d86dbd5 --- /dev/null +++ b/packages/server/src/lib/crypto.ts @@ -0,0 +1,94 @@ +// import { +// decodeHex, +// encodeBase32LowerCaseNoPadding, +// encodeHexLowerCase, +// } from "@oslojs/encoding"; +// import { generateRandomString } from "@oslojs/crypto/random"; +// import { constantTimeEqual } from "@oslojs/crypto/subtle"; +// import { scrypt } from "./scrypt/index"; + +// import type { RandomReader } from "@oslojs/crypto/random"; + +// async function generateScryptKey( +// data: string, +// salt: string, +// blockSize = 16, +// ): Promise { +// const encodedData = new TextEncoder().encode(data); +// const encodedSalt = new TextEncoder().encode(salt); +// const keyUint8Array = await scrypt(encodedData, encodedSalt, { +// N: 16384, +// r: blockSize, +// p: 1, +// dkLen: 64, +// }); +// return new Uint8Array(keyUint8Array); +// } + +// const random: RandomReader = { +// read(bytes: Uint8Array): void { +// crypto.getRandomValues(bytes); +// }, +// }; + +// export function generateId(length: number): string { +// const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; +// return generateRandomString(random, alphabet, length); +// } + +// export function generateIdFromEntropySize(size: number): string { +// const buffer = crypto.getRandomValues(new Uint8Array(size)); +// return encodeBase32LowerCaseNoPadding(buffer); +// } + +// export class Scrypt implements PasswordHashingAlgorithm { +// async hash(password: string): Promise { +// const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16))); +// const key = await generateScryptKey(password.normalize("NFKC"), salt); +// return `${salt}:${encodeHexLowerCase(key)}`; +// } +// async verify(hash: string, password: string): Promise { +// const parts = hash.split(":"); +// if (parts.length !== 2) return false; + +// const [salt, key] = parts; +// const targetKey = await generateScryptKey(password.normalize("NFKC"), salt); +// return constantTimeEqual(targetKey, decodeHex(key)); +// } +// } + +// export class LegacyScrypt implements PasswordHashingAlgorithm { +// async hash(password: string): Promise { +// const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16))); +// const key = await generateScryptKey(password.normalize("NFKC"), salt); +// return `s2:${salt}:${encodeHexLowerCase(key)}`; +// } +// async verify(hash: string, password: string): Promise { +// const parts = hash.split(":"); +// if (parts.length === 2) { +// const [salt, key] = parts; +// const targetKey = await generateScryptKey( +// password.normalize("NFKC"), +// salt, +// 8, +// ); +// const result = constantTimeEqual(targetKey, decodeHex(key)); +// return result; +// } +// if (parts.length !== 3) return false; +// const [version, salt, key] = parts; +// if (version === "s2") { +// const targetKey = await generateScryptKey( +// password.normalize("NFKC"), +// salt, +// ); +// return constantTimeEqual(targetKey, decodeHex(key)); +// } +// return false; +// } +// } + +// export interface PasswordHashingAlgorithm { +// hash(password: string): Promise; +// verify(hash: string, password: string): Promise; +// } diff --git a/packages/server/src/lib/scrypt/index.ts b/packages/server/src/lib/scrypt/index.ts new file mode 100644 index 000000000..8337712ea --- /dev/null +++ b/packages/server/src/lib/scrypt/index.ts @@ -0,0 +1 @@ +// diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bb49174f4..09f7885b4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -558,6 +558,12 @@ importers: '@octokit/auth-app': specifier: ^6.0.4 version: 6.1.1 + '@oslojs/crypto': + specifier: 1.0.1 + version: 1.0.1 + '@oslojs/encoding': + specifier: 1.1.0 + version: 1.1.0 '@react-email/components': specifier: ^0.0.21 version: 0.0.21(@types/react@18.3.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) @@ -2164,6 +2170,18 @@ packages: '@one-ini/wasm@0.1.1': resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + '@oslojs/asn1@1.0.0': + resolution: {integrity: sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA==} + + '@oslojs/binary@1.0.0': + resolution: {integrity: sha512-9RCU6OwXU6p67H4NODbuxv2S3eenuQ4/WFLrsq+K/k682xrznH5EVWA7N4VFk9VYVcbFtKqur5YQQZc0ySGhsQ==} + + '@oslojs/crypto@1.0.1': + resolution: {integrity: sha512-7n08G8nWjAr/Yu3vu9zzrd0L9XnrJfpMioQcvCMxBIiF5orECHe5/3J0jmXRVvgfqMm/+4oxlQ+Sq39COYLcNQ==} + + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@peculiar/asn1-android@2.3.15': resolution: {integrity: sha512-8U2TIj59cRlSXTX2d0mzUKP7whfWGFMzTeC3qPgAbccXFrPNZLaDhpNEdG5U2QZ/tBv/IHlCJ8s+KYXpJeop6w==} @@ -8507,6 +8525,19 @@ snapshots: '@one-ini/wasm@0.1.1': {} + '@oslojs/asn1@1.0.0': + dependencies: + '@oslojs/binary': 1.0.0 + + '@oslojs/binary@1.0.0': {} + + '@oslojs/crypto@1.0.1': + dependencies: + '@oslojs/asn1': 1.0.0 + '@oslojs/binary': 1.0.0 + + '@oslojs/encoding@1.1.0': {} + '@peculiar/asn1-android@2.3.15': dependencies: '@peculiar/asn1-schema': 2.3.15 From bc901bcb255e655f6e8481672c09d170a322b333 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 02:36:08 -0600 Subject: [PATCH 020/126] refactor: update --- apps/dokploy/drizzle/0067_migrate-data.sql | 4 ++-- apps/dokploy/migrate.ts | 4 ++-- apps/dokploy/pages/index.tsx | 4 ++-- packages/server/src/auth/auth.ts | 2 +- packages/server/src/lib/auth.ts | 16 ++++++++++------ 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql index b5cefd7b7..2057bd965 100644 --- a/apps/dokploy/drizzle/0067_migrate-data.sql +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -72,7 +72,7 @@ inserted_accounts AS ( SELECT gen_random_uuid(), gen_random_uuid(), - 'credentials', + 'credential', a."adminId", auth.password, COALESCE(auth."is2FAEnabled", false), @@ -166,7 +166,7 @@ inserted_member_accounts AS ( SELECT gen_random_uuid(), gen_random_uuid(), - 'credentials', + 'credential', u."userId", auth.password, COALESCE(auth."is2FAEnabled", false), diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index 8a8e0dcbe..d56006f18 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -54,7 +54,7 @@ await db .then((user) => user[0]); await db.insert(schema.account).values({ - providerId: "credentials", + providerId: "credential", userId: user?.id || "", password: admin.auth.password, is2FAEnabled: admin.auth.is2FAEnabled || false, @@ -101,7 +101,7 @@ await db .then((userTemp) => userTemp[0]); await db.insert(schema.account).values({ - providerId: "credentials", + providerId: "credential", userId: member?.userId || "", password: member.auth.password, is2FAEnabled: member.auth.is2FAEnabled || false, diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 210160843..4c53f4797 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -69,8 +69,8 @@ export default function Home({ IS_CLOUD }: Props) { const router = useRouter(); const form = useForm({ defaultValues: { - email: "user5@yopmail.com", - password: "Password1234", + email: "", + password: "", }, resolver: zodResolver(loginSchema), }); diff --git a/packages/server/src/auth/auth.ts b/packages/server/src/auth/auth.ts index 7dc26c95f..09372fa5b 100644 --- a/packages/server/src/auth/auth.ts +++ b/packages/server/src/auth/auth.ts @@ -16,7 +16,7 @@ export const lucia = new Lucia(adapter, { secure: false, }, }, - + sessionExpiresIn: new TimeSpan(1, "d"), getUserAttributes: (attributes) => { return { diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 9555e9b6c..2844d1d17 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -1,23 +1,28 @@ import type { IncomingMessage } from "node:http"; +import * as bcrypt from "bcrypt"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { admin, createAuthMiddleware, organization } from "better-auth/plugins"; +import { createAuthMiddleware, organization } from "better-auth/plugins"; import { eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; -import { Scrypt } from "lucia"; -const scrypt = new Scrypt(); export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: schema, }), + emailAndPassword: { enabled: true, + password: { - hash: scrypt.hash, - verify: scrypt.verify, + async hash(password) { + return bcrypt.hashSync(password, 10); + }, + async verify({ hash, password }) { + return bcrypt.compareSync(password, hash); + }, }, }, hooks: { @@ -35,7 +40,6 @@ export const auth = betterAuth({ }, user: { modelName: "users_temp", - additionalFields: {}, }, plugins: [organization()], }); From 5c24281f720578df8ab26c4113c6057ee61835a8 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 13 Feb 2025 02:45:33 -0600 Subject: [PATCH 021/126] refactor: return correct information --- apps/dokploy/server/api/routers/auth.ts | 1 - packages/server/src/lib/auth.ts | 7 +++++++ packages/server/src/services/auth.ts | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index b6b915f69..c1c2e0a75 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -169,7 +169,6 @@ export const authRouter = createTRPCRouter({ }), get: protectedProcedure.query(async ({ ctx }) => { - console.log(ctx.user); const auth = await findAuthById(ctx.user.id); return auth; }), diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 2844d1d17..878313ae5 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -40,6 +40,11 @@ export const auth = betterAuth({ }, user: { modelName: "users_temp", + additionalFields: { + role: { + type: "string", + }, + }, }, plugins: [organization()], }); @@ -51,6 +56,8 @@ export const validateRequest = async (request: IncomingMessage) => { }), }); + console.log(session); + if (!session?.session || !session.user) { return { session: null, diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 787ceedb8..8781f4f19 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -100,8 +100,8 @@ export const findAuthByEmail = async (email: string) => { }; export const findAuthById = async (authId: string) => { - const result = await db.query.user.findFirst({ - where: eq(user.id, authId), + const result = await db.query.users_temp.findFirst({ + where: eq(users_temp.id, authId), columns: { createdAt: false, updatedAt: false, From d9c83b7010fe099ef4d0e83291eb3c62e38c26b5 Mon Sep 17 00:00:00 2001 From: Khiet Tam Nguyen Date: Thu, 13 Feb 2025 20:36:03 +1100 Subject: [PATCH 022/126] docs(template): note on networking for superset --- .../templates/superset/docker-compose.yml | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/templates/superset/docker-compose.yml b/apps/dokploy/templates/superset/docker-compose.yml index 8dd1cd2d4..b73bf55e5 100644 --- a/apps/dokploy/templates/superset/docker-compose.yml +++ b/apps/dokploy/templates/superset/docker-compose.yml @@ -1,5 +1,8 @@ -# Note: this is an UNOFFICIAL production docker image build for Superset: +# This is an UNOFFICIAL production docker image build for Superset: # - https://github.com/amancevice/docker-superset + + +# ## SETUP INSTRUCTIONS # # After deploying this image, you will need to run one of the two # commands below in a terminal within the superset container: @@ -7,11 +10,30 @@ # $ superset-init # Initialise database only # # You will be prompted to enter the credentials for the admin user. + + +# ## NETWORK INSTRUCTIONS +# +# If you want to connect superset with other internal databases managed by +# Dokploy (on dokploy-network) using internal hostnames, you will need to +# uncomment the `networks` section, both for the superset container and +# at the very bottom of this docker-compose template. +# +# Note that the `superset` service name/hostname will not be unique on the +# global `dokploy-network`. If you plan to: +# +# 1. deploy a second instance of superset on dokploy-network, and +# 2. have other containers on dokploy-network utilise the second instance's +# Superset API (https://superset.apache.org/docs/api) +# +# Please change the service name of the second instance. services: superset: image: amancevice/superset restart: always + #networks: + # - dokploy-network depends_on: - superset_postgres - superset_redis @@ -44,8 +66,7 @@ services: timeout: 10s retries: 3 - - superset_redis: +superset_redis: image: redis restart: always volumes: @@ -57,6 +78,9 @@ services: timeout: 10s retries: 3 +#networks: +# dokploy-network: +# external: true volumes: superset_postgres_data: From bbdda014d88c874386ec2d463ec63c52bf9399ac Mon Sep 17 00:00:00 2001 From: Dominik Koch Date: Thu, 13 Feb 2025 21:46:06 +0100 Subject: [PATCH 023/126] feat(template): update plausible --- apps/dokploy/templates/plausible/docker-compose.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/dokploy/templates/plausible/docker-compose.yml b/apps/dokploy/templates/plausible/docker-compose.yml index f17bdfa31..ad483ecf6 100644 --- a/apps/dokploy/templates/plausible/docker-compose.yml +++ b/apps/dokploy/templates/plausible/docker-compose.yml @@ -1,4 +1,3 @@ -version: "3.8" services: plausible_db: image: postgres:16-alpine @@ -24,7 +23,7 @@ services: hard: 262144 plausible: - image: ghcr.io/plausible/community-edition:v2.1.4 + image: ghcr.io/plausible/community-edition:v2.1.5 restart: always command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run" depends_on: From bf6c2698d4e5a60c8018d7b1d5166650d6296bc5 Mon Sep 17 00:00:00 2001 From: Dominik Koch Date: Thu, 13 Feb 2025 21:52:01 +0100 Subject: [PATCH 024/126] fix: update plausible version --- apps/dokploy/templates/templates.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 8143bbb2f..1c0a9a40c 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -34,7 +34,7 @@ export const templates: TemplateData[] = [ { id: "plausible", name: "Plausible", - version: "v2.1.4", + version: "v2.1.5", description: "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", logo: "plausible.svg", From ed54df9bd2889daa44a220f027d70e8b5a7dfa2a Mon Sep 17 00:00:00 2001 From: mafrasil Date: Fri, 14 Feb 2025 09:23:41 +0400 Subject: [PATCH 025/126] feat(template): add convex.dev --- apps/dokploy/public/templates/convex.svg | 5 + .../templates/convex/docker-compose.yml | 37 + apps/dokploy/templates/convex/index.ts | 38 + apps/dokploy/templates/templates.ts | 2975 +++++++++-------- 4 files changed, 1575 insertions(+), 1480 deletions(-) create mode 100644 apps/dokploy/public/templates/convex.svg create mode 100644 apps/dokploy/templates/convex/docker-compose.yml create mode 100644 apps/dokploy/templates/convex/index.ts diff --git a/apps/dokploy/public/templates/convex.svg b/apps/dokploy/public/templates/convex.svg new file mode 100644 index 000000000..8622c4c07 --- /dev/null +++ b/apps/dokploy/public/templates/convex.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/dokploy/templates/convex/docker-compose.yml b/apps/dokploy/templates/convex/docker-compose.yml new file mode 100644 index 000000000..12e2b5ada --- /dev/null +++ b/apps/dokploy/templates/convex/docker-compose.yml @@ -0,0 +1,37 @@ +services: + backend: + image: ghcr.io/get-convex/convex-backend:6c974d219776b753cd23d26f4a296629ff7c2cad + ports: + - "${PORT:-3210}:3210" + - "${SITE_PROXY_PORT:-3211}:3211" + volumes: + - data:/convex/data + environment: + - INSTANCE_NAME=${INSTANCE_NAME:-} + - INSTANCE_SECRET=${INSTANCE_SECRET:-} + - CONVEX_RELEASE_VERSION_DEV=${CONVEX_RELEASE_VERSION_DEV:-} + - ACTIONS_USER_TIMEOUT_SECS=${ACTIONS_USER_TIMEOUT_SECS:-} + - CONVEX_CLOUD_ORIGIN=${CONVEX_CLOUD_ORIGIN:-http://127.0.0.1:3210} + - CONVEX_SITE_ORIGIN=${CONVEX_SITE_ORIGIN:-http://127.0.0.1:3211} + - DATABASE_URL=${DATABASE_URL:-} + - DISABLE_BEACON=${DISABLE_BEACON:-} + - REDACT_LOGS_TO_CLIENT=${REDACT_LOGS_TO_CLIENT:-} + - RUST_LOG=${RUST_LOG:-info} + - RUST_BACKTRACE=${RUST_BACKTRACE:-} + healthcheck: + test: curl -f http://localhost:3210/version + interval: 5s + start_period: 5s + + dashboard: + image: ghcr.io/get-convex/convex-dashboard:4499dd4fd7f2148687a7774599c613d052950f46 + ports: + - "${DASHBOARD_PORT:-6791}:6791" + environment: + - NEXT_PUBLIC_DEPLOYMENT_URL=${NEXT_PUBLIC_DEPLOYMENT_URL:-http://127.0.0.1:3210} + depends_on: + backend: + condition: service_healthy + +volumes: + data: diff --git a/apps/dokploy/templates/convex/index.ts b/apps/dokploy/templates/convex/index.ts new file mode 100644 index 000000000..0b3a981ac --- /dev/null +++ b/apps/dokploy/templates/convex/index.ts @@ -0,0 +1,38 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const dashboardDomain = generateRandomDomain(schema); + const backendDomain = generateRandomDomain(schema); + const actionsDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: dashboardDomain, + port: 6791, + serviceName: "dashboard", + }, + { + host: backendDomain, + port: 3210, + serviceName: "backend", + }, + { + host: actionsDomain, + port: 3211, + serviceName: "backend", + }, + ]; + + const envs = [ + `NEXT_PUBLIC_DEPLOYMENT_URL=${backendDomain}`, + `CONVEX_CLOUD_ORIGIN=${backendDomain}`, + `CONVEX_SITE_ORIGIN=${actionsDomain}`, + ]; + + return { envs, domains }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 8143bbb2f..f708c55ee 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,1486 +1,1501 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ - { - id: "supabase", - name: "SupaBase", - version: "1.24.07", - description: - "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", - links: { - github: "https://github.com/supabase/supabase", - website: "https://supabase.com/", - docs: "https://supabase.com/docs/guides/self-hosting", - }, - logo: "supabase.svg", - load: () => import("./supabase/index").then((m) => m.generate), - tags: ["database", "firebase", "postgres"], - }, - { - id: "pocketbase", - name: "Pocketbase", - version: "v0.22.12", - description: - "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", - links: { - github: "https://github.com/pocketbase/pocketbase", - website: "https://pocketbase.io/", - docs: "https://pocketbase.io/docs/", - }, - logo: "pocketbase.svg", - load: () => import("./pocketbase/index").then((m) => m.generate), - tags: ["database", "cms", "headless"], - }, - { - id: "plausible", - name: "Plausible", - version: "v2.1.4", - description: - "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "plausible.svg", - links: { - github: "https://github.com/plausible/plausible", - website: "https://plausible.io/", - docs: "https://plausible.io/docs", - }, - tags: ["analytics"], - load: () => import("./plausible/index").then((m) => m.generate), - }, - { - id: "calcom", - name: "Calcom", - version: "v2.7.6", - description: - "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", + { + id: "supabase", + name: "SupaBase", + version: "1.24.07", + description: + "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", + links: { + github: "https://github.com/supabase/supabase", + website: "https://supabase.com/", + docs: "https://supabase.com/docs/guides/self-hosting", + }, + logo: "supabase.svg", + load: () => import("./supabase/index").then((m) => m.generate), + tags: ["database", "firebase", "postgres"], + }, + { + id: "pocketbase", + name: "Pocketbase", + version: "v0.22.12", + description: + "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", + links: { + github: "https://github.com/pocketbase/pocketbase", + website: "https://pocketbase.io/", + docs: "https://pocketbase.io/docs/", + }, + logo: "pocketbase.svg", + load: () => import("./pocketbase/index").then((m) => m.generate), + tags: ["database", "cms", "headless"], + }, + { + id: "plausible", + name: "Plausible", + version: "v2.1.4", + description: + "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "plausible.svg", + links: { + github: "https://github.com/plausible/plausible", + website: "https://plausible.io/", + docs: "https://plausible.io/docs", + }, + tags: ["analytics"], + load: () => import("./plausible/index").then((m) => m.generate), + }, + { + id: "calcom", + name: "Calcom", + version: "v2.7.6", + description: + "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", - links: { - github: "https://github.com/calcom/cal.com", - website: "https://cal.com/", - docs: "https://cal.com/docs", - }, - logo: "calcom.jpg", - tags: ["scheduling", "booking"], - load: () => import("./calcom/index").then((m) => m.generate), - }, - { - id: "grafana", - name: "Grafana", - version: "9.5.20", - description: - "Grafana is an open source platform for data visualization and monitoring.", - logo: "grafana.svg", - links: { - github: "https://github.com/grafana/grafana", - website: "https://grafana.com/", - docs: "https://grafana.com/docs/", - }, - tags: ["monitoring"], - load: () => import("./grafana/index").then((m) => m.generate), - }, - { - id: "directus", - name: "Directus", - version: "11.0.2", - description: - "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", - logo: "directus.jpg", - links: { - github: "https://github.com/directus/directus", - website: "https://directus.io/", - docs: "https://docs.directus.io/", - }, - tags: ["cms"], - load: () => import("./directus/index").then((m) => m.generate), - }, - { - id: "baserow", - name: "Baserow", - version: "1.25.2", - description: - "Baserow is an open source database management tool that allows you to create and manage databases.", - logo: "baserow.webp", - links: { - github: "https://github.com/Baserow/baserow", - website: "https://baserow.io/", - docs: "https://baserow.io/docs/index", - }, - tags: ["database"], - load: () => import("./baserow/index").then((m) => m.generate), - }, - { - id: "budibase", - name: "Budibase", - version: "3.2.25", - description: - "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", - logo: "budibase.svg", - links: { - github: "https://github.com/Budibase/budibase", - website: "https://budibase.com/", - docs: "https://docs.budibase.com/docs/", - }, - tags: ["database", "low-code", "nocode", "applications"], - load: () => import("./budibase/index").then((m) => m.generate), - }, - { - id: "ghost", - name: "Ghost", - version: "5.0.0", - description: - "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", - logo: "ghost.jpeg", - links: { - github: "https://github.com/TryGhost/Ghost", - website: "https://ghost.org/", - docs: "https://ghost.org/docs/", - }, - tags: ["cms"], - load: () => import("./ghost/index").then((m) => m.generate), - }, - { - id: "uptime-kuma", - name: "Uptime Kuma", - version: "1.23.15", - description: - "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", - logo: "uptime-kuma.png", - links: { - github: "https://github.com/louislam/uptime-kuma", - website: "https://uptime.kuma.pet/", - docs: "https://github.com/louislam/uptime-kuma/wiki", - }, - tags: ["monitoring"], - load: () => import("./uptime-kuma/index").then((m) => m.generate), - }, - { - id: "n8n", - name: "n8n", - version: "1.70.3", - description: - "n8n is an open source low-code platform for automating workflows and integrations.", - logo: "n8n.png", - links: { - github: "https://github.com/n8n-io/n8n", - website: "https://n8n.io/", - docs: "https://docs.n8n.io/", - }, - tags: ["automation"], - load: () => import("./n8n/index").then((m) => m.generate), - }, - { - id: "wordpress", - name: "Wordpress", - version: "6.7.1", - description: - "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", - logo: "wordpress.png", - links: { - github: "https://github.com/WordPress/WordPress", - website: "https://wordpress.org/", - docs: "https://wordpress.org/documentation/", - }, - tags: ["cms"], - load: () => import("./wordpress/index").then((m) => m.generate), - }, - { - id: "odoo", - name: "Odoo", - version: "16.0", - description: - "Odoo is a free and open source business management software that helps you manage your company's operations.", - logo: "odoo.png", - links: { - github: "https://github.com/odoo/odoo", - website: "https://odoo.com/", - docs: "https://www.odoo.com/documentation/", - }, - tags: ["cms"], - load: () => import("./odoo/index").then((m) => m.generate), - }, - { - id: "appsmith", - name: "Appsmith", - version: "v1.29", - description: - "Appsmith is a free and open source platform for building internal tools and applications.", - logo: "appsmith.png", - links: { - github: "https://github.com/appsmithorg/appsmith", - website: "https://appsmith.com/", - docs: "https://docs.appsmith.com/", - }, - tags: ["cms"], - load: () => import("./appsmith/index").then((m) => m.generate), - }, - { - id: "excalidraw", - name: "Excalidraw", - version: "latest", - description: - "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", - logo: "excalidraw.jpg", - links: { - github: "https://github.com/excalidraw/excalidraw", - website: "https://excalidraw.com/", - docs: "https://docs.excalidraw.com/", - }, - tags: ["drawing"], - load: () => import("./excalidraw/index").then((m) => m.generate), - }, - { - id: "documenso", - name: "Documenso", - version: "v1.5.6", - description: - "Documenso is the open source alternative to DocuSign for signing documents digitally", - links: { - github: "https://github.com/documenso/documenso", - website: "https://documenso.com/", - docs: "https://documenso.com/docs", - }, - logo: "documenso.png", - tags: ["document-signing"], - load: () => import("./documenso/index").then((m) => m.generate), - }, - { - id: "nocodb", - name: "NocoDB", - version: "0.257.2", - description: - "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", + links: { + github: "https://github.com/calcom/cal.com", + website: "https://cal.com/", + docs: "https://cal.com/docs", + }, + logo: "calcom.jpg", + tags: ["scheduling", "booking"], + load: () => import("./calcom/index").then((m) => m.generate), + }, + { + id: "grafana", + name: "Grafana", + version: "9.5.20", + description: + "Grafana is an open source platform for data visualization and monitoring.", + logo: "grafana.svg", + links: { + github: "https://github.com/grafana/grafana", + website: "https://grafana.com/", + docs: "https://grafana.com/docs/", + }, + tags: ["monitoring"], + load: () => import("./grafana/index").then((m) => m.generate), + }, + { + id: "directus", + name: "Directus", + version: "11.0.2", + description: + "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", + logo: "directus.jpg", + links: { + github: "https://github.com/directus/directus", + website: "https://directus.io/", + docs: "https://docs.directus.io/", + }, + tags: ["cms"], + load: () => import("./directus/index").then((m) => m.generate), + }, + { + id: "baserow", + name: "Baserow", + version: "1.25.2", + description: + "Baserow is an open source database management tool that allows you to create and manage databases.", + logo: "baserow.webp", + links: { + github: "https://github.com/Baserow/baserow", + website: "https://baserow.io/", + docs: "https://baserow.io/docs/index", + }, + tags: ["database"], + load: () => import("./baserow/index").then((m) => m.generate), + }, + { + id: "budibase", + name: "Budibase", + version: "3.2.25", + description: + "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", + logo: "budibase.svg", + links: { + github: "https://github.com/Budibase/budibase", + website: "https://budibase.com/", + docs: "https://docs.budibase.com/docs/", + }, + tags: ["database", "low-code", "nocode", "applications"], + load: () => import("./budibase/index").then((m) => m.generate), + }, + { + id: "ghost", + name: "Ghost", + version: "5.0.0", + description: + "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", + logo: "ghost.jpeg", + links: { + github: "https://github.com/TryGhost/Ghost", + website: "https://ghost.org/", + docs: "https://ghost.org/docs/", + }, + tags: ["cms"], + load: () => import("./ghost/index").then((m) => m.generate), + }, + { + id: "uptime-kuma", + name: "Uptime Kuma", + version: "1.23.15", + description: + "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", + logo: "uptime-kuma.png", + links: { + github: "https://github.com/louislam/uptime-kuma", + website: "https://uptime.kuma.pet/", + docs: "https://github.com/louislam/uptime-kuma/wiki", + }, + tags: ["monitoring"], + load: () => import("./uptime-kuma/index").then((m) => m.generate), + }, + { + id: "n8n", + name: "n8n", + version: "1.70.3", + description: + "n8n is an open source low-code platform for automating workflows and integrations.", + logo: "n8n.png", + links: { + github: "https://github.com/n8n-io/n8n", + website: "https://n8n.io/", + docs: "https://docs.n8n.io/", + }, + tags: ["automation"], + load: () => import("./n8n/index").then((m) => m.generate), + }, + { + id: "wordpress", + name: "Wordpress", + version: "6.7.1", + description: + "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", + logo: "wordpress.png", + links: { + github: "https://github.com/WordPress/WordPress", + website: "https://wordpress.org/", + docs: "https://wordpress.org/documentation/", + }, + tags: ["cms"], + load: () => import("./wordpress/index").then((m) => m.generate), + }, + { + id: "odoo", + name: "Odoo", + version: "16.0", + description: + "Odoo is a free and open source business management software that helps you manage your company's operations.", + logo: "odoo.png", + links: { + github: "https://github.com/odoo/odoo", + website: "https://odoo.com/", + docs: "https://www.odoo.com/documentation/", + }, + tags: ["cms"], + load: () => import("./odoo/index").then((m) => m.generate), + }, + { + id: "appsmith", + name: "Appsmith", + version: "v1.29", + description: + "Appsmith is a free and open source platform for building internal tools and applications.", + logo: "appsmith.png", + links: { + github: "https://github.com/appsmithorg/appsmith", + website: "https://appsmith.com/", + docs: "https://docs.appsmith.com/", + }, + tags: ["cms"], + load: () => import("./appsmith/index").then((m) => m.generate), + }, + { + id: "excalidraw", + name: "Excalidraw", + version: "latest", + description: + "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", + logo: "excalidraw.jpg", + links: { + github: "https://github.com/excalidraw/excalidraw", + website: "https://excalidraw.com/", + docs: "https://docs.excalidraw.com/", + }, + tags: ["drawing"], + load: () => import("./excalidraw/index").then((m) => m.generate), + }, + { + id: "documenso", + name: "Documenso", + version: "v1.5.6", + description: + "Documenso is the open source alternative to DocuSign for signing documents digitally", + links: { + github: "https://github.com/documenso/documenso", + website: "https://documenso.com/", + docs: "https://documenso.com/docs", + }, + logo: "documenso.png", + tags: ["document-signing"], + load: () => import("./documenso/index").then((m) => m.generate), + }, + { + id: "nocodb", + name: "NocoDB", + version: "0.257.2", + description: + "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", - links: { - github: "https://github.com/nocodb/nocodb", - website: "https://nocodb.com/", - docs: "https://docs.nocodb.com/", - }, - logo: "nocodb.png", - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./nocodb/index").then((m) => m.generate), - }, - { - id: "meilisearch", - name: "Meilisearch", - version: "v1.8.3", - description: - "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", - logo: "meilisearch.png", - links: { - github: "https://github.com/meilisearch/meilisearch", - website: "https://www.meilisearch.com/", - docs: "https://docs.meilisearch.com/", - }, - tags: ["search"], - load: () => import("./meilisearch/index").then((m) => m.generate), - }, - { - id: "phpmyadmin", - name: "Phpmyadmin", - version: "5.2.1", - description: - "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", - logo: "phpmyadmin.png", - links: { - github: "https://github.com/phpmyadmin/phpmyadmin", - website: "https://www.phpmyadmin.net/", - docs: "https://www.phpmyadmin.net/docs/", - }, - tags: ["database"], - load: () => import("./phpmyadmin/index").then((m) => m.generate), - }, - { - id: "rocketchat", - name: "Rocketchat", - version: "6.9.2", - description: - "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", - logo: "rocketchat.png", - links: { - github: "https://github.com/RocketChat/Rocket.Chat", - website: "https://rocket.chat/", - docs: "https://rocket.chat/docs/", - }, - tags: ["chat"], - load: () => import("./rocketchat/index").then((m) => m.generate), - }, - { - id: "minio", - name: "Minio", - description: - "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", - logo: "minio.png", - version: "latest", - links: { - github: "https://github.com/minio/minio", - website: "https://minio.io/", - docs: "https://docs.minio.io/", - }, - tags: ["storage"], - load: () => import("./minio/index").then((m) => m.generate), - }, - { - id: "metabase", - name: "Metabase", - version: "v0.50.8", - description: - "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", - logo: "metabase.png", - links: { - github: "https://github.com/metabase/metabase", - website: "https://www.metabase.com/", - docs: "https://www.metabase.com/docs/", - }, - tags: ["database", "dashboard"], - load: () => import("./metabase/index").then((m) => m.generate), - }, - { - id: "glitchtip", - name: "Glitchtip", - version: "v4.0", - description: "Glitchtip is simple, open source error tracking", - logo: "glitchtip.png", - links: { - github: "https://gitlab.com/glitchtip/", - website: "https://glitchtip.com/", - docs: "https://glitchtip.com/documentation", - }, - tags: ["hosting"], - load: () => import("./glitchtip/index").then((m) => m.generate), - }, - { - id: "open-webui", - name: "Open WebUI", - version: "v0.3.7", - description: - "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", - logo: "open-webui.png", - links: { - github: "https://github.com/open-webui/open-webui", - website: "https://openwebui.com/", - docs: "https://docs.openwebui.com/", - }, - tags: ["chat"], - load: () => import("./open-webui/index").then((m) => m.generate), - }, - { - id: "listmonk", - name: "Listmonk", - version: "v3.0.0", - description: - "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", - logo: "listmonk.png", - links: { - github: "https://github.com/knadh/listmonk", - website: "https://listmonk.app/", - docs: "https://listmonk.app/docs/", - }, - tags: ["email", "newsletter", "mailing-list"], - load: () => import("./listmonk/index").then((m) => m.generate), - }, - { - id: "doublezero", - name: "Double Zero", - version: "v0.2.1", - description: - "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", - logo: "doublezero.svg", - links: { - github: "https://github.com/technomancy-dev/00", - website: "https://www.double-zero.cloud/", - docs: "https://github.com/technomancy-dev/00", - }, - tags: ["email"], - load: () => import("./doublezero/index").then((m) => m.generate), - }, - { - id: "umami", - name: "Umami", - version: "v2.14.0", - description: - "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", - logo: "umami.png", - links: { - github: "https://github.com/umami-software/umami", - website: "https://umami.is", - docs: "https://umami.is/docs", - }, - tags: ["analytics"], - load: () => import("./umami/index").then((m) => m.generate), - }, - { - id: "jellyfin", - name: "jellyfin", - version: "v10.9.7", - description: - "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", - logo: "jellyfin.svg", - links: { - github: "https://github.com/jellyfin/jellyfin", - website: "https://jellyfin.org/", - docs: "https://jellyfin.org/docs/", - }, - tags: ["media system"], - load: () => import("./jellyfin/index").then((m) => m.generate), - }, - { - id: "teable", - name: "teable", - version: "v1.3.1-alpha-build.460", - description: - "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", - logo: "teable.png", - links: { - github: "https://github.com/teableio/teable", - website: "https://teable.io/", - docs: "https://help.teable.io/", - }, - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./teable/index").then((m) => m.generate), - }, - { - id: "zipline", - name: "Zipline", - version: "v3.7.9", - description: - "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", - logo: "zipline.png", - links: { - github: "https://github.com/diced/zipline", - website: "https://zipline.diced.sh/", - docs: "https://zipline.diced.sh/docs/", - }, - tags: ["media system", "storage"], - load: () => import("./zipline/index").then((m) => m.generate), - }, - { - id: "soketi", - name: "Soketi", - version: "v1.6.1-16", - description: - "Soketi is your simple, fast, and resilient open-source WebSockets server.", - logo: "soketi.png", - links: { - github: "https://github.com/soketi/soketi", - website: "https://soketi.app/", - docs: "https://docs.soketi.app/", - }, - tags: ["chat"], - load: () => import("./soketi/index").then((m) => m.generate), - }, - { - id: "aptabase", - name: "Aptabase", - version: "v1.0.0", - description: - "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "aptabase.svg", - links: { - github: "https://github.com/aptabase/aptabase", - website: "https://aptabase.com/", - docs: "https://github.com/aptabase/aptabase/blob/main/README.md", - }, - tags: ["analytics", "self-hosted"], - load: () => import("./aptabase/index").then((m) => m.generate), - }, - { - id: "typebot", - name: "Typebot", - version: "2.27.0", - description: "Typebot is an open-source chatbot builder platform.", - logo: "typebot.svg", - links: { - github: "https://github.com/baptisteArno/typebot.io", - website: "https://typebot.io/", - docs: "https://docs.typebot.io/get-started/introduction", - }, - tags: ["chatbot", "builder", "open-source"], - load: () => import("./typebot/index").then((m) => m.generate), - }, - { - id: "gitea", - name: "Gitea", - version: "1.22.3", - description: - "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", - logo: "gitea.png", - links: { - github: "https://github.com/go-gitea/gitea.git", - website: "https://gitea.com/", - docs: "https://docs.gitea.com/installation/install-with-docker", - }, - tags: ["self-hosted", "storage"], - load: () => import("./gitea/index").then((m) => m.generate), - }, - { - id: "roundcube", - name: "Roundcube", - version: "1.6.9", - description: - "Free and open source webmail software for the masses, written in PHP.", - logo: "roundcube.svg", - links: { - github: "https://github.com/roundcube/roundcubemail", - website: "https://roundcube.net/", - docs: "https://roundcube.net/about/", - }, - tags: ["self-hosted", "email", "webmail"], - load: () => import("./roundcube/index").then((m) => m.generate), - }, - { - id: "filebrowser", - name: "File Browser", - version: "2.31.2", - description: - "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", - logo: "filebrowser.svg", - links: { - github: "https://github.com/filebrowser/filebrowser", - website: "https://filebrowser.org/", - docs: "https://filebrowser.org/", - }, - tags: ["file-manager", "storage"], - load: () => import("./filebrowser/index").then((m) => m.generate), - }, - { - id: "tolgee", - name: "Tolgee", - version: "v3.80.4", - description: - "Developer & translator friendly web-based localization platform", - logo: "tolgee.svg", - links: { - github: "https://github.com/tolgee/tolgee-platform", - website: "https://tolgee.io", - docs: "https://tolgee.io/platform", - }, - tags: ["self-hosted", "i18n", "localization", "translations"], - load: () => import("./tolgee/index").then((m) => m.generate), - }, - { - id: "portainer", - name: "Portainer", - version: "2.21.4", - description: - "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", - logo: "portainer.svg", - links: { - github: "https://github.com/portainer/portainer", - website: "https://www.portainer.io/", - docs: "https://docs.portainer.io/", - }, - tags: ["cloud", "monitoring"], - load: () => import("./portainer/index").then((m) => m.generate), - }, - { - id: "influxdb", - name: "InfluxDB", - version: "2.7.10", - description: - "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", - logo: "influxdb.png", - links: { - github: "https://github.com/influxdata/influxdb", - website: "https://www.influxdata.com/", - docs: "https://docs.influxdata.com/influxdb/v2/", - }, - tags: ["self-hosted", "open-source", "storage", "database"], - load: () => import("./influxdb/index").then((m) => m.generate), - }, - { - id: "infisical", - name: "Infisical", - version: "0.90.1", - description: - "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", - logo: "infisical.jpg", - links: { - github: "https://github.com/Infisical/infisical", - website: "https://infisical.com/", - docs: "https://infisical.com/docs/documentation/getting-started/introduction", - }, - tags: ["self-hosted", "open-source"], - load: () => import("./infisical/index").then((m) => m.generate), - }, - { - id: "docmost", - name: "Docmost", - version: "0.4.1", - description: - "Docmost, is an open-source collaborative wiki and documentation software.", - logo: "docmost.png", - links: { - github: "https://github.com/docmost/docmost", - website: "https://docmost.com/", - docs: "https://docmost.com/docs/", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./docmost/index").then((m) => m.generate), - }, - { - id: "vaultwarden", - name: "Vaultwarden", - version: "1.32.7", - description: - "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", - logo: "vaultwarden.svg", - links: { - github: "https://github.com/dani-garcia/vaultwarden", - website: "", - docs: "https://github.com/dani-garcia/vaultwarden/wiki", - }, - tags: ["open-source"], - load: () => import("./vaultwarden/index").then((m) => m.generate), - }, - { - id: "hi-events", - name: "Hi.events", - version: "0.8.0-beta.1", - description: - "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", - logo: "hi-events.svg", - links: { - github: "https://github.com/HiEventsDev/hi.events", - website: "https://hi.events/", - docs: "https://hi.events/docs", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./hi-events/index").then((m) => m.generate), - }, - { - id: "windows", - name: "Windows (dockerized)", - version: "4.00", - description: "Windows inside a Docker container.", - logo: "windows.png", - links: { - github: "https://github.com/dockur/windows", - website: "", - docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./windows/index").then((m) => m.generate), - }, - { - id: "macos", - name: "MacOS (dockerized)", - version: "1.14", - description: "MacOS inside a Docker container.", - logo: "macos.png", - links: { - github: "https://github.com/dockur/macos", - website: "", - docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./macos/index").then((m) => m.generate), - }, - { - id: "coder", - name: "Coder", - version: "2.15.3", - description: - "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", - logo: "coder.svg", - links: { - github: "https://github.com/coder/coder", - website: "https://coder.com/", - docs: "https://coder.com/docs", - }, - tags: ["self-hosted", "open-source", "builder"], - load: () => import("./coder/index").then((m) => m.generate), - }, - { - id: "stirling", - name: "Stirling PDF", - version: "0.30.1", - description: "A locally hosted one-stop shop for all your PDF needs", - logo: "stirling.svg", - links: { - github: "https://github.com/Stirling-Tools/Stirling-PDF", - website: "https://www.stirlingpdf.com/", - docs: "https://docs.stirlingpdf.com/", - }, - tags: ["pdf", "tools"], - load: () => import("./stirling/index").then((m) => m.generate), - }, - { - id: "lobe-chat", - name: "Lobe Chat", - version: "v1.26.1", - description: "Lobe Chat - an open-source, modern-design AI chat framework.", - logo: "lobe-chat.png", - links: { - github: "https://github.com/lobehub/lobe-chat", - website: "https://chat-preview.lobehub.com/", - docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", - }, - tags: ["IA", "chat"], - load: () => import("./lobe-chat/index").then((m) => m.generate), - }, - { - id: "peppermint", - name: "Peppermint", - version: "latest", - description: - "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", - logo: "peppermint.svg", - links: { - github: "https://github.com/Peppermint-Lab/peppermint", - website: "https://peppermint.sh/", - docs: "https://docs.peppermint.sh/", - }, - tags: ["api", "development", "documentation"], - load: () => import("./peppermint/index").then((m) => m.generate), - }, - { - id: "windmill", - name: "Windmill", - version: "latest", - description: - "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", - logo: "windmill.svg", - links: { - github: "https://github.com/windmill-labs/windmill", - website: "https://www.windmill.dev/", - docs: "https://docs.windmill.dev/", - }, - tags: ["workflow", "automation", "development"], - load: () => import("./windmill/index").then((m) => m.generate), - }, - { - id: "activepieces", - name: "Activepieces", - version: "0.35.0", - description: - "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", - logo: "activepieces.svg", - links: { - github: "https://github.com/activepieces/activepieces", - website: "https://www.activepieces.com/", - docs: "https://www.activepieces.com/docs", - }, - tags: ["automation", "workflow", "no-code"], - load: () => import("./activepieces/index").then((m) => m.generate), - }, - { - id: "invoiceshelf", - name: "InvoiceShelf", - version: "latest", - description: - "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", - logo: "invoiceshelf.png", - links: { - github: "https://github.com/InvoiceShelf/invoiceshelf", - website: "https://invoiceshelf.com", - docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./invoiceshelf/index").then((m) => m.generate), - }, - { - id: "postiz", - name: "Postiz", - version: "latest", - description: - "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", - logo: "postiz.png", - links: { - github: "https://github.com/gitroomhq/postiz", - website: "https://postiz.com", - docs: "https://docs.postiz.com", - }, - tags: ["cms", "content-management", "publishing"], - load: () => import("./postiz/index").then((m) => m.generate), - }, - { - id: "slash", - name: "Slash", - version: "latest", - description: - "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", - logo: "slash.png", - links: { - github: "https://github.com/yourselfhosted/slash", - website: "https://github.com/yourselfhosted/slash#readme", - docs: "https://github.com/yourselfhosted/slash/wiki", - }, - tags: ["bookmarks", "link-shortener", "self-hosted"], - load: () => import("./slash/index").then((m) => m.generate), - }, - { - id: "discord-tickets", - name: "Discord Tickets", - version: "4.0.21", - description: - "An open-source Discord bot for creating and managing support ticket channels.", - logo: "discord-tickets.png", - links: { - github: "https://github.com/discord-tickets/bot", - website: "https://discordtickets.app", - docs: "https://discordtickets.app/self-hosting/installation/docker/", - }, - tags: ["discord", "tickets", "support"], - load: () => import("./discord-tickets/index").then((m) => m.generate), - }, - { - id: "nextcloud-aio", - name: "Nextcloud All in One", - version: "30.0.2", - description: - "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", - logo: "nextcloud-aio.svg", - links: { - github: "https://github.com/nextcloud/docker", - website: "https://nextcloud.com/", - docs: "https://docs.nextcloud.com/", - }, - tags: ["file-manager", "sync"], - load: () => import("./nextcloud-aio/index").then((m) => m.generate), - }, - { - id: "blender", - name: "Blender", - version: "latest", - description: - "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", - logo: "blender.svg", - links: { - github: "https://github.com/linuxserver/docker-blender", - website: "https://www.blender.org/", - docs: "https://docs.blender.org/", - }, - tags: ["3d", "rendering", "animation"], - load: () => import("./blender/index").then((m) => m.generate), - }, - { - id: "heyform", - name: "HeyForm", - version: "latest", - description: - "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", - logo: "heyform.svg", - links: { - github: "https://github.com/heyform/heyform", - website: "https://heyform.net", - docs: "https://docs.heyform.net", - }, - tags: ["form", "builder", "questionnaire", "quiz", "survey"], - load: () => import("./heyform/index").then((m) => m.generate), - }, - { - id: "chatwoot", - name: "Chatwoot", - version: "v3.14.1", - description: - "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", - logo: "chatwoot.svg", - links: { - github: "https://github.com/chatwoot/chatwoot", - website: "https://www.chatwoot.com", - docs: "https://www.chatwoot.com/docs", - }, - tags: ["support", "chat", "customer-service"], - load: () => import("./chatwoot/index").then((m) => m.generate), - }, - { - id: "discourse", - name: "Discourse", - version: "3.3.2", - description: - "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", - logo: "discourse.svg", - links: { - github: "https://github.com/discourse/discourse", - website: "https://www.discourse.org/", - docs: "https://meta.discourse.org/", - }, - tags: ["forum", "community", "discussion"], - load: () => import("./discourse/index").then((m) => m.generate), - }, - { - id: "immich", - name: "Immich", - version: "v1.121.0", - description: - "High performance self-hosted photo and video backup solution directly from your mobile phone.", - logo: "immich.svg", - links: { - github: "https://github.com/immich-app/immich", - website: "https://immich.app/", - docs: "https://immich.app/docs/overview/introduction", - }, - tags: ["photos", "videos", "backup", "media"], - load: () => import("./immich/index").then((m) => m.generate), - }, - { - id: "twenty", - name: "Twenty CRM", - version: "latest", - description: - "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", - logo: "twenty.svg", - links: { - github: "https://github.com/twentyhq/twenty", - website: "https://twenty.com", - docs: "https://docs.twenty.com", - }, - tags: ["crm", "sales", "business"], - load: () => import("./twenty/index").then((m) => m.generate), - }, - { - id: "yourls", - name: "YOURLS", - version: "1.9.2", - description: - "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", - logo: "yourls.svg", - links: { - github: "https://github.com/YOURLS/YOURLS", - website: "https://yourls.org/", - docs: "https://yourls.org/#documentation", - }, - tags: ["url-shortener", "php"], - load: () => import("./yourls/index").then((m) => m.generate), - }, - { - id: "ryot", - name: "Ryot", - version: "v7.10", - description: - "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", - logo: "ryot.png", - links: { - github: "https://github.com/IgnisDa/ryot", - website: "https://ryot.io/", - docs: "https://docs.ryot.io/", - }, - tags: ["media", "tracking", "self-hosted"], - load: () => import("./ryot/index").then((m) => m.generate), - }, - { - id: "photoprism", - name: "Photoprism", - version: "latest", - description: - "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", - logo: "photoprism.svg", - links: { - github: "https://github.com/photoprism/photoprism", - website: "https://www.photoprism.app/", - docs: "https://docs.photoprism.app/", - }, - tags: ["media", "photos", "self-hosted"], - load: () => import("./photoprism/index").then((m) => m.generate), - }, - { - id: "ontime", - name: "Ontime", - version: "v3.8.0", - description: - "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", - logo: "ontime.png", - links: { - github: "https://github.com/cpvalente/ontime/", - website: "https://getontime.no", - docs: "https://docs.getontime.no", - }, - tags: ["event"], - load: () => import("./ontime/index").then((m) => m.generate), - }, - { - id: "triggerdotdev", - name: "Trigger.dev", - version: "v3", - description: - "Trigger is a platform for building event-driven applications.", - logo: "triggerdotdev.svg", - links: { - github: "https://github.com/triggerdotdev/trigger.dev", - website: "https://trigger.dev/", - docs: "https://trigger.dev/docs", - }, - tags: ["event-driven", "applications"], - load: () => import("./triggerdotdev/index").then((m) => m.generate), - }, - { - id: "browserless", - name: "Browserless", - version: "2.23.0", - description: - "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", - logo: "browserless.svg", - links: { - github: "https://github.com/browserless/browserless", - website: "https://www.browserless.io/", - docs: "https://docs.browserless.io/", - }, - tags: ["browser", "automation"], - load: () => import("./browserless/index").then((m) => m.generate), - }, - { - id: "drawio", - name: "draw.io", - version: "24.7.17", - description: - "draw.io is a configurable diagramming/whiteboarding visualization application.", - logo: "drawio.svg", - links: { - github: "https://github.com/jgraph/drawio", - website: "https://draw.io/", - docs: "https://www.drawio.com/doc/", - }, - tags: ["drawing", "diagrams"], - load: () => import("./drawio/index").then((m) => m.generate), - }, - { - id: "kimai", - name: "Kimai", - version: "2.26.0", - description: - "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", - logo: "kimai.svg", - links: { - github: "https://github.com/kimai/kimai", - website: "https://www.kimai.org", - docs: "https://www.kimai.org/documentation", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./kimai/index").then((m) => m.generate), - }, - { - id: "logto", - name: "Logto", - version: "1.22.0", - description: - "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", - logo: "logto.png", - links: { - github: "https://github.com/logto-io/logto", - website: "https://logto.io/", - docs: "https://docs.logto.io/introduction", - }, - tags: ["identity", "auth"], - load: () => import("./logto/index").then((m) => m.generate), - }, - { - id: "penpot", - name: "Penpot", - version: "2.3.2", - description: - "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", - logo: "penpot.svg", - links: { - github: "https://github.com/penpot/penpot", - website: "https://penpot.app/", - docs: "https://docs.penpot.app/", - }, - tags: ["design", "collaboration"], - load: () => import("./penpot/index").then((m) => m.generate), - }, - { - id: "huly", - name: "Huly", - version: "0.6.377", - description: - "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", - logo: "huly.svg", - links: { - github: "https://github.com/hcengineering/huly-selfhost", - website: "https://huly.io/", - docs: "https://docs.huly.io/", - }, - tags: ["project-management", "community", "discussion"], - load: () => import("./huly/index").then((m) => m.generate), - }, - { - id: "unsend", - name: "Unsend", - version: "v1.3.2", - description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", - logo: "unsend.png", - links: { - github: "https://github.com/unsend-dev/unsend", - website: "https://unsend.dev/", - docs: "https://docs.unsend.dev/get-started/", - }, - tags: ["e-mail", "marketing", "business"], - load: () => import("./unsend/index").then((m) => m.generate), - }, - { - id: "langflow", - name: "Langflow", - version: "1.1.1", - description: - "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", - logo: "langflow.svg", - links: { - github: "https://github.com/langflow-ai/langflow/tree/main", - website: "https://www.langflow.org/", - docs: "https://docs.langflow.org/", - }, - tags: ["ai"], - load: () => import("./langflow/index").then((m) => m.generate), - }, - { - id: "elastic-search", - name: "Elasticsearch", - version: "8.10.2", - description: - "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", - logo: "elasticsearch.svg", - links: { - github: "https://github.com/elastic/elasticsearch", - website: "https://www.elastic.co/elasticsearch/", - docs: "https://docs.elastic.co/elasticsearch/", - }, - tags: ["search", "analytics"], - load: () => import("./elastic-search/index").then((m) => m.generate), - }, - { - id: "onedev", - name: "OneDev", - version: "11.6.6", - description: - "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", - logo: "onedev.png", - links: { - github: "https://github.com/theonedev/onedev/", - website: "https://onedev.io/", - docs: "https://docs.onedev.io/", - }, - tags: ["self-hosted", "development"], - load: () => import("./onedev/index").then((m) => m.generate), - }, - { - id: "unifi", - name: "Unifi Network", - version: "11.6.6", - description: - "Unifi Network is an open-source enterprise network management platform for wireless networks.", - logo: "unifi.webp", - links: { - github: "https://github.com/ubiquiti", - website: "https://www.ui.com/", - docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", - }, - tags: ["self-hosted", "networking"], - load: () => import("./unifi/index").then((m) => m.generate), - }, - { - id: "glpi", - name: "GLPI Project", - version: "10.0.16", - description: "The most complete open source service management software", - logo: "glpi.webp", - links: { - github: "https://github.com/glpi-project/glpi", - website: "https://glpi-project.org/", - docs: "https://glpi-project.org/documentation/", - }, - tags: ["self-hosted", "project-management", "management"], - load: () => import("./glpi/index").then((m) => m.generate), - }, - { - id: "checkmate", - name: "Checkmate", - version: "2.0.1", - description: - "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", - logo: "checkmate.png", - links: { - github: "https://github.com/bluewave-labs/checkmate", - website: "https://bluewavelabs.ca", - docs: "https://bluewavelabs.gitbook.io/checkmate", - }, - tags: ["self-hosted", "monitoring", "uptime"], - load: () => import("./checkmate/index").then((m) => m.generate), - }, - { - id: "gotenberg", - name: "Gotenberg", - version: "latest", - description: "Gotenberg is a Docker-powered stateless API for PDF files.", - logo: "gotenberg.png", - links: { - github: "https://github.com/gotenberg/gotenberg", - website: "https://gotenberg.dev", - docs: "https://gotenberg.dev/docs/getting-started/introduction", - }, - tags: ["api", "backend", "pdf", "tools"], - load: () => import("./gotenberg/index").then((m) => m.generate), - }, - { - id: "actualbudget", - name: "Actual Budget", - version: "latest", - description: - "A super fast and privacy-focused app for managing your finances.", - logo: "actualbudget.png", - links: { - github: "https://github.com/actualbudget/actual", - website: "https://actualbudget.org", - docs: "https://actualbudget.org/docs", - }, - tags: ["budgeting", "finance", "money"], - load: () => import("./actualbudget/index").then((m) => m.generate), - }, - { - id: "conduit", - name: "Conduit", - version: "v0.9.0", - description: - "Conduit is a simple, fast and reliable chat server powered by Matrix", - logo: "conduit.svg", - links: { - github: "https://gitlab.com/famedly/conduit", - website: "https://conduit.rs/", - docs: "https://docs.conduit.rs/", - }, - tags: ["matrix", "communication"], - load: () => import("./conduit/index").then((m) => m.generate), - }, - { - id: "evolutionapi", - name: "Evolution API", - version: "v2.1.2", - description: - "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", - logo: "evolutionapi.png", - links: { - github: "https://github.com/EvolutionAPI/evolution-api", - docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", - website: "https://evolution-api.com/opensource-whatsapp-api/", - }, - tags: ["api", "whatsapp", "messaging"], - load: () => import("./evolutionapi/index").then((m) => m.generate), - }, - { - id: "conduwuit", - name: "Conduwuit", - version: "latest", - description: - "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", - logo: "conduwuit.svg", - links: { - github: "https://github.com/girlbossceo/conduwuit", - website: "https://conduwuit.puppyirl.gay", - docs: "https://conduwuit.puppyirl.gay/configuration.html", - }, - tags: ["backend", "chat", "communication", "matrix", "server"], - load: () => import("./conduwuit/index").then((m) => m.generate), - }, - { - id: "cloudflared", - name: "Cloudflared", - version: "latest", - description: - "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", - logo: "cloudflared.svg", - links: { - github: "https://github.com/cloudflare/cloudflared", - website: - "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", - docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", - }, - tags: ["cloud", "networking", "security", "tunnel"], - load: () => import("./cloudflared/index").then((m) => m.generate), - }, - { - id: "couchdb", - name: "CouchDB", - version: "latest", - description: - "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", - logo: "couchdb.png", - links: { - github: "https://github.com/apache/couchdb", - website: "https://couchdb.apache.org/", - docs: "https://docs.couchdb.org/en/stable/", - }, - tags: ["database", "storage"], - load: () => import("./couchdb/index").then((m) => m.generate), - }, - { - id: "it-tools", - name: "IT Tools", - version: "latest", - description: "A collection of handy online it-tools for developers.", - logo: "it-tools.svg", - links: { - github: "https://github.com/CorentinTh/it-tools", - website: "https://it-tools.tech", - }, - tags: ["developer", "tools"], - load: () => import("./it-tools/index").then((m) => m.generate), - }, - { - id: "superset", - name: "Superset (Unofficial)", - version: "latest", - description: "Data visualization and data exploration platform.", - logo: "superset.svg", - links: { - github: "https://github.com/amancevice/docker-superset", - website: "https://superset.apache.org", - docs: "https://superset.apache.org/docs/intro", - }, - tags: ["analytics", "bi", "dashboard", "database", "sql"], - load: () => import("./superset/index").then((m) => m.generate), - }, - { - id: "glance", - name: "Glance", - version: "latest", - description: - "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", - logo: "glance.png", - links: { - github: "https://github.com/glanceapp/glance", - docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", - }, - tags: ["dashboard", "monitoring", "widgets", "rss"], - load: () => import("./glance/index").then((m) => m.generate), - }, - { - id: "homarr", - name: "Homarr", - version: "latest", - description: - "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", - logo: "homarr.png", - links: { - github: "https://github.com/homarr-labs/homarr", - docs: "https://homarr.dev/docs/getting-started/installation/docker", - website: "https://homarr.dev/", - }, - tags: ["dashboard", "monitoring"], - load: () => import("./homarr/index").then((m) => m.generate), - }, - { - id: "erpnext", - name: "ERPNext", - version: "version-15", - description: "100% Open Source and highly customizable ERP software.", - logo: "erpnext.svg", - links: { - github: "https://github.com/frappe/erpnext", - docs: "https://docs.frappe.io/erpnext", - website: "https://erpnext.com", - }, - tags: [ - "erp", - "accounts", - "manufacturing", - "retail", - "sales", - "pos", - "hrms", - ], - load: () => import("./erpnext/index").then((m) => m.generate), - }, - { - id: "maybe", - name: "Maybe", - version: "latest", - description: - "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", - logo: "maybe.svg", - links: { - github: "https://github.com/maybe-finance/maybe", - website: "https://maybe.finance/", - docs: "https://docs.maybe.finance/", - }, - tags: ["finance", "self-hosted"], - load: () => import("./maybe/index").then((m) => m.generate), - }, - { - id: "spacedrive", - name: "Spacedrive", - version: "latest", - description: - "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", - links: { - github: "https://github.com/spacedriveapp/spacedrive", - website: "https://spacedrive.com/", - docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", - }, - logo: "spacedrive.png", - tags: ["file-manager", "vdfs", "storage"], - load: () => import("./spacedrive/index").then((m) => m.generate), - }, - { - id: "alist", - name: "AList", - version: "v3.41.0", - description: - "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", - logo: "alist.svg", - links: { - github: "https://github.com/AlistGo/alist", - website: "https://alist.nn.ci", - docs: "https://alist.nn.ci/guide/install/docker.html", - }, - tags: ["file", "webdav", "storage"], - load: () => import("./alist/index").then((m) => m.generate), - }, - { - id: "answer", - name: "Answer", - version: "v1.4.1", - description: - "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", - logo: "answer.png", - links: { - github: "https://github.com/apache/answer", - website: "https://answer.apache.org/", - docs: "https://answer.apache.org/docs", - }, - tags: ["q&a", "self-hosted"], - load: () => import("./answer/index").then((m) => m.generate), - }, - { - id: "shlink", - name: "Shlink", - version: "stable", - description: - "URL shortener that can be used to serve shortened URLs under your own domain.", - logo: "shlink.svg", - links: { - github: "https://github.com/shlinkio/shlink", - website: "https://shlink.io", - docs: "https://shlink.io/documentation", - }, - tags: ["sharing", "shortener", "url"], - load: () => import("./shlink/index").then((m) => m.generate), - }, - { - id: "frappe-hr", - name: "Frappe HR", - version: "version-15", - description: - "Feature rich HR & Payroll software. 100% FOSS and customizable.", - logo: "frappe-hr.svg", - links: { - github: "https://github.com/frappe/hrms", - docs: "https://docs.frappe.io/hr", - website: "https://frappe.io/hr", - }, - tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], - load: () => import("./frappe-hr/index").then((m) => m.generate), - }, - { - id: "formbricks", - name: "Formbricks", - version: "v3.1.3", - description: - "Formbricks is an open-source survey and form platform for collecting user data.", - logo: "formbricks.png", - links: { - github: "https://github.com/formbricks/formbricks", - website: "https://formbricks.com/", - docs: "https://formbricks.com/docs", - }, - tags: ["forms", "analytics"], - load: () => import("./formbricks/index").then((m) => m.generate), - }, + links: { + github: "https://github.com/nocodb/nocodb", + website: "https://nocodb.com/", + docs: "https://docs.nocodb.com/", + }, + logo: "nocodb.png", + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./nocodb/index").then((m) => m.generate), + }, + { + id: "meilisearch", + name: "Meilisearch", + version: "v1.8.3", + description: + "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", + logo: "meilisearch.png", + links: { + github: "https://github.com/meilisearch/meilisearch", + website: "https://www.meilisearch.com/", + docs: "https://docs.meilisearch.com/", + }, + tags: ["search"], + load: () => import("./meilisearch/index").then((m) => m.generate), + }, + { + id: "phpmyadmin", + name: "Phpmyadmin", + version: "5.2.1", + description: + "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", + logo: "phpmyadmin.png", + links: { + github: "https://github.com/phpmyadmin/phpmyadmin", + website: "https://www.phpmyadmin.net/", + docs: "https://www.phpmyadmin.net/docs/", + }, + tags: ["database"], + load: () => import("./phpmyadmin/index").then((m) => m.generate), + }, + { + id: "rocketchat", + name: "Rocketchat", + version: "6.9.2", + description: + "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", + logo: "rocketchat.png", + links: { + github: "https://github.com/RocketChat/Rocket.Chat", + website: "https://rocket.chat/", + docs: "https://rocket.chat/docs/", + }, + tags: ["chat"], + load: () => import("./rocketchat/index").then((m) => m.generate), + }, + { + id: "minio", + name: "Minio", + description: + "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", + logo: "minio.png", + version: "latest", + links: { + github: "https://github.com/minio/minio", + website: "https://minio.io/", + docs: "https://docs.minio.io/", + }, + tags: ["storage"], + load: () => import("./minio/index").then((m) => m.generate), + }, + { + id: "metabase", + name: "Metabase", + version: "v0.50.8", + description: + "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", + logo: "metabase.png", + links: { + github: "https://github.com/metabase/metabase", + website: "https://www.metabase.com/", + docs: "https://www.metabase.com/docs/", + }, + tags: ["database", "dashboard"], + load: () => import("./metabase/index").then((m) => m.generate), + }, + { + id: "glitchtip", + name: "Glitchtip", + version: "v4.0", + description: "Glitchtip is simple, open source error tracking", + logo: "glitchtip.png", + links: { + github: "https://gitlab.com/glitchtip/", + website: "https://glitchtip.com/", + docs: "https://glitchtip.com/documentation", + }, + tags: ["hosting"], + load: () => import("./glitchtip/index").then((m) => m.generate), + }, + { + id: "open-webui", + name: "Open WebUI", + version: "v0.3.7", + description: + "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", + logo: "open-webui.png", + links: { + github: "https://github.com/open-webui/open-webui", + website: "https://openwebui.com/", + docs: "https://docs.openwebui.com/", + }, + tags: ["chat"], + load: () => import("./open-webui/index").then((m) => m.generate), + }, + { + id: "listmonk", + name: "Listmonk", + version: "v3.0.0", + description: + "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", + logo: "listmonk.png", + links: { + github: "https://github.com/knadh/listmonk", + website: "https://listmonk.app/", + docs: "https://listmonk.app/docs/", + }, + tags: ["email", "newsletter", "mailing-list"], + load: () => import("./listmonk/index").then((m) => m.generate), + }, + { + id: "doublezero", + name: "Double Zero", + version: "v0.2.1", + description: + "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", + logo: "doublezero.svg", + links: { + github: "https://github.com/technomancy-dev/00", + website: "https://www.double-zero.cloud/", + docs: "https://github.com/technomancy-dev/00", + }, + tags: ["email"], + load: () => import("./doublezero/index").then((m) => m.generate), + }, + { + id: "umami", + name: "Umami", + version: "v2.14.0", + description: + "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", + logo: "umami.png", + links: { + github: "https://github.com/umami-software/umami", + website: "https://umami.is", + docs: "https://umami.is/docs", + }, + tags: ["analytics"], + load: () => import("./umami/index").then((m) => m.generate), + }, + { + id: "jellyfin", + name: "jellyfin", + version: "v10.9.7", + description: + "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", + logo: "jellyfin.svg", + links: { + github: "https://github.com/jellyfin/jellyfin", + website: "https://jellyfin.org/", + docs: "https://jellyfin.org/docs/", + }, + tags: ["media system"], + load: () => import("./jellyfin/index").then((m) => m.generate), + }, + { + id: "teable", + name: "teable", + version: "v1.3.1-alpha-build.460", + description: + "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", + logo: "teable.png", + links: { + github: "https://github.com/teableio/teable", + website: "https://teable.io/", + docs: "https://help.teable.io/", + }, + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./teable/index").then((m) => m.generate), + }, + { + id: "zipline", + name: "Zipline", + version: "v3.7.9", + description: + "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", + logo: "zipline.png", + links: { + github: "https://github.com/diced/zipline", + website: "https://zipline.diced.sh/", + docs: "https://zipline.diced.sh/docs/", + }, + tags: ["media system", "storage"], + load: () => import("./zipline/index").then((m) => m.generate), + }, + { + id: "soketi", + name: "Soketi", + version: "v1.6.1-16", + description: + "Soketi is your simple, fast, and resilient open-source WebSockets server.", + logo: "soketi.png", + links: { + github: "https://github.com/soketi/soketi", + website: "https://soketi.app/", + docs: "https://docs.soketi.app/", + }, + tags: ["chat"], + load: () => import("./soketi/index").then((m) => m.generate), + }, + { + id: "aptabase", + name: "Aptabase", + version: "v1.0.0", + description: + "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "aptabase.svg", + links: { + github: "https://github.com/aptabase/aptabase", + website: "https://aptabase.com/", + docs: "https://github.com/aptabase/aptabase/blob/main/README.md", + }, + tags: ["analytics", "self-hosted"], + load: () => import("./aptabase/index").then((m) => m.generate), + }, + { + id: "typebot", + name: "Typebot", + version: "2.27.0", + description: "Typebot is an open-source chatbot builder platform.", + logo: "typebot.svg", + links: { + github: "https://github.com/baptisteArno/typebot.io", + website: "https://typebot.io/", + docs: "https://docs.typebot.io/get-started/introduction", + }, + tags: ["chatbot", "builder", "open-source"], + load: () => import("./typebot/index").then((m) => m.generate), + }, + { + id: "gitea", + name: "Gitea", + version: "1.22.3", + description: + "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", + logo: "gitea.png", + links: { + github: "https://github.com/go-gitea/gitea.git", + website: "https://gitea.com/", + docs: "https://docs.gitea.com/installation/install-with-docker", + }, + tags: ["self-hosted", "storage"], + load: () => import("./gitea/index").then((m) => m.generate), + }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", + }, + tags: ["self-hosted", "email", "webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + }, + { + id: "filebrowser", + name: "File Browser", + version: "2.31.2", + description: + "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", + logo: "filebrowser.svg", + links: { + github: "https://github.com/filebrowser/filebrowser", + website: "https://filebrowser.org/", + docs: "https://filebrowser.org/", + }, + tags: ["file-manager", "storage"], + load: () => import("./filebrowser/index").then((m) => m.generate), + }, + { + id: "tolgee", + name: "Tolgee", + version: "v3.80.4", + description: + "Developer & translator friendly web-based localization platform", + logo: "tolgee.svg", + links: { + github: "https://github.com/tolgee/tolgee-platform", + website: "https://tolgee.io", + docs: "https://tolgee.io/platform", + }, + tags: ["self-hosted", "i18n", "localization", "translations"], + load: () => import("./tolgee/index").then((m) => m.generate), + }, + { + id: "portainer", + name: "Portainer", + version: "2.21.4", + description: + "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", + logo: "portainer.svg", + links: { + github: "https://github.com/portainer/portainer", + website: "https://www.portainer.io/", + docs: "https://docs.portainer.io/", + }, + tags: ["cloud", "monitoring"], + load: () => import("./portainer/index").then((m) => m.generate), + }, + { + id: "influxdb", + name: "InfluxDB", + version: "2.7.10", + description: + "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", + logo: "influxdb.png", + links: { + github: "https://github.com/influxdata/influxdb", + website: "https://www.influxdata.com/", + docs: "https://docs.influxdata.com/influxdb/v2/", + }, + tags: ["self-hosted", "open-source", "storage", "database"], + load: () => import("./influxdb/index").then((m) => m.generate), + }, + { + id: "infisical", + name: "Infisical", + version: "0.90.1", + description: + "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", + logo: "infisical.jpg", + links: { + github: "https://github.com/Infisical/infisical", + website: "https://infisical.com/", + docs: "https://infisical.com/docs/documentation/getting-started/introduction", + }, + tags: ["self-hosted", "open-source"], + load: () => import("./infisical/index").then((m) => m.generate), + }, + { + id: "docmost", + name: "Docmost", + version: "0.4.1", + description: + "Docmost, is an open-source collaborative wiki and documentation software.", + logo: "docmost.png", + links: { + github: "https://github.com/docmost/docmost", + website: "https://docmost.com/", + docs: "https://docmost.com/docs/", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./docmost/index").then((m) => m.generate), + }, + { + id: "vaultwarden", + name: "Vaultwarden", + version: "1.32.7", + description: + "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", + logo: "vaultwarden.svg", + links: { + github: "https://github.com/dani-garcia/vaultwarden", + website: "", + docs: "https://github.com/dani-garcia/vaultwarden/wiki", + }, + tags: ["open-source"], + load: () => import("./vaultwarden/index").then((m) => m.generate), + }, + { + id: "hi-events", + name: "Hi.events", + version: "0.8.0-beta.1", + description: + "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", + logo: "hi-events.svg", + links: { + github: "https://github.com/HiEventsDev/hi.events", + website: "https://hi.events/", + docs: "https://hi.events/docs", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./hi-events/index").then((m) => m.generate), + }, + { + id: "windows", + name: "Windows (dockerized)", + version: "4.00", + description: "Windows inside a Docker container.", + logo: "windows.png", + links: { + github: "https://github.com/dockur/windows", + website: "", + docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./windows/index").then((m) => m.generate), + }, + { + id: "macos", + name: "MacOS (dockerized)", + version: "1.14", + description: "MacOS inside a Docker container.", + logo: "macos.png", + links: { + github: "https://github.com/dockur/macos", + website: "", + docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./macos/index").then((m) => m.generate), + }, + { + id: "coder", + name: "Coder", + version: "2.15.3", + description: + "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", + logo: "coder.svg", + links: { + github: "https://github.com/coder/coder", + website: "https://coder.com/", + docs: "https://coder.com/docs", + }, + tags: ["self-hosted", "open-source", "builder"], + load: () => import("./coder/index").then((m) => m.generate), + }, + { + id: "stirling", + name: "Stirling PDF", + version: "0.30.1", + description: "A locally hosted one-stop shop for all your PDF needs", + logo: "stirling.svg", + links: { + github: "https://github.com/Stirling-Tools/Stirling-PDF", + website: "https://www.stirlingpdf.com/", + docs: "https://docs.stirlingpdf.com/", + }, + tags: ["pdf", "tools"], + load: () => import("./stirling/index").then((m) => m.generate), + }, + { + id: "lobe-chat", + name: "Lobe Chat", + version: "v1.26.1", + description: "Lobe Chat - an open-source, modern-design AI chat framework.", + logo: "lobe-chat.png", + links: { + github: "https://github.com/lobehub/lobe-chat", + website: "https://chat-preview.lobehub.com/", + docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", + }, + tags: ["IA", "chat"], + load: () => import("./lobe-chat/index").then((m) => m.generate), + }, + { + id: "peppermint", + name: "Peppermint", + version: "latest", + description: + "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", + logo: "peppermint.svg", + links: { + github: "https://github.com/Peppermint-Lab/peppermint", + website: "https://peppermint.sh/", + docs: "https://docs.peppermint.sh/", + }, + tags: ["api", "development", "documentation"], + load: () => import("./peppermint/index").then((m) => m.generate), + }, + { + id: "windmill", + name: "Windmill", + version: "latest", + description: + "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", + logo: "windmill.svg", + links: { + github: "https://github.com/windmill-labs/windmill", + website: "https://www.windmill.dev/", + docs: "https://docs.windmill.dev/", + }, + tags: ["workflow", "automation", "development"], + load: () => import("./windmill/index").then((m) => m.generate), + }, + { + id: "activepieces", + name: "Activepieces", + version: "0.35.0", + description: + "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", + logo: "activepieces.svg", + links: { + github: "https://github.com/activepieces/activepieces", + website: "https://www.activepieces.com/", + docs: "https://www.activepieces.com/docs", + }, + tags: ["automation", "workflow", "no-code"], + load: () => import("./activepieces/index").then((m) => m.generate), + }, + { + id: "invoiceshelf", + name: "InvoiceShelf", + version: "latest", + description: + "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", + logo: "invoiceshelf.png", + links: { + github: "https://github.com/InvoiceShelf/invoiceshelf", + website: "https://invoiceshelf.com", + docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./invoiceshelf/index").then((m) => m.generate), + }, + { + id: "postiz", + name: "Postiz", + version: "latest", + description: + "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", + logo: "postiz.png", + links: { + github: "https://github.com/gitroomhq/postiz", + website: "https://postiz.com", + docs: "https://docs.postiz.com", + }, + tags: ["cms", "content-management", "publishing"], + load: () => import("./postiz/index").then((m) => m.generate), + }, + { + id: "slash", + name: "Slash", + version: "latest", + description: + "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", + logo: "slash.png", + links: { + github: "https://github.com/yourselfhosted/slash", + website: "https://github.com/yourselfhosted/slash#readme", + docs: "https://github.com/yourselfhosted/slash/wiki", + }, + tags: ["bookmarks", "link-shortener", "self-hosted"], + load: () => import("./slash/index").then((m) => m.generate), + }, + { + id: "discord-tickets", + name: "Discord Tickets", + version: "4.0.21", + description: + "An open-source Discord bot for creating and managing support ticket channels.", + logo: "discord-tickets.png", + links: { + github: "https://github.com/discord-tickets/bot", + website: "https://discordtickets.app", + docs: "https://discordtickets.app/self-hosting/installation/docker/", + }, + tags: ["discord", "tickets", "support"], + load: () => import("./discord-tickets/index").then((m) => m.generate), + }, + { + id: "nextcloud-aio", + name: "Nextcloud All in One", + version: "30.0.2", + description: + "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", + logo: "nextcloud-aio.svg", + links: { + github: "https://github.com/nextcloud/docker", + website: "https://nextcloud.com/", + docs: "https://docs.nextcloud.com/", + }, + tags: ["file-manager", "sync"], + load: () => import("./nextcloud-aio/index").then((m) => m.generate), + }, + { + id: "blender", + name: "Blender", + version: "latest", + description: + "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + logo: "blender.svg", + links: { + github: "https://github.com/linuxserver/docker-blender", + website: "https://www.blender.org/", + docs: "https://docs.blender.org/", + }, + tags: ["3d", "rendering", "animation"], + load: () => import("./blender/index").then((m) => m.generate), + }, + { + id: "heyform", + name: "HeyForm", + version: "latest", + description: + "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", + logo: "heyform.svg", + links: { + github: "https://github.com/heyform/heyform", + website: "https://heyform.net", + docs: "https://docs.heyform.net", + }, + tags: ["form", "builder", "questionnaire", "quiz", "survey"], + load: () => import("./heyform/index").then((m) => m.generate), + }, + { + id: "chatwoot", + name: "Chatwoot", + version: "v3.14.1", + description: + "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", + logo: "chatwoot.svg", + links: { + github: "https://github.com/chatwoot/chatwoot", + website: "https://www.chatwoot.com", + docs: "https://www.chatwoot.com/docs", + }, + tags: ["support", "chat", "customer-service"], + load: () => import("./chatwoot/index").then((m) => m.generate), + }, + { + id: "discourse", + name: "Discourse", + version: "3.3.2", + description: + "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", + logo: "discourse.svg", + links: { + github: "https://github.com/discourse/discourse", + website: "https://www.discourse.org/", + docs: "https://meta.discourse.org/", + }, + tags: ["forum", "community", "discussion"], + load: () => import("./discourse/index").then((m) => m.generate), + }, + { + id: "immich", + name: "Immich", + version: "v1.121.0", + description: + "High performance self-hosted photo and video backup solution directly from your mobile phone.", + logo: "immich.svg", + links: { + github: "https://github.com/immich-app/immich", + website: "https://immich.app/", + docs: "https://immich.app/docs/overview/introduction", + }, + tags: ["photos", "videos", "backup", "media"], + load: () => import("./immich/index").then((m) => m.generate), + }, + { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { + id: "yourls", + name: "YOURLS", + version: "1.9.2", + description: + "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", + logo: "yourls.svg", + links: { + github: "https://github.com/YOURLS/YOURLS", + website: "https://yourls.org/", + docs: "https://yourls.org/#documentation", + }, + tags: ["url-shortener", "php"], + load: () => import("./yourls/index").then((m) => m.generate), + }, + { + id: "ryot", + name: "Ryot", + version: "v7.10", + description: + "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", + logo: "ryot.png", + links: { + github: "https://github.com/IgnisDa/ryot", + website: "https://ryot.io/", + docs: "https://docs.ryot.io/", + }, + tags: ["media", "tracking", "self-hosted"], + load: () => import("./ryot/index").then((m) => m.generate), + }, + { + id: "photoprism", + name: "Photoprism", + version: "latest", + description: + "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + logo: "photoprism.svg", + links: { + github: "https://github.com/photoprism/photoprism", + website: "https://www.photoprism.app/", + docs: "https://docs.photoprism.app/", + }, + tags: ["media", "photos", "self-hosted"], + load: () => import("./photoprism/index").then((m) => m.generate), + }, + { + id: "ontime", + name: "Ontime", + version: "v3.8.0", + description: + "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", + logo: "ontime.png", + links: { + github: "https://github.com/cpvalente/ontime/", + website: "https://getontime.no", + docs: "https://docs.getontime.no", + }, + tags: ["event"], + load: () => import("./ontime/index").then((m) => m.generate), + }, + { + id: "triggerdotdev", + name: "Trigger.dev", + version: "v3", + description: + "Trigger is a platform for building event-driven applications.", + logo: "triggerdotdev.svg", + links: { + github: "https://github.com/triggerdotdev/trigger.dev", + website: "https://trigger.dev/", + docs: "https://trigger.dev/docs", + }, + tags: ["event-driven", "applications"], + load: () => import("./triggerdotdev/index").then((m) => m.generate), + }, + { + id: "browserless", + name: "Browserless", + version: "2.23.0", + description: + "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", + logo: "browserless.svg", + links: { + github: "https://github.com/browserless/browserless", + website: "https://www.browserless.io/", + docs: "https://docs.browserless.io/", + }, + tags: ["browser", "automation"], + load: () => import("./browserless/index").then((m) => m.generate), + }, + { + id: "drawio", + name: "draw.io", + version: "24.7.17", + description: + "draw.io is a configurable diagramming/whiteboarding visualization application.", + logo: "drawio.svg", + links: { + github: "https://github.com/jgraph/drawio", + website: "https://draw.io/", + docs: "https://www.drawio.com/doc/", + }, + tags: ["drawing", "diagrams"], + load: () => import("./drawio/index").then((m) => m.generate), + }, + { + id: "kimai", + name: "Kimai", + version: "2.26.0", + description: + "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", + logo: "kimai.svg", + links: { + github: "https://github.com/kimai/kimai", + website: "https://www.kimai.org", + docs: "https://www.kimai.org/documentation", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./kimai/index").then((m) => m.generate), + }, + { + id: "logto", + name: "Logto", + version: "1.22.0", + description: + "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", + logo: "logto.png", + links: { + github: "https://github.com/logto-io/logto", + website: "https://logto.io/", + docs: "https://docs.logto.io/introduction", + }, + tags: ["identity", "auth"], + load: () => import("./logto/index").then((m) => m.generate), + }, + { + id: "penpot", + name: "Penpot", + version: "2.3.2", + description: + "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", + logo: "penpot.svg", + links: { + github: "https://github.com/penpot/penpot", + website: "https://penpot.app/", + docs: "https://docs.penpot.app/", + }, + tags: ["design", "collaboration"], + load: () => import("./penpot/index").then((m) => m.generate), + }, + { + id: "huly", + name: "Huly", + version: "0.6.377", + description: + "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", + logo: "huly.svg", + links: { + github: "https://github.com/hcengineering/huly-selfhost", + website: "https://huly.io/", + docs: "https://docs.huly.io/", + }, + tags: ["project-management", "community", "discussion"], + load: () => import("./huly/index").then((m) => m.generate), + }, + { + id: "unsend", + name: "Unsend", + version: "v1.3.2", + description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", + logo: "unsend.png", + links: { + github: "https://github.com/unsend-dev/unsend", + website: "https://unsend.dev/", + docs: "https://docs.unsend.dev/get-started/", + }, + tags: ["e-mail", "marketing", "business"], + load: () => import("./unsend/index").then((m) => m.generate), + }, + { + id: "langflow", + name: "Langflow", + version: "1.1.1", + description: + "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", + logo: "langflow.svg", + links: { + github: "https://github.com/langflow-ai/langflow/tree/main", + website: "https://www.langflow.org/", + docs: "https://docs.langflow.org/", + }, + tags: ["ai"], + load: () => import("./langflow/index").then((m) => m.generate), + }, + { + id: "elastic-search", + name: "Elasticsearch", + version: "8.10.2", + description: + "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", + logo: "elasticsearch.svg", + links: { + github: "https://github.com/elastic/elasticsearch", + website: "https://www.elastic.co/elasticsearch/", + docs: "https://docs.elastic.co/elasticsearch/", + }, + tags: ["search", "analytics"], + load: () => import("./elastic-search/index").then((m) => m.generate), + }, + { + id: "onedev", + name: "OneDev", + version: "11.6.6", + description: + "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", + logo: "onedev.png", + links: { + github: "https://github.com/theonedev/onedev/", + website: "https://onedev.io/", + docs: "https://docs.onedev.io/", + }, + tags: ["self-hosted", "development"], + load: () => import("./onedev/index").then((m) => m.generate), + }, + { + id: "unifi", + name: "Unifi Network", + version: "11.6.6", + description: + "Unifi Network is an open-source enterprise network management platform for wireless networks.", + logo: "unifi.webp", + links: { + github: "https://github.com/ubiquiti", + website: "https://www.ui.com/", + docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", + }, + tags: ["self-hosted", "networking"], + load: () => import("./unifi/index").then((m) => m.generate), + }, + { + id: "glpi", + name: "GLPI Project", + version: "10.0.16", + description: "The most complete open source service management software", + logo: "glpi.webp", + links: { + github: "https://github.com/glpi-project/glpi", + website: "https://glpi-project.org/", + docs: "https://glpi-project.org/documentation/", + }, + tags: ["self-hosted", "project-management", "management"], + load: () => import("./glpi/index").then((m) => m.generate), + }, + { + id: "checkmate", + name: "Checkmate", + version: "2.0.1", + description: + "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", + logo: "checkmate.png", + links: { + github: "https://github.com/bluewave-labs/checkmate", + website: "https://bluewavelabs.ca", + docs: "https://bluewavelabs.gitbook.io/checkmate", + }, + tags: ["self-hosted", "monitoring", "uptime"], + load: () => import("./checkmate/index").then((m) => m.generate), + }, + { + id: "gotenberg", + name: "Gotenberg", + version: "latest", + description: "Gotenberg is a Docker-powered stateless API for PDF files.", + logo: "gotenberg.png", + links: { + github: "https://github.com/gotenberg/gotenberg", + website: "https://gotenberg.dev", + docs: "https://gotenberg.dev/docs/getting-started/introduction", + }, + tags: ["api", "backend", "pdf", "tools"], + load: () => import("./gotenberg/index").then((m) => m.generate), + }, + { + id: "actualbudget", + name: "Actual Budget", + version: "latest", + description: + "A super fast and privacy-focused app for managing your finances.", + logo: "actualbudget.png", + links: { + github: "https://github.com/actualbudget/actual", + website: "https://actualbudget.org", + docs: "https://actualbudget.org/docs", + }, + tags: ["budgeting", "finance", "money"], + load: () => import("./actualbudget/index").then((m) => m.generate), + }, + { + id: "conduit", + name: "Conduit", + version: "v0.9.0", + description: + "Conduit is a simple, fast and reliable chat server powered by Matrix", + logo: "conduit.svg", + links: { + github: "https://gitlab.com/famedly/conduit", + website: "https://conduit.rs/", + docs: "https://docs.conduit.rs/", + }, + tags: ["matrix", "communication"], + load: () => import("./conduit/index").then((m) => m.generate), + }, + { + id: "evolutionapi", + name: "Evolution API", + version: "v2.1.2", + description: + "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", + logo: "evolutionapi.png", + links: { + github: "https://github.com/EvolutionAPI/evolution-api", + docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", + website: "https://evolution-api.com/opensource-whatsapp-api/", + }, + tags: ["api", "whatsapp", "messaging"], + load: () => import("./evolutionapi/index").then((m) => m.generate), + }, + { + id: "conduwuit", + name: "Conduwuit", + version: "latest", + description: + "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", + logo: "conduwuit.svg", + links: { + github: "https://github.com/girlbossceo/conduwuit", + website: "https://conduwuit.puppyirl.gay", + docs: "https://conduwuit.puppyirl.gay/configuration.html", + }, + tags: ["backend", "chat", "communication", "matrix", "server"], + load: () => import("./conduwuit/index").then((m) => m.generate), + }, + { + id: "cloudflared", + name: "Cloudflared", + version: "latest", + description: + "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", + logo: "cloudflared.svg", + links: { + github: "https://github.com/cloudflare/cloudflared", + website: + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", + docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", + }, + tags: ["cloud", "networking", "security", "tunnel"], + load: () => import("./cloudflared/index").then((m) => m.generate), + }, + { + id: "couchdb", + name: "CouchDB", + version: "latest", + description: + "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", + logo: "couchdb.png", + links: { + github: "https://github.com/apache/couchdb", + website: "https://couchdb.apache.org/", + docs: "https://docs.couchdb.org/en/stable/", + }, + tags: ["database", "storage"], + load: () => import("./couchdb/index").then((m) => m.generate), + }, + { + id: "it-tools", + name: "IT Tools", + version: "latest", + description: "A collection of handy online it-tools for developers.", + logo: "it-tools.svg", + links: { + github: "https://github.com/CorentinTh/it-tools", + website: "https://it-tools.tech", + }, + tags: ["developer", "tools"], + load: () => import("./it-tools/index").then((m) => m.generate), + }, + { + id: "superset", + name: "Superset (Unofficial)", + version: "latest", + description: "Data visualization and data exploration platform.", + logo: "superset.svg", + links: { + github: "https://github.com/amancevice/docker-superset", + website: "https://superset.apache.org", + docs: "https://superset.apache.org/docs/intro", + }, + tags: ["analytics", "bi", "dashboard", "database", "sql"], + load: () => import("./superset/index").then((m) => m.generate), + }, + { + id: "glance", + name: "Glance", + version: "latest", + description: + "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", + logo: "glance.png", + links: { + github: "https://github.com/glanceapp/glance", + docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", + }, + tags: ["dashboard", "monitoring", "widgets", "rss"], + load: () => import("./glance/index").then((m) => m.generate), + }, + { + id: "homarr", + name: "Homarr", + version: "latest", + description: + "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", + logo: "homarr.png", + links: { + github: "https://github.com/homarr-labs/homarr", + docs: "https://homarr.dev/docs/getting-started/installation/docker", + website: "https://homarr.dev/", + }, + tags: ["dashboard", "monitoring"], + load: () => import("./homarr/index").then((m) => m.generate), + }, + { + id: "erpnext", + name: "ERPNext", + version: "version-15", + description: "100% Open Source and highly customizable ERP software.", + logo: "erpnext.svg", + links: { + github: "https://github.com/frappe/erpnext", + docs: "https://docs.frappe.io/erpnext", + website: "https://erpnext.com", + }, + tags: [ + "erp", + "accounts", + "manufacturing", + "retail", + "sales", + "pos", + "hrms", + ], + load: () => import("./erpnext/index").then((m) => m.generate), + }, + { + id: "maybe", + name: "Maybe", + version: "latest", + description: + "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", + logo: "maybe.svg", + links: { + github: "https://github.com/maybe-finance/maybe", + website: "https://maybe.finance/", + docs: "https://docs.maybe.finance/", + }, + tags: ["finance", "self-hosted"], + load: () => import("./maybe/index").then((m) => m.generate), + }, + { + id: "spacedrive", + name: "Spacedrive", + version: "latest", + description: + "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", + links: { + github: "https://github.com/spacedriveapp/spacedrive", + website: "https://spacedrive.com/", + docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", + }, + logo: "spacedrive.png", + tags: ["file-manager", "vdfs", "storage"], + load: () => import("./spacedrive/index").then((m) => m.generate), + }, + { + id: "alist", + name: "AList", + version: "v3.41.0", + description: + "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", + logo: "alist.svg", + links: { + github: "https://github.com/AlistGo/alist", + website: "https://alist.nn.ci", + docs: "https://alist.nn.ci/guide/install/docker.html", + }, + tags: ["file", "webdav", "storage"], + load: () => import("./alist/index").then((m) => m.generate), + }, + { + id: "answer", + name: "Answer", + version: "v1.4.1", + description: + "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", + logo: "answer.png", + links: { + github: "https://github.com/apache/answer", + website: "https://answer.apache.org/", + docs: "https://answer.apache.org/docs", + }, + tags: ["q&a", "self-hosted"], + load: () => import("./answer/index").then((m) => m.generate), + }, + { + id: "shlink", + name: "Shlink", + version: "stable", + description: + "URL shortener that can be used to serve shortened URLs under your own domain.", + logo: "shlink.svg", + links: { + github: "https://github.com/shlinkio/shlink", + website: "https://shlink.io", + docs: "https://shlink.io/documentation", + }, + tags: ["sharing", "shortener", "url"], + load: () => import("./shlink/index").then((m) => m.generate), + }, + { + id: "frappe-hr", + name: "Frappe HR", + version: "version-15", + description: + "Feature rich HR & Payroll software. 100% FOSS and customizable.", + logo: "frappe-hr.svg", + links: { + github: "https://github.com/frappe/hrms", + docs: "https://docs.frappe.io/hr", + website: "https://frappe.io/hr", + }, + tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], + load: () => import("./frappe-hr/index").then((m) => m.generate), + }, + { + id: "formbricks", + name: "Formbricks", + version: "v3.1.3", + description: + "Formbricks is an open-source survey and form platform for collecting user data.", + logo: "formbricks.png", + links: { + github: "https://github.com/formbricks/formbricks", + website: "https://formbricks.com/", + docs: "https://formbricks.com/docs", + }, + tags: ["forms", "analytics"], + load: () => import("./formbricks/index").then((m) => m.generate), + }, + { + id: "convex", + name: "Convex", + version: "latest", + description: + "Convex is an open-source reactive database designed to make life easy for web app developers.", + logo: "convex.svg", + links: { + github: "https://github.com/get-convex/convex", + website: "https://www.convex.dev/", + docs: "https://www.convex.dev/docs", + }, + tags: ["backend", "database", "api"], + load: () => import("./convex/index").then((m) => m.generate), + }, ]; From ca217affe66d860381100417b286615189e90d38 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 14 Feb 2025 02:18:53 -0600 Subject: [PATCH 026/126] feat: update references --- .../dashboard/projects/handle-project.tsx | 24 +---- apps/dokploy/pages/index.tsx | 18 ++-- apps/dokploy/server/api/routers/admin.ts | 100 +++++++++--------- .../dokploy/server/api/routers/application.ts | 44 ++++---- apps/dokploy/server/api/routers/auth.ts | 1 - apps/dokploy/server/api/routers/bitbucket.ts | 16 +-- .../dokploy/server/api/routers/certificate.ts | 8 +- apps/dokploy/server/api/routers/compose.ts | 32 +++--- apps/dokploy/server/api/routers/deployment.ts | 6 +- .../dokploy/server/api/routers/destination.ts | 14 +-- apps/dokploy/server/api/routers/domain.ts | 24 ++--- .../server/api/routers/git-provider.ts | 4 +- apps/dokploy/server/api/routers/github.ts | 14 +-- apps/dokploy/server/api/routers/gitlab.ts | 16 +-- apps/dokploy/server/api/routers/mariadb.ts | 22 ++-- apps/dokploy/server/api/routers/mongo.ts | 24 ++--- apps/dokploy/server/api/routers/mysql.ts | 24 ++--- .../server/api/routers/notification.ts | 54 +++++----- apps/dokploy/server/api/routers/postgres.ts | 24 ++--- .../server/api/routers/preview-deployment.ts | 6 +- apps/dokploy/server/api/routers/project.ts | 8 +- apps/dokploy/server/api/routers/redirects.ts | 8 +- apps/dokploy/server/api/routers/redis.ts | 22 ++-- apps/dokploy/server/api/routers/registry.ts | 12 +-- apps/dokploy/server/api/routers/security.ts | 8 +- apps/dokploy/server/api/routers/server.ts | 41 ++++--- apps/dokploy/server/api/routers/settings.ts | 1 - apps/dokploy/server/api/routers/ssh-key.ts | 11 +- apps/dokploy/server/api/routers/stripe.ts | 31 +++--- apps/dokploy/server/api/trpc.ts | 4 +- packages/server/src/db/schema/account.ts | 33 ++++++ packages/server/src/db/schema/bitbucket.ts | 2 +- packages/server/src/db/schema/notification.ts | 11 +- packages/server/src/db/schema/user.ts | 10 +- packages/server/src/lib/auth.ts | 18 +++- packages/server/src/services/admin.ts | 44 ++++---- packages/server/src/services/bitbucket.ts | 8 +- packages/server/src/services/github.ts | 4 +- packages/server/src/services/notification.ts | 30 +++--- packages/server/src/services/registry.ts | 8 +- packages/server/src/services/server.ts | 8 +- 41 files changed, 416 insertions(+), 381 deletions(-) diff --git a/apps/dokploy/components/dashboard/projects/handle-project.tsx b/apps/dokploy/components/dashboard/projects/handle-project.tsx index 5b0777716..fb2cbf67a 100644 --- a/apps/dokploy/components/dashboard/projects/handle-project.tsx +++ b/apps/dokploy/components/dashboard/projects/handle-project.tsx @@ -123,26 +123,10 @@ export const HandleProject = ({ projectId }: Props) => { Update ) : ( - <> - - - + )} diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 4c53f4797..0fecc209c 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -60,17 +60,18 @@ interface Props { IS_CLOUD: boolean; } export default function Home({ IS_CLOUD }: Props) { + const [isLoading, setIsLoading] = useState(false); + const [isError, setIsError] = useState(false); + const [error, setError] = useState(null); const [temp, setTemp] = useState({ is2FAEnabled: false, authId: "", }); - const { mutateAsync, isLoading, error, isError } = - api.auth.login.useMutation(); const router = useRouter(); const form = useForm({ defaultValues: { - email: "", - password: "", + email: "siumauricio@hotmail.com", + password: "Password123", }, resolver: zodResolver(loginSchema), }); @@ -80,6 +81,7 @@ export default function Home({ IS_CLOUD }: Props) { }, [form, form.reset, form.formState.isSubmitSuccessful]); const onSubmit = async (values: Login) => { + setIsLoading(true); const { data, error } = await authClient.signIn.email({ email: values.email, password: values.password, @@ -92,15 +94,17 @@ export default function Home({ IS_CLOUD }: Props) { toast.success("Successfully signed in", { duration: 2000, }); - // router.push("/dashboard/projects"); + router.push("/dashboard/projects"); // } } else { + setIsError(true); + setError(error.message ?? "Error to signup"); toast.error("Error to sign up", { description: error.message, }); } - console.log(data, error); + setIsLoading(false); // await mutateAsync({ // email: values.email.toLowerCase(), // password: values.password, @@ -136,7 +140,7 @@ export default function Home({ IS_CLOUD }: Props) {
{isError && ( - {error?.message} + {error} )} diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 6a4764f56..0b232f6da 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -6,18 +6,15 @@ import { apiRemoveUser, apiUpdateAdmin, apiUpdateWebServerMonitoring, - user, } from "@/server/db/schema"; import { IS_CLOUD, createInvitation, - findAdminById, findUserByAuthId, findUserById, getUserByToken, - removeUserByAuthId, + removeUserById, setupWebMonitoring, - updateAdmin, updateAdminById, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; @@ -32,7 +29,7 @@ import { export const adminRouter = createTRPCRouter({ one: adminProcedure.query(async ({ ctx }) => { - const { sshPrivateKey, ...rest } = await findAdminById(ctx.user.adminId); + const { sshPrivateKey, ...rest } = await findUserById(ctx.user.id); return { haveSSH: !!sshPrivateKey, ...rest, @@ -47,15 +44,15 @@ export const adminRouter = createTRPCRouter({ message: "You are not allowed to update this admin", }); } - const { authId } = await findAdminById(ctx.user.adminId); + const { id } = await findUserById(ctx.user.id); // @ts-ignore - return updateAdmin(authId, input); + return updateAdmin(id, input); }), createUserInvitation: adminProcedure .input(apiCreateUserInvitation) .mutation(async ({ input, ctx }) => { try { - await createInvitation(input, ctx.user.adminId); + await createInvitation(input, ctx.user.id); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -69,15 +66,16 @@ export const adminRouter = createTRPCRouter({ .input(apiRemoveUser) .mutation(async ({ input, ctx }) => { try { - const user = await findUserByAuthId(input.authId); + const user = await findUserById(input.id); - if (user.adminId !== ctx.user.adminId) { + if (user.id !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this user", }); } - return await removeUserByAuthId(input.authId); + + return await removeUserById(input.id); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -95,20 +93,20 @@ export const adminRouter = createTRPCRouter({ .input(apiAssignPermissions) .mutation(async ({ input, ctx }) => { try { - const user = await findUserById(input.userId); + const user = await findUserById(input.id); - if (user.adminId !== ctx.user.adminId) { + if (user.id !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to assign permissions", }); } - await db - .update(users) - .set({ - ...input, - }) - .where(eq(users.userId, input.userId)); + // await db + // .update(users) + // .set({ + // ...input, + // }) + // .where(eq(users.userId, input.userId)); } catch (error) { throw error; } @@ -124,50 +122,50 @@ export const adminRouter = createTRPCRouter({ message: "Feature disabled on cloud", }); } - const admin = await findAdminById(ctx.user.adminId); - if (admin.adminId !== ctx.user.adminId) { + const user = await findUserById(ctx.user.ownerId); + if (user.id !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", }); } - await updateAdminById(admin.adminId, { - metricsConfig: { - server: { - type: "Dokploy", - refreshRate: input.metricsConfig.server.refreshRate, - port: input.metricsConfig.server.port, - token: input.metricsConfig.server.token, - cronJob: input.metricsConfig.server.cronJob, - urlCallback: input.metricsConfig.server.urlCallback, - retentionDays: input.metricsConfig.server.retentionDays, - thresholds: { - cpu: input.metricsConfig.server.thresholds.cpu, - memory: input.metricsConfig.server.thresholds.memory, - }, - }, - containers: { - refreshRate: input.metricsConfig.containers.refreshRate, - services: { - include: input.metricsConfig.containers.services.include || [], - exclude: input.metricsConfig.containers.services.exclude || [], - }, - }, - }, - }); - const currentServer = await setupWebMonitoring(admin.adminId); - return currentServer; + // await updateAdminById(admin.adminId, { + // metricsConfig: { + // server: { + // type: "Dokploy", + // refreshRate: input.metricsConfig.server.refreshRate, + // port: input.metricsConfig.server.port, + // token: input.metricsConfig.server.token, + // cronJob: input.metricsConfig.server.cronJob, + // urlCallback: input.metricsConfig.server.urlCallback, + // retentionDays: input.metricsConfig.server.retentionDays, + // thresholds: { + // cpu: input.metricsConfig.server.thresholds.cpu, + // memory: input.metricsConfig.server.thresholds.memory, + // }, + // }, + // containers: { + // refreshRate: input.metricsConfig.containers.refreshRate, + // services: { + // include: input.metricsConfig.containers.services.include || [], + // exclude: input.metricsConfig.containers.services.exclude || [], + // }, + // }, + // }, + // }); + // const currentServer = await setupWebMonitoring(admin.adminId); + // return currentServer; } catch (error) { throw error; } }), getMetricsToken: protectedProcedure.query(async ({ ctx }) => { - const admin = await findAdminById(ctx.user.adminId); + const user = await findUserById(ctx.user.ownerId); return { - serverIp: admin.serverIp, - enabledFeatures: admin.enablePaidFeatures, - metricsConfig: admin?.metricsConfig, + serverIp: user.serverIp, + enabledFeatures: user.enablePaidFeatures, + metricsConfig: user?.metricsConfig, }; }), diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 13b7c80df..d07caa435 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -72,7 +72,7 @@ export const applicationRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -106,7 +106,7 @@ export const applicationRouter = createTRPCRouter({ ); } const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -119,7 +119,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiReloadApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this application", @@ -153,7 +153,7 @@ export const applicationRouter = createTRPCRouter({ } const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this application", @@ -194,7 +194,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const service = await findApplicationById(input.applicationId); - if (service.project.adminId !== ctx.user.adminId) { + if (service.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this application", @@ -214,7 +214,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const service = await findApplicationById(input.applicationId); - if (service.project.adminId !== ctx.user.adminId) { + if (service.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this application", @@ -235,7 +235,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to redeploy this application", @@ -268,7 +268,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariables) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -284,7 +284,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveBuildType) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this build type", @@ -305,7 +305,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveGithubProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this github provider", @@ -327,7 +327,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveGitlabProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this gitlab provider", @@ -351,7 +351,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveBitbucketProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this bitbucket provider", @@ -373,7 +373,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveDockerProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this docker provider", @@ -394,7 +394,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveGitProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this git provider", @@ -415,7 +415,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to mark this application as running", @@ -427,7 +427,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiUpdateApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this application", @@ -451,7 +451,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to refresh this application", @@ -466,7 +466,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this application", @@ -500,7 +500,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to clean this application", @@ -513,7 +513,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to read this application", @@ -548,7 +548,7 @@ export const applicationRouter = createTRPCRouter({ const app = await findApplicationById(input.applicationId as string); - if (app.project.adminId !== ctx.user.adminId) { + if (app.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this application", @@ -590,7 +590,7 @@ export const applicationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this application", diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index c1c2e0a75..cc88f2c34 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -21,7 +21,6 @@ import { lucia, luciaToken, removeAdminByAuthId, - removeUserByAuthId, sendDiscordNotification, sendEmailNotification, updateAuthById, diff --git a/apps/dokploy/server/api/routers/bitbucket.ts b/apps/dokploy/server/api/routers/bitbucket.ts index c66716d30..75513a805 100644 --- a/apps/dokploy/server/api/routers/bitbucket.ts +++ b/apps/dokploy/server/api/routers/bitbucket.ts @@ -23,7 +23,7 @@ export const bitbucketRouter = createTRPCRouter({ .input(apiCreateBitbucket) .mutation(async ({ input, ctx }) => { try { - return await createBitbucket(input, ctx.user.adminId); + return await createBitbucket(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -38,7 +38,7 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.adminId !== ctx.user.adminId + bitbucketProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -61,7 +61,7 @@ export const bitbucketRouter = createTRPCRouter({ if (IS_CLOUD) { // TODO: mAyBe a rEfaCtoR 🤫 result = result.filter( - (provider) => provider.gitProvider.adminId === ctx.user.adminId, + (provider) => provider.gitProvider.userId === ctx.user.ownerId, ); } return result; @@ -73,7 +73,7 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.adminId !== ctx.user.adminId + bitbucketProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -91,7 +91,7 @@ export const bitbucketRouter = createTRPCRouter({ ); if ( IS_CLOUD && - bitbucketProvider.gitProvider.adminId !== ctx.user.adminId + bitbucketProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -108,7 +108,7 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.adminId !== ctx.user.adminId + bitbucketProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -132,7 +132,7 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.adminId !== ctx.user.adminId + bitbucketProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -142,7 +142,7 @@ export const bitbucketRouter = createTRPCRouter({ } return await updateBitbucket(input.bitbucketId, { ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); }), }); diff --git a/apps/dokploy/server/api/routers/certificate.ts b/apps/dokploy/server/api/routers/certificate.ts index 0f8d6fd94..b1c6f5c2d 100644 --- a/apps/dokploy/server/api/routers/certificate.ts +++ b/apps/dokploy/server/api/routers/certificate.ts @@ -25,14 +25,14 @@ export const certificateRouter = createTRPCRouter({ message: "Please set a server to create a certificate", }); } - return await createCertificate(input, ctx.user.adminId); + return await createCertificate(input, ctx.user.ownerId); }), one: adminProcedure .input(apiFindCertificate) .query(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if (IS_CLOUD && certificates.adminId !== ctx.user.adminId) { + if (IS_CLOUD && certificates.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this certificate", @@ -44,7 +44,7 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .mutation(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if (IS_CLOUD && certificates.adminId !== ctx.user.adminId) { + if (IS_CLOUD && certificates.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this certificate", @@ -56,7 +56,7 @@ export const certificateRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { return await db.query.certificates.findMany({ // TODO: Remove this line when the cloud version is ready - ...(IS_CLOUD && { where: eq(certificates.adminId, ctx.user.adminId) }), + ...(IS_CLOUD && { where: eq(certificates.userId, ctx.user.ownerId) }), }); }), }); diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 463d0398c..5d5c11731 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -71,7 +71,7 @@ export const composeRouter = createTRPCRouter({ }); } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -97,7 +97,7 @@ export const composeRouter = createTRPCRouter({ } const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -110,7 +110,7 @@ export const composeRouter = createTRPCRouter({ .input(apiUpdateCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this compose", @@ -126,7 +126,7 @@ export const composeRouter = createTRPCRouter({ } const composeResult = await findComposeById(input.composeId); - if (composeResult.project.adminId !== ctx.user.adminId) { + if (composeResult.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this compose", @@ -157,7 +157,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to clean this compose", @@ -170,7 +170,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFetchServices) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to load this compose", @@ -184,7 +184,7 @@ export const composeRouter = createTRPCRouter({ try { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to fetch this compose", @@ -209,7 +209,7 @@ export const composeRouter = createTRPCRouter({ .input(apiRandomizeCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to randomize this compose", @@ -221,7 +221,7 @@ export const composeRouter = createTRPCRouter({ .input(apiRandomizeCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to randomize this compose", @@ -236,7 +236,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to get this compose", @@ -254,7 +254,7 @@ export const composeRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this compose", @@ -287,7 +287,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to redeploy this compose", @@ -319,7 +319,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this compose", @@ -333,7 +333,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this compose", @@ -348,7 +348,7 @@ export const composeRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to get this compose", @@ -361,7 +361,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to refresh this compose", diff --git a/apps/dokploy/server/api/routers/deployment.ts b/apps/dokploy/server/api/routers/deployment.ts index bf981c6d9..b9daaf0e7 100644 --- a/apps/dokploy/server/api/routers/deployment.ts +++ b/apps/dokploy/server/api/routers/deployment.ts @@ -19,7 +19,7 @@ export const deploymentRouter = createTRPCRouter({ .input(apiFindAllByApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -32,7 +32,7 @@ export const deploymentRouter = createTRPCRouter({ .input(apiFindAllByCompose) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -44,7 +44,7 @@ export const deploymentRouter = createTRPCRouter({ .input(apiFindAllByServer) .query(async ({ input, ctx }) => { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this server", diff --git a/apps/dokploy/server/api/routers/destination.ts b/apps/dokploy/server/api/routers/destination.ts index d13928be8..49edadf5e 100644 --- a/apps/dokploy/server/api/routers/destination.ts +++ b/apps/dokploy/server/api/routers/destination.ts @@ -28,7 +28,7 @@ export const destinationRouter = createTRPCRouter({ .input(apiCreateDestination) .mutation(async ({ input, ctx }) => { try { - return await createDestintation(input, ctx.user.adminId); + return await createDestintation(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -84,7 +84,7 @@ export const destinationRouter = createTRPCRouter({ .input(apiFindOneDestination) .query(async ({ input, ctx }) => { const destination = await findDestinationById(input.destinationId); - if (destination.adminId !== ctx.user.adminId) { + if (destination.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this destination", @@ -94,7 +94,7 @@ export const destinationRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { return await db.query.destinations.findMany({ - where: eq(destinations.adminId, ctx.user.adminId), + where: eq(destinations.userId, ctx.user.ownerId), }); }), remove: adminProcedure @@ -103,7 +103,7 @@ export const destinationRouter = createTRPCRouter({ try { const destination = await findDestinationById(input.destinationId); - if (destination.adminId !== ctx.user.adminId) { + if (destination.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this destination", @@ -111,7 +111,7 @@ export const destinationRouter = createTRPCRouter({ } return await removeDestinationById( input.destinationId, - ctx.user.adminId, + ctx.user.ownerId, ); } catch (error) { throw error; @@ -122,7 +122,7 @@ export const destinationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const destination = await findDestinationById(input.destinationId); - if (destination.adminId !== ctx.user.adminId) { + if (destination.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to update this destination", @@ -130,7 +130,7 @@ export const destinationRouter = createTRPCRouter({ } return await updateDestinationById(input.destinationId, { ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw error; diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index f122cf86f..526a156bb 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -30,7 +30,7 @@ export const domainRouter = createTRPCRouter({ try { if (input.domainType === "compose" && input.composeId) { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -38,7 +38,7 @@ export const domainRouter = createTRPCRouter({ } } else if (input.domainType === "application" && input.applicationId) { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -58,7 +58,7 @@ export const domainRouter = createTRPCRouter({ .input(apiFindOneApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -70,7 +70,7 @@ export const domainRouter = createTRPCRouter({ .input(apiFindCompose) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -83,7 +83,7 @@ export const domainRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { return generateTraefikMeDomain( input.appName, - ctx.user.adminId, + ctx.user.ownerId, input.serverId, ); }), @@ -95,7 +95,7 @@ export const domainRouter = createTRPCRouter({ if (currentDomain.applicationId) { const newApp = await findApplicationById(currentDomain.applicationId); - if (newApp.project.adminId !== ctx.user.adminId) { + if (newApp.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -103,7 +103,7 @@ export const domainRouter = createTRPCRouter({ } } else if (currentDomain.composeId) { const newCompose = await findComposeById(currentDomain.composeId); - if (newCompose.project.adminId !== ctx.user.adminId) { + if (newCompose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -114,7 +114,7 @@ export const domainRouter = createTRPCRouter({ currentDomain.previewDeploymentId, ); if ( - newPreviewDeployment.application.project.adminId !== ctx.user.adminId + newPreviewDeployment.application.project.userId !== ctx.user.ownerId ) { throw new TRPCError({ code: "UNAUTHORIZED", @@ -143,7 +143,7 @@ export const domainRouter = createTRPCRouter({ const domain = await findDomainById(input.domainId); if (domain.applicationId) { const application = await findApplicationById(domain.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -151,7 +151,7 @@ export const domainRouter = createTRPCRouter({ } } else if (domain.composeId) { const compose = await findComposeById(domain.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -166,7 +166,7 @@ export const domainRouter = createTRPCRouter({ const domain = await findDomainById(input.domainId); if (domain.applicationId) { const application = await findApplicationById(domain.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -174,7 +174,7 @@ export const domainRouter = createTRPCRouter({ } } else if (domain.composeId) { const compose = await findComposeById(domain.composeId); - if (compose.project.adminId !== ctx.user.adminId) { + if (compose.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", diff --git a/apps/dokploy/server/api/routers/git-provider.ts b/apps/dokploy/server/api/routers/git-provider.ts index abd933923..83e71dd81 100644 --- a/apps/dokploy/server/api/routers/git-provider.ts +++ b/apps/dokploy/server/api/routers/git-provider.ts @@ -18,7 +18,7 @@ export const gitProviderRouter = createTRPCRouter({ github: true, }, orderBy: desc(gitProvider.createdAt), - ...(IS_CLOUD && { where: eq(gitProvider.adminId, ctx.user.adminId) }), + ...(IS_CLOUD && { where: eq(gitProvider.userId, ctx.user.ownerId) }), //TODO: Remove this line when the cloud version is ready }); }), @@ -28,7 +28,7 @@ export const gitProviderRouter = createTRPCRouter({ try { const gitProvider = await findGitProviderById(input.gitProviderId); - if (IS_CLOUD && gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && gitProvider.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", diff --git a/apps/dokploy/server/api/routers/github.ts b/apps/dokploy/server/api/routers/github.ts index 562225778..62c21805d 100644 --- a/apps/dokploy/server/api/routers/github.ts +++ b/apps/dokploy/server/api/routers/github.ts @@ -20,7 +20,7 @@ export const githubRouter = createTRPCRouter({ .input(apiFindOneGithub) .query(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); - if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -33,7 +33,7 @@ export const githubRouter = createTRPCRouter({ .input(apiFindOneGithub) .query(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); - if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -46,7 +46,7 @@ export const githubRouter = createTRPCRouter({ .input(apiFindGithubBranches) .query(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId || ""); - if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -65,7 +65,7 @@ export const githubRouter = createTRPCRouter({ if (IS_CLOUD) { // TODO: mAyBe a rEfaCtoR 🤫 result = result.filter( - (provider) => provider.gitProvider.adminId === ctx.user.adminId, + (provider) => provider.gitProvider.userId === ctx.user.ownerId, ); } @@ -90,7 +90,7 @@ export const githubRouter = createTRPCRouter({ const githubProvider = await findGithubById(input.githubId); if ( IS_CLOUD && - githubProvider.gitProvider.adminId !== ctx.user.adminId + githubProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -111,7 +111,7 @@ export const githubRouter = createTRPCRouter({ .input(apiUpdateGithub) .mutation(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); - if (IS_CLOUD && githubProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -120,7 +120,7 @@ export const githubRouter = createTRPCRouter({ } await updateGitProvider(input.gitProviderId, { name: input.name, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); }), }); diff --git a/apps/dokploy/server/api/routers/gitlab.ts b/apps/dokploy/server/api/routers/gitlab.ts index 6d35f4a28..d3ba5a44a 100644 --- a/apps/dokploy/server/api/routers/gitlab.ts +++ b/apps/dokploy/server/api/routers/gitlab.ts @@ -26,7 +26,7 @@ export const gitlabRouter = createTRPCRouter({ .input(apiCreateGitlab) .mutation(async ({ input, ctx }) => { try { - return await createGitlab(input, ctx.user.adminId); + return await createGitlab(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -39,7 +39,7 @@ export const gitlabRouter = createTRPCRouter({ .input(apiFindOneGitlab) .query(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId); - if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -58,7 +58,7 @@ export const gitlabRouter = createTRPCRouter({ if (IS_CLOUD) { // TODO: mAyBe a rEfaCtoR 🤫 result = result.filter( - (provider) => provider.gitProvider.adminId === ctx.user.adminId, + (provider) => provider.gitProvider.userId === ctx.user.ownerId, ); } const filtered = result @@ -78,7 +78,7 @@ export const gitlabRouter = createTRPCRouter({ .input(apiFindOneGitlab) .query(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId); - if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -92,7 +92,7 @@ export const gitlabRouter = createTRPCRouter({ .input(apiFindGitlabBranches) .query(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId || ""); - if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -108,7 +108,7 @@ export const gitlabRouter = createTRPCRouter({ const gitlabProvider = await findGitlabById(input.gitlabId || ""); if ( IS_CLOUD && - gitlabProvider.gitProvider.adminId !== ctx.user.adminId + gitlabProvider.gitProvider.userId !== ctx.user.ownerId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -130,7 +130,7 @@ export const gitlabRouter = createTRPCRouter({ .input(apiUpdateGitlab) .mutation(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId); - if (IS_CLOUD && gitlabProvider.gitProvider.adminId !== ctx.user.adminId) { + if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -140,7 +140,7 @@ export const gitlabRouter = createTRPCRouter({ if (input.name) { await updateGitProvider(input.gitProviderId, { name: input.name, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); await updateGitlab(input.gitlabId, { diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 6e85d274b..283455fae 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -49,7 +49,7 @@ export const mariadbRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -83,7 +83,7 @@ export const mariadbRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.authId, input.mariadbId, "access"); } const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.adminId !== ctx.user.adminId) { + if (mariadb.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this Mariadb", @@ -96,7 +96,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiFindOneMariaDB) .mutation(async ({ input, ctx }) => { const service = await findMariadbById(input.mariadbId); - if (service.project.adminId !== ctx.user.adminId) { + if (service.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this Mariadb", @@ -133,7 +133,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiSaveExternalPortMariaDB) .mutation(async ({ input, ctx }) => { const mongo = await findMariadbById(input.mariadbId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -149,7 +149,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiDeployMariaDB) .mutation(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.adminId !== ctx.user.adminId) { + if (mariadb.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Mariadb", @@ -170,7 +170,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiDeployMariaDB) .subscription(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.adminId !== ctx.user.adminId) { + if (mariadb.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Mariadb", @@ -187,7 +187,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiChangeMariaDBStatus) .mutation(async ({ input, ctx }) => { const mongo = await findMariadbById(input.mariadbId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this Mariadb status", @@ -206,7 +206,7 @@ export const mariadbRouter = createTRPCRouter({ } const mongo = await findMariadbById(input.mariadbId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this Mariadb", @@ -232,7 +232,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesMariaDB) .mutation(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.adminId !== ctx.user.adminId) { + if (mariadb.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -255,7 +255,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiResetMariadb) .mutation(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.adminId !== ctx.user.adminId) { + if (mariadb.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this Mariadb", @@ -285,7 +285,7 @@ export const mariadbRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { mariadbId, ...rest } = input; const mariadb = await findMariadbById(mariadbId); - if (mariadb.project.adminId !== ctx.user.adminId) { + if (mariadb.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this Mariadb", diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 2bca3ec5a..94a012724 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -48,7 +48,7 @@ export const mongoRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -87,7 +87,7 @@ export const mongoRouter = createTRPCRouter({ } const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this mongo", @@ -101,7 +101,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const service = await findMongoById(input.mongoId); - if (service.project.adminId !== ctx.user.adminId) { + if (service.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this mongo", @@ -124,7 +124,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this mongo", @@ -146,7 +146,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiSaveExternalPortMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -162,7 +162,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiDeployMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this mongo", @@ -182,7 +182,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiDeployMongo) .subscription(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this mongo", @@ -199,7 +199,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiChangeMongoStatus) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this mongo status", @@ -214,7 +214,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiResetMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this mongo", @@ -248,7 +248,7 @@ export const mongoRouter = createTRPCRouter({ const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this mongo", @@ -274,7 +274,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -298,7 +298,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { mongoId, ...rest } = input; const mongo = await findMongoById(mongoId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this mongo", diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 7ebf4623a..efea72076 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -50,7 +50,7 @@ export const mysqlRouter = createTRPCRouter({ } 1; const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -89,7 +89,7 @@ export const mysqlRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.authId, input.mysqlId, "access"); } const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.adminId !== ctx.user.adminId) { + if (mysql.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this MySQL", @@ -102,7 +102,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { const service = await findMySqlById(input.mysqlId); - if (service.project.adminId !== ctx.user.adminId) { + if (service.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this MySQL", @@ -124,7 +124,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this MySQL", @@ -145,7 +145,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiSaveExternalPortMySql) .mutation(async ({ input, ctx }) => { const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -161,7 +161,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiDeployMySql) .mutation(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.adminId !== ctx.user.adminId) { + if (mysql.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this MySQL", @@ -181,7 +181,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiDeployMySql) .subscription(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.adminId !== ctx.user.adminId) { + if (mysql.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this MySQL", @@ -198,7 +198,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiChangeMySqlStatus) .mutation(async ({ input, ctx }) => { const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this MySQL status", @@ -213,7 +213,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiResetMysql) .mutation(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.adminId !== ctx.user.adminId) { + if (mysql.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this MySQL", @@ -244,7 +244,7 @@ export const mysqlRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.authId, input.mysqlId, "delete"); } const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this MySQL", @@ -270,7 +270,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesMySql) .mutation(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.adminId !== ctx.user.adminId) { + if (mysql.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -294,7 +294,7 @@ export const mysqlRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { mysqlId, ...rest } = input; const mysql = await findMySqlById(mysqlId); - if (mysql.project.adminId !== ctx.user.adminId) { + if (mysql.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this MySQL", diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index ea3469731..6c2b469d9 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -6,7 +6,6 @@ import { } from "@/server/api/trpc"; import { db } from "@/server/db"; import { - admins, apiCreateDiscord, apiCreateEmail, apiCreateGotify, @@ -25,6 +24,7 @@ import { apiUpdateTelegram, notifications, server, + users_temp, } from "@/server/db/schema"; import { IS_CLOUD, @@ -57,7 +57,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateSlack) .mutation(async ({ input, ctx }) => { try { - return await createSlackNotification(input, ctx.user.adminId); + return await createSlackNotification(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -71,7 +71,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -80,7 +80,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateSlackNotification({ ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw error; @@ -107,7 +107,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateTelegram) .mutation(async ({ input, ctx }) => { try { - return await createTelegramNotification(input, ctx.user.adminId); + return await createTelegramNotification(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -122,7 +122,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -131,7 +131,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateTelegramNotification({ ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw new TRPCError({ @@ -159,7 +159,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateDiscord) .mutation(async ({ input, ctx }) => { try { - return await createDiscordNotification(input, ctx.user.adminId); + return await createDiscordNotification(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -174,7 +174,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -183,7 +183,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateDiscordNotification({ ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw new TRPCError({ @@ -220,7 +220,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateEmail) .mutation(async ({ input, ctx }) => { try { - return await createEmailNotification(input, ctx.user.adminId); + return await createEmailNotification(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -234,7 +234,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -243,7 +243,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateEmailNotification({ ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw new TRPCError({ @@ -276,7 +276,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -295,7 +295,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiFindOneNotification) .query(async ({ input, ctx }) => { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -314,7 +314,7 @@ export const notificationRouter = createTRPCRouter({ gotify: true, }, orderBy: desc(notifications.createdAt), - ...(IS_CLOUD && { where: eq(notifications.adminId, ctx.user.adminId) }), + ...(IS_CLOUD && { where: eq(notifications.userId, ctx.user.ownerId) }), // TODO: Remove this line when the cloud version is ready }); }), @@ -332,24 +332,24 @@ export const notificationRouter = createTRPCRouter({ ) .mutation(async ({ input }) => { try { - let adminId = ""; + let userId = ""; let ServerName = ""; if (input.ServerType === "Dokploy") { const result = await db .select() - .from(admins) + .from(users_temp) .where( - sql`${admins.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, + sql`${users_temp.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, ); - if (!result?.[0]?.adminId) { + if (!result?.[0]?.id) { throw new TRPCError({ code: "BAD_REQUEST", message: "Token not found", }); } - adminId = result?.[0]?.adminId; + userId = result?.[0]?.id; ServerName = "Dokploy"; } else { const result = await db @@ -359,18 +359,18 @@ export const notificationRouter = createTRPCRouter({ sql`${server.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, ); - if (!result?.[0]?.adminId) { + if (!result?.[0]?.userId) { throw new TRPCError({ code: "BAD_REQUEST", message: "Token not found", }); } - adminId = result?.[0]?.adminId; + userId = result?.[0]?.userId; ServerName = "Remote"; } - await sendServerThresholdNotifications(adminId, { + await sendServerThresholdNotifications(userId, { ...input, ServerName, }); @@ -386,7 +386,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateGotify) .mutation(async ({ input, ctx }) => { try { - return await createGotifyNotification(input, ctx.user.adminId); + return await createGotifyNotification(input, ctx.user.ownerId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -400,7 +400,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.adminId !== ctx.user.adminId) { + if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this notification", @@ -408,7 +408,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateGotifyNotification({ ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw error; diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 92603a613..a7d0de399 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -56,7 +56,7 @@ export const postgresRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -95,7 +95,7 @@ export const postgresRouter = createTRPCRouter({ } const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this Postgres", @@ -109,7 +109,7 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const service = await findPostgresById(input.postgresId); - if (service.project.adminId !== ctx.user.adminId) { + if (service.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this Postgres", @@ -131,7 +131,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiFindOnePostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this Postgres", @@ -153,7 +153,7 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -169,7 +169,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiDeployPostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Postgres", @@ -190,7 +190,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiDeployPostgres) .subscription(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Postgres", @@ -207,7 +207,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiChangePostgresStatus) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this Postgres status", @@ -226,7 +226,7 @@ export const postgresRouter = createTRPCRouter({ } const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this Postgres", @@ -249,7 +249,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesPostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -272,7 +272,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiResetPostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this Postgres", @@ -302,7 +302,7 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { postgresId, ...rest } = input; const postgres = await findPostgresById(postgresId); - if (postgres.project.adminId !== ctx.user.adminId) { + if (postgres.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this Postgres", diff --git a/apps/dokploy/server/api/routers/preview-deployment.ts b/apps/dokploy/server/api/routers/preview-deployment.ts index 74b8461ae..482bb3505 100644 --- a/apps/dokploy/server/api/routers/preview-deployment.ts +++ b/apps/dokploy/server/api/routers/preview-deployment.ts @@ -14,7 +14,7 @@ export const previewDeploymentRouter = createTRPCRouter({ .input(apiFindAllByApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -28,7 +28,7 @@ export const previewDeploymentRouter = createTRPCRouter({ const previewDeployment = await findPreviewDeploymentById( input.previewDeploymentId, ); - if (previewDeployment.application.project.adminId !== ctx.user.adminId) { + if (previewDeployment.application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this preview deployment", @@ -43,7 +43,7 @@ export const previewDeploymentRouter = createTRPCRouter({ const previewDeployment = await findPreviewDeploymentById( input.previewDeploymentId, ); - if (previewDeployment.application.project.adminId !== ctx.user.adminId) { + if (previewDeployment.application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this preview deployment", diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index c2852ff6b..1d2d20360 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -75,7 +75,7 @@ export const projectRouter = createTRPCRouter({ const project = await db.query.projects.findFirst({ where: and( eq(projects.projectId, input.projectId), - eq(projects.adminId, ctx.user.adminId), + eq(projects.userId, ctx.user.ownerId), ), with: { compose: { @@ -115,7 +115,7 @@ export const projectRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -207,7 +207,7 @@ export const projectRouter = createTRPCRouter({ await checkProjectAccess(ctx.user.authId, "delete"); } const currentProject = await findProjectById(input.projectId); - if (currentProject.adminId !== ctx.user.adminId) { + if (currentProject.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this project", @@ -225,7 +225,7 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const currentProject = await findProjectById(input.projectId); - if (currentProject.adminId !== ctx.user.adminId) { + if (currentProject.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this project", diff --git a/apps/dokploy/server/api/routers/redirects.ts b/apps/dokploy/server/api/routers/redirects.ts index bcd7962a6..1a8ba4cfa 100644 --- a/apps/dokploy/server/api/routers/redirects.ts +++ b/apps/dokploy/server/api/routers/redirects.ts @@ -18,7 +18,7 @@ export const redirectsRouter = createTRPCRouter({ .input(apiCreateRedirect) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -31,7 +31,7 @@ export const redirectsRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const redirect = await findRedirectById(input.redirectId); const application = await findApplicationById(redirect.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -44,7 +44,7 @@ export const redirectsRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const redirect = await findRedirectById(input.redirectId); const application = await findApplicationById(redirect.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -57,7 +57,7 @@ export const redirectsRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const redirect = await findRedirectById(input.redirectId); const application = await findApplicationById(redirect.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index 967fb51a2..af1d4234a 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -48,7 +48,7 @@ export const redisRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.adminId !== ctx.user.adminId) { + if (project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -80,7 +80,7 @@ export const redisRouter = createTRPCRouter({ } const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this Redis", @@ -93,7 +93,7 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this Redis", @@ -115,7 +115,7 @@ export const redisRouter = createTRPCRouter({ .input(apiResetRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this Redis", @@ -145,7 +145,7 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this Redis", @@ -166,7 +166,7 @@ export const redisRouter = createTRPCRouter({ .input(apiSaveExternalPortRedis) .mutation(async ({ input, ctx }) => { const mongo = await findRedisById(input.redisId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -182,7 +182,7 @@ export const redisRouter = createTRPCRouter({ .input(apiDeployRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Redis", @@ -202,7 +202,7 @@ export const redisRouter = createTRPCRouter({ .input(apiDeployRedis) .subscription(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Redis", @@ -218,7 +218,7 @@ export const redisRouter = createTRPCRouter({ .input(apiChangeRedisStatus) .mutation(async ({ input, ctx }) => { const mongo = await findRedisById(input.redisId); - if (mongo.project.adminId !== ctx.user.adminId) { + if (mongo.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this Redis status", @@ -238,7 +238,7 @@ export const redisRouter = createTRPCRouter({ const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this Redis", @@ -261,7 +261,7 @@ export const redisRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.adminId !== ctx.user.adminId) { + if (redis.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index f66ed4aea..76df5cd65 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -10,7 +10,7 @@ import { createRegistry, execAsync, execAsyncRemote, - findAllRegistryByAdminId, + findAllRegistryByUserId, findRegistryById, removeRegistry, updateRegistry, @@ -22,13 +22,13 @@ export const registryRouter = createTRPCRouter({ create: adminProcedure .input(apiCreateRegistry) .mutation(async ({ ctx, input }) => { - return await createRegistry(input, ctx.user.adminId); + return await createRegistry(input, ctx.user.ownerId); }), remove: adminProcedure .input(apiRemoveRegistry) .mutation(async ({ ctx, input }) => { const registry = await findRegistryById(input.registryId); - if (registry.adminId !== ctx.user.adminId) { + if (registry.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this registry", @@ -41,7 +41,7 @@ export const registryRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { registryId, ...rest } = input; const registry = await findRegistryById(registryId); - if (registry.adminId !== ctx.user.adminId) { + if (registry.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to update this registry", @@ -61,13 +61,13 @@ export const registryRouter = createTRPCRouter({ return true; }), all: protectedProcedure.query(async ({ ctx }) => { - return await findAllRegistryByAdminId(ctx.user.adminId); + return await findAllRegistryByUserId(ctx.user.ownerId); }), one: adminProcedure .input(apiFindOneRegistry) .query(async ({ input, ctx }) => { const registry = await findRegistryById(input.registryId); - if (registry.adminId !== ctx.user.adminId) { + if (registry.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this registry", diff --git a/apps/dokploy/server/api/routers/security.ts b/apps/dokploy/server/api/routers/security.ts index 5318a2939..ca4892d26 100644 --- a/apps/dokploy/server/api/routers/security.ts +++ b/apps/dokploy/server/api/routers/security.ts @@ -18,7 +18,7 @@ export const securityRouter = createTRPCRouter({ .input(apiCreateSecurity) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -31,7 +31,7 @@ export const securityRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const security = await findSecurityById(input.securityId); const application = await findApplicationById(security.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -44,7 +44,7 @@ export const securityRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const security = await findSecurityById(input.securityId); const application = await findApplicationById(security.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -57,7 +57,7 @@ export const securityRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const security = await findSecurityById(input.securityId); const application = await findApplicationById(security.applicationId); - if (application.project.adminId !== ctx.user.adminId) { + if (application.project.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 0094b6756..8e63d7e66 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -21,9 +21,9 @@ import { createServer, defaultCommand, deleteServer, - findAdminById, findServerById, - findServersByAdminId, + findServersByUserId, + findUserById, getPublicIpWithFallback, haveActiveServices, removeDeploymentsByServerId, @@ -42,15 +42,15 @@ export const serverRouter = createTRPCRouter({ .input(apiCreateServer) .mutation(async ({ ctx, input }) => { try { - const admin = await findAdminById(ctx.user.adminId); - const servers = await findServersByAdminId(admin.adminId); - if (IS_CLOUD && servers.length >= admin.serversQuantity) { + const user = await findUserById(ctx.user.ownerId); + const servers = await findServersByUserId(user.id); + if (IS_CLOUD && servers.length >= user.serversQuantity) { throw new TRPCError({ code: "BAD_REQUEST", message: "You cannot create more servers", }); } - const project = await createServer(input, ctx.user.adminId); + const project = await createServer(input, ctx.user.ownerId); return project; } catch (error) { throw new TRPCError({ @@ -65,7 +65,7 @@ export const serverRouter = createTRPCRouter({ .input(apiFindOneServer) .query(async ({ input, ctx }) => { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this server", @@ -93,7 +93,7 @@ export const serverRouter = createTRPCRouter({ .leftJoin(mongo, eq(mongo.serverId, server.serverId)) .leftJoin(mysql, eq(mysql.serverId, server.serverId)) .leftJoin(postgres, eq(postgres.serverId, server.serverId)) - .where(eq(server.adminId, ctx.user.adminId)) + .where(eq(server.userId, ctx.user.ownerId)) .orderBy(desc(server.createdAt)) .groupBy(server.serverId); @@ -105,10 +105,10 @@ export const serverRouter = createTRPCRouter({ where: IS_CLOUD ? and( isNotNull(server.sshKeyId), - eq(server.adminId, ctx.user.adminId), + eq(server.userId, ctx.user.ownerId), eq(server.serverStatus, "active"), ) - : and(isNotNull(server.sshKeyId), eq(server.adminId, ctx.user.adminId)), + : and(isNotNull(server.sshKeyId), eq(server.userId, ctx.user.ownerId)), }); return result; }), @@ -117,7 +117,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", @@ -142,7 +142,7 @@ export const serverRouter = createTRPCRouter({ .subscription(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", @@ -162,7 +162,7 @@ export const serverRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to validate this server", @@ -204,7 +204,7 @@ export const serverRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to validate this server", @@ -254,7 +254,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", @@ -296,7 +296,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this server", @@ -315,12 +315,9 @@ export const serverRouter = createTRPCRouter({ await deleteServer(input.serverId); if (IS_CLOUD) { - const admin = await findAdminById(ctx.user.adminId); + const admin = await findUserById(ctx.user.ownerId); - await updateServersBasedOnQuantity( - admin.adminId, - admin.serversQuantity, - ); + await updateServersBasedOnQuantity(admin.id, admin.serversQuantity); } return currentServer; @@ -333,7 +330,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.userId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this server", diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 116de6ad8..6dda93835 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -47,7 +47,6 @@ import { startServiceRemote, stopService, stopServiceRemote, - updateAdmin, updateLetsEncryptEmail, updateServerById, updateServerTraefik, diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index fe2f24f91..4beb0bae8 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -24,9 +24,10 @@ export const sshRouter = createTRPCRouter({ .input(apiCreateSshKey) .mutation(async ({ input, ctx }) => { try { + console.log(ctx.user.ownerId); await createSshKey({ ...input, - adminId: ctx.user.adminId, + userId: ctx.user.ownerId, }); } catch (error) { throw new TRPCError({ @@ -41,7 +42,7 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const sshKey = await findSSHKeyById(input.sshKeyId); - if (IS_CLOUD && sshKey.adminId !== ctx.user.adminId) { + if (IS_CLOUD && sshKey.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -59,7 +60,7 @@ export const sshRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const sshKey = await findSSHKeyById(input.sshKeyId); - if (IS_CLOUD && sshKey.adminId !== ctx.user.adminId) { + if (IS_CLOUD && sshKey.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -70,7 +71,7 @@ export const sshRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { return await db.query.sshKeys.findMany({ - ...(IS_CLOUD && { where: eq(sshKeys.adminId, ctx.user.adminId) }), + ...(IS_CLOUD && { where: eq(sshKeys.userId, ctx.user.ownerId) }), orderBy: desc(sshKeys.createdAt), }); // TODO: Remove this line when the cloud version is ready @@ -85,7 +86,7 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const sshKey = await findSSHKeyById(input.sshKeyId); - if (IS_CLOUD && sshKey.adminId !== ctx.user.adminId) { + if (IS_CLOUD && sshKey.userId !== ctx.user.ownerId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts index 7a8a537c1..91fa44fd6 100644 --- a/apps/dokploy/server/api/routers/stripe.ts +++ b/apps/dokploy/server/api/routers/stripe.ts @@ -1,9 +1,9 @@ import { WEBSITE_URL, getStripeItems } from "@/server/utils/stripe"; import { IS_CLOUD, - findAdminById, - findServersByAdminId, - updateAdmin, + findServersByUserId, + findUserById, + updateUser, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import Stripe from "stripe"; @@ -12,8 +12,8 @@ import { adminProcedure, createTRPCRouter } from "../trpc"; export const stripeRouter = createTRPCRouter({ getProducts: adminProcedure.query(async ({ ctx }) => { - const admin = await findAdminById(ctx.user.adminId); - const stripeCustomerId = admin.stripeCustomerId; + const user = await findUserById(ctx.user.ownerId); + const stripeCustomerId = user.stripeCustomerId; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: "2024-09-30.acacia", @@ -56,15 +56,15 @@ export const stripeRouter = createTRPCRouter({ }); const items = getStripeItems(input.serverQuantity, input.isAnnual); - const admin = await findAdminById(ctx.user.adminId); + const user = await findUserById(ctx.user.ownerId); - let stripeCustomerId = admin.stripeCustomerId; + let stripeCustomerId = user.stripeCustomerId; if (stripeCustomerId) { const customer = await stripe.customers.retrieve(stripeCustomerId); if (customer.deleted) { - await updateAdmin(admin.authId, { + await updateUser(user.id, { stripeCustomerId: null, }); stripeCustomerId = null; @@ -78,7 +78,7 @@ export const stripeRouter = createTRPCRouter({ customer: stripeCustomerId, }), metadata: { - adminId: admin.adminId, + ownerId: user.id, }, allow_promotion_codes: true, success_url: `${WEBSITE_URL}/dashboard/settings/servers?success=true`, @@ -89,15 +89,15 @@ export const stripeRouter = createTRPCRouter({ }), createCustomerPortalSession: adminProcedure.mutation( async ({ ctx, input }) => { - const admin = await findAdminById(ctx.user.adminId); + const user = await findUserById(ctx.user.ownerId); - if (!admin.stripeCustomerId) { + if (!user.stripeCustomerId) { throw new TRPCError({ code: "BAD_REQUEST", message: "Stripe Customer ID not found", }); } - const stripeCustomerId = admin.stripeCustomerId; + const stripeCustomerId = user.stripeCustomerId; const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: "2024-09-30.acacia", @@ -119,13 +119,14 @@ export const stripeRouter = createTRPCRouter({ ), canCreateMoreServers: adminProcedure.query(async ({ ctx }) => { - const admin = await findAdminById(ctx.user.adminId); - const servers = await findServersByAdminId(admin.adminId); + const user = await findUserById(ctx.user.ownerId); + console.log(user); + // const servers = await findServersByUserId(user.id); if (!IS_CLOUD) { return true; } - return servers.length < admin.serversQuantity; + return servers.length < user.serversQuantity; }), }); diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 9579407b5..9f373ad3f 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -31,7 +31,7 @@ import { ZodError } from "zod"; */ interface CreateContextOptions { - user: (User & { authId: string; rol: "admin" | "user" }) | null; + user: (User & { rol: "admin" | "user"; ownerId: string }) | null; session: Session | null; req: CreateNextContextOptions["req"]; res: CreateNextContextOptions["res"]; @@ -81,10 +81,10 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { session: session, ...((user && { user: { - authId: "Null", email: user.email, rol: user.role, id: user.id, + ownerId: user.ownerId, }, }) || { user: null, diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 3e81fbf69..e7a263fa0 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,6 +1,7 @@ import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { nanoid } from "nanoid"; import { users_temp } from "./user"; +import { relations } from "drizzle-orm"; export const account = pgTable("account", { id: text("id") @@ -29,6 +30,13 @@ export const account = pgTable("account", { confirmationExpiresAt: text("confirmationExpiresAt"), }); +export const accountRelations = relations(account, ({ one }) => ({ + user: one(users_temp, { + fields: [account.userId], + references: [users_temp.id], + }), +})); + export const verification = pgTable("verification", { id: text("id").primaryKey(), identifier: text("identifier").notNull(), @@ -52,6 +60,13 @@ export const organization = pgTable("organization", { .references(() => users_temp.id), }); +export const organizationRelations = relations(organization, ({ one }) => ({ + owner: one(users_temp, { + fields: [organization.ownerId], + references: [users_temp.id], + }), +})); + export const member = pgTable("member", { id: text("id") .primaryKey() @@ -66,6 +81,17 @@ export const member = pgTable("member", { createdAt: timestamp("created_at").notNull(), }); +export const memberRelations = relations(member, ({ one }) => ({ + organization: one(organization, { + fields: [member.organizationId], + references: [organization.id], + }), + user: one(users_temp, { + fields: [member.userId], + references: [users_temp.id], + }), +})); + export const invitation = pgTable("invitation", { id: text("id").primaryKey(), organizationId: text("organization_id") @@ -79,3 +105,10 @@ export const invitation = pgTable("invitation", { .notNull() .references(() => users_temp.id), }); + +export const invitationRelations = relations(invitation, ({ one }) => ({ + organization: one(organization, { + fields: [invitation.organizationId], + references: [organization.id], + }), +})); diff --git a/packages/server/src/db/schema/bitbucket.ts b/packages/server/src/db/schema/bitbucket.ts index 393cb1e7d..b8ecb6682 100644 --- a/packages/server/src/db/schema/bitbucket.ts +++ b/packages/server/src/db/schema/bitbucket.ts @@ -61,5 +61,5 @@ export const apiUpdateBitbucket = createSchema.extend({ name: z.string().min(1), bitbucketUsername: z.string().optional(), bitbucketWorkspaceName: z.string().optional(), - adminId: z.string().optional(), + userId: z.string().optional(), }); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 462b67109..78596b0d7 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -3,7 +3,6 @@ import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { admins } from "./admin"; import { users_temp } from "./user"; // import { user } from "./user"; @@ -153,7 +152,7 @@ export const apiCreateSlack = notificationsSchema export const apiUpdateSlack = apiCreateSlack.partial().extend({ notificationId: z.string().min(1), slackId: z.string(), - adminId: z.string().optional(), + userId: z.string().optional(), }); export const apiTestSlackConnection = apiCreateSlack.pick({ @@ -180,7 +179,7 @@ export const apiCreateTelegram = notificationsSchema export const apiUpdateTelegram = apiCreateTelegram.partial().extend({ notificationId: z.string().min(1), telegramId: z.string().min(1), - adminId: z.string().optional(), + userId: z.string().optional(), }); export const apiTestTelegramConnection = apiCreateTelegram.pick({ @@ -207,7 +206,7 @@ export const apiCreateDiscord = notificationsSchema export const apiUpdateDiscord = apiCreateDiscord.partial().extend({ notificationId: z.string().min(1), discordId: z.string().min(1), - adminId: z.string().optional(), + userId: z.string().optional(), }); export const apiTestDiscordConnection = apiCreateDiscord @@ -241,7 +240,7 @@ export const apiCreateEmail = notificationsSchema export const apiUpdateEmail = apiCreateEmail.partial().extend({ notificationId: z.string().min(1), emailId: z.string().min(1), - adminId: z.string().optional(), + userId: z.string().optional(), }); export const apiTestEmailConnection = apiCreateEmail.pick({ @@ -273,7 +272,7 @@ export const apiCreateGotify = notificationsSchema export const apiUpdateGotify = apiCreateGotify.partial().extend({ notificationId: z.string().min(1), gotifyId: z.string().min(1), - adminId: z.string().optional(), + userId: z.string().optional(), }); export const apiTestGotifyConnection = apiCreateGotify diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index d754687cd..0a3535137 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -196,8 +196,8 @@ export const usersRelations = relations(users, ({ one }) => ({ // }), })); -const createSchema = createInsertSchema(users, { - userId: z.string().min(1), +const createSchema = createInsertSchema(users_temp, { + id: z.string().min(1), // authId: z.string().min(1), token: z.string().min(1), isRegistered: z.boolean().optional(), @@ -218,7 +218,7 @@ export const apiCreateUserInvitation = createSchema.pick({}).extend({ export const apiRemoveUser = createSchema .pick({ - // authId: true, + id: true, }) .required(); @@ -230,7 +230,7 @@ export const apiFindOneToken = createSchema export const apiAssignPermissions = createSchema .pick({ - userId: true, + id: true, canCreateProjects: true, canCreateServices: true, canDeleteProjects: true, @@ -247,7 +247,7 @@ export const apiAssignPermissions = createSchema export const apiFindOneUser = createSchema .pick({ - userId: true, + id: true, }) .required(); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 878313ae5..48e11f215 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -44,6 +44,9 @@ export const auth = betterAuth({ role: { type: "string", }, + ownerId: { + type: "string", + }, }, }, plugins: [organization()], @@ -56,7 +59,20 @@ export const validateRequest = async (request: IncomingMessage) => { }), }); - console.log(session); + if (session?.user.role === "user") { + const owner = await db.query.member.findFirst({ + where: eq(schema.member.userId, session.user.id), + with: { + organization: true, + }, + }); + + if (owner) { + session.user.ownerId = owner.organization.ownerId; + } + } else { + session.user.ownerId = session?.user.id; + } if (!session?.session || !session.user) { return { diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 5155231b0..c4b7650ba 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -11,7 +11,7 @@ import * as bcrypt from "bcrypt"; import { eq } from "drizzle-orm"; import { IS_CLOUD } from "../constants"; -export type Admin = typeof admins.$inferSelect; +export type Admin = typeof users_temp.$inferSelect; export const createInvitation = async ( input: typeof apiCreateUserInvitation._type, adminId: string, @@ -48,33 +48,30 @@ export const createInvitation = async ( }); }; -export const findAdminById = async (adminId: string) => { - const admin = await db.query.admins.findFirst({ - where: eq(admins.adminId, adminId), +export const findUserById = async (userId: string) => { + const user = await db.query.users_temp.findFirst({ + where: eq(users_temp.id, userId), }); - if (!admin) { + if (!user) { throw new TRPCError({ code: "NOT_FOUND", - message: "Admin not found", + message: "User not found", }); } - return admin; + return user; }; -export const updateAdmin = async ( - authId: string, - adminData: Partial, -) => { - const admin = await db - .update(admins) +export const updateUser = async (userId: string, userData: Partial) => { + const user = await db + .update(users_temp) .set({ - ...adminData, + ...userData, }) - .where(eq(admins.authId, authId)) + .where(eq(users_temp.id, userId)) .returning() .then((res) => res[0]); - return admin; + return user; }; export const updateAdminById = async ( @@ -93,6 +90,13 @@ export const updateAdminById = async ( return admin; }; +export const findAdminById = async (userId: string) => { + const admin = await db.query.admins.findFirst({ + where: eq(admins.userId, userId), + }); + return admin; +}; + export const isAdminPresent = async () => { const admin = await db.query.user.findFirst({ where: eq(user.role, "admin"), @@ -154,10 +158,10 @@ export const getUserByToken = async (token: string) => { }; }; -export const removeUserByAuthId = async (authId: string) => { +export const removeUserById = async (userId: string) => { await db - .delete(auth) - .where(eq(auth.id, authId)) + .delete(users_temp) + .where(eq(users_temp.id, userId)) .returning() .then((res) => res[0]); }; @@ -170,7 +174,7 @@ export const removeAdminByAuthId = async (authId: string) => { const users = admin.users; for (const user of users) { - await removeUserByAuthId(user.authId); + await removeUserById(user.id); } // Then delete the auth record which will cascade delete the admin return await db diff --git a/packages/server/src/services/bitbucket.ts b/packages/server/src/services/bitbucket.ts index 218071567..4ce4a7b03 100644 --- a/packages/server/src/services/bitbucket.ts +++ b/packages/server/src/services/bitbucket.ts @@ -12,14 +12,14 @@ export type Bitbucket = typeof bitbucket.$inferSelect; export const createBitbucket = async ( input: typeof apiCreateBitbucket._type, - adminId: string, + userId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "bitbucket", - adminId: adminId, + userId: userId, name: input.name, }) .returning() @@ -74,12 +74,12 @@ export const updateBitbucket = async ( .where(eq(bitbucket.bitbucketId, bitbucketId)) .returning(); - if (input.name || input.adminId) { + if (input.name || input.userId) { await tx .update(gitProvider) .set({ name: input.name, - adminId: input.adminId, + userId: input.userId, }) .where(eq(gitProvider.gitProviderId, input.gitProviderId)) .returning(); diff --git a/packages/server/src/services/github.ts b/packages/server/src/services/github.ts index a5d9d8638..b23edf207 100644 --- a/packages/server/src/services/github.ts +++ b/packages/server/src/services/github.ts @@ -12,14 +12,14 @@ import { updatePreviewDeployment } from "./preview-deployment"; export type Github = typeof github.$inferSelect; export const createGithub = async ( input: typeof apiCreateGithub._type, - adminId: string, + userId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "github", - adminId: adminId, + userId: userId, name: input.name, }) .returning() diff --git a/packages/server/src/services/notification.ts b/packages/server/src/services/notification.ts index 8b17da367..03f6bd09d 100644 --- a/packages/server/src/services/notification.ts +++ b/packages/server/src/services/notification.ts @@ -24,7 +24,7 @@ export type Notification = typeof notifications.$inferSelect; export const createSlackNotification = async ( input: typeof apiCreateSlack._type, - adminId: string, + userId: string, ) => { await db.transaction(async (tx) => { const newSlack = await tx @@ -54,7 +54,7 @@ export const createSlackNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "slack", - adminId: adminId, + userId: userId, serverThreshold: input.serverThreshold, }) .returning() @@ -84,7 +84,7 @@ export const updateSlackNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - adminId: input.adminId, + userId: input.userId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -114,7 +114,7 @@ export const updateSlackNotification = async ( export const createTelegramNotification = async ( input: typeof apiCreateTelegram._type, - adminId: string, + userId: string, ) => { await db.transaction(async (tx) => { const newTelegram = await tx @@ -144,7 +144,7 @@ export const createTelegramNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "telegram", - adminId: adminId, + userId: userId, serverThreshold: input.serverThreshold, }) .returning() @@ -174,7 +174,7 @@ export const updateTelegramNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - adminId: input.adminId, + userId: input.userId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -204,7 +204,7 @@ export const updateTelegramNotification = async ( export const createDiscordNotification = async ( input: typeof apiCreateDiscord._type, - adminId: string, + userId: string, ) => { await db.transaction(async (tx) => { const newDiscord = await tx @@ -234,7 +234,7 @@ export const createDiscordNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "discord", - adminId: adminId, + userId: userId, serverThreshold: input.serverThreshold, }) .returning() @@ -264,7 +264,7 @@ export const updateDiscordNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - adminId: input.adminId, + userId: input.userId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -294,7 +294,7 @@ export const updateDiscordNotification = async ( export const createEmailNotification = async ( input: typeof apiCreateEmail._type, - adminId: string, + userId: string, ) => { await db.transaction(async (tx) => { const newEmail = await tx @@ -328,7 +328,7 @@ export const createEmailNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "email", - adminId: adminId, + userId: userId, serverThreshold: input.serverThreshold, }) .returning() @@ -358,7 +358,7 @@ export const updateEmailNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - adminId: input.adminId, + userId: input.userId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -392,7 +392,7 @@ export const updateEmailNotification = async ( export const createGotifyNotification = async ( input: typeof apiCreateGotify._type, - adminId: string, + userId: string, ) => { await db.transaction(async (tx) => { const newGotify = await tx @@ -424,7 +424,7 @@ export const createGotifyNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "gotify", - adminId: adminId, + userId: userId, }) .returning() .then((value) => value[0]); @@ -453,7 +453,7 @@ export const updateGotifyNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - adminId: input.adminId, + userId: input.userId, }) .where(eq(notifications.notificationId, input.notificationId)) .returning() diff --git a/packages/server/src/services/registry.ts b/packages/server/src/services/registry.ts index 2bcf3a4a4..b92eabdb0 100644 --- a/packages/server/src/services/registry.ts +++ b/packages/server/src/services/registry.ts @@ -12,14 +12,14 @@ export type Registry = typeof registry.$inferSelect; export const createRegistry = async ( input: typeof apiCreateRegistry._type, - adminId: string, + userId: string, ) => { return await db.transaction(async (tx) => { const newRegistry = await tx .insert(registry) .values({ ...input, - adminId: adminId, + userId: userId, }) .returning() .then((value) => value[0]); @@ -135,9 +135,9 @@ export const findRegistryById = async (registryId: string) => { return registryResponse; }; -export const findAllRegistryByAdminId = async (adminId: string) => { +export const findAllRegistryByUserId = async (userId: string) => { const registryResponse = await db.query.registry.findMany({ - where: eq(registry.adminId, adminId), + where: eq(registry.userId, userId), }); return registryResponse; }; diff --git a/packages/server/src/services/server.ts b/packages/server/src/services/server.ts index 081b19fad..6aaa8becc 100644 --- a/packages/server/src/services/server.ts +++ b/packages/server/src/services/server.ts @@ -7,13 +7,13 @@ export type Server = typeof server.$inferSelect; export const createServer = async ( input: typeof apiCreateServer._type, - adminId: string, + userId: string, ) => { const newServer = await db .insert(server) .values({ ...input, - adminId: adminId, + userId: userId, }) .returning() .then((value) => value[0]); @@ -45,9 +45,9 @@ export const findServerById = async (serverId: string) => { return currentServer; }; -export const findServersByAdminId = async (adminId: string) => { +export const findServersByUserId = async (userId: string) => { const servers = await db.query.server.findMany({ - where: eq(server.adminId, adminId), + where: eq(server.userId, userId), orderBy: desc(server.createdAt), }); From b6c29ccf0543d9713dd8a2f543da0e45a0c30799 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 14 Feb 2025 02:40:11 -0600 Subject: [PATCH 027/126] refactor: update --- apps/dokploy/server/api/routers/admin.ts | 57 ++++++------ .../dokploy/server/api/routers/application.ts | 16 +--- apps/dokploy/server/api/routers/auth.ts | 21 +++-- apps/dokploy/server/api/routers/compose.ts | 15 +-- apps/dokploy/server/api/routers/mariadb.ts | 8 +- apps/dokploy/server/api/routers/mongo.ts | 8 +- apps/dokploy/server/api/routers/mysql.ts | 8 +- apps/dokploy/server/api/routers/postgres.ts | 8 +- apps/dokploy/server/api/routers/project.ts | 18 ++-- apps/dokploy/server/api/routers/redis.ts | 8 +- apps/dokploy/server/api/routers/stripe.ts | 3 +- packages/server/src/db/schema/account.ts | 2 +- packages/server/src/db/schema/user.ts | 13 ++- packages/server/src/services/admin.ts | 3 + packages/server/src/services/auth.ts | 7 +- packages/server/src/services/server.ts | 1 + packages/server/src/services/user.ts | 91 +++++++++---------- packages/server/src/setup/monitoring-setup.ts | 14 +-- 18 files changed, 154 insertions(+), 147 deletions(-) diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 0b232f6da..e8467283a 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -16,6 +16,7 @@ import { removeUserById, setupWebMonitoring, updateAdminById, + updateUser, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; @@ -101,6 +102,9 @@ export const adminRouter = createTRPCRouter({ message: "You are not allowed to assign permissions", }); } + await updateUser(user.id, { + ...input, + }); // await db // .update(users) // .set({ @@ -130,32 +134,33 @@ export const adminRouter = createTRPCRouter({ }); } - // await updateAdminById(admin.adminId, { - // metricsConfig: { - // server: { - // type: "Dokploy", - // refreshRate: input.metricsConfig.server.refreshRate, - // port: input.metricsConfig.server.port, - // token: input.metricsConfig.server.token, - // cronJob: input.metricsConfig.server.cronJob, - // urlCallback: input.metricsConfig.server.urlCallback, - // retentionDays: input.metricsConfig.server.retentionDays, - // thresholds: { - // cpu: input.metricsConfig.server.thresholds.cpu, - // memory: input.metricsConfig.server.thresholds.memory, - // }, - // }, - // containers: { - // refreshRate: input.metricsConfig.containers.refreshRate, - // services: { - // include: input.metricsConfig.containers.services.include || [], - // exclude: input.metricsConfig.containers.services.exclude || [], - // }, - // }, - // }, - // }); - // const currentServer = await setupWebMonitoring(admin.adminId); - // return currentServer; + await updateUser(user.id, { + metricsConfig: { + server: { + type: "Dokploy", + refreshRate: input.metricsConfig.server.refreshRate, + port: input.metricsConfig.server.port, + token: input.metricsConfig.server.token, + cronJob: input.metricsConfig.server.cronJob, + urlCallback: input.metricsConfig.server.urlCallback, + retentionDays: input.metricsConfig.server.retentionDays, + thresholds: { + cpu: input.metricsConfig.server.thresholds.cpu, + memory: input.metricsConfig.server.thresholds.memory, + }, + }, + containers: { + refreshRate: input.metricsConfig.containers.refreshRate, + services: { + include: input.metricsConfig.containers.services.include || [], + exclude: input.metricsConfig.containers.services.exclude || [], + }, + }, + }, + }); + + const currentServer = await setupWebMonitoring(user.id); + return currentServer; } catch (error) { throw error; } diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index d07caa435..0f7c1eb67 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -61,7 +61,7 @@ export const applicationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -81,7 +81,7 @@ export const applicationRouter = createTRPCRouter({ const newApplication = await createApplication(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newApplication.applicationId); + await addNewService(ctx.user.id, newApplication.applicationId); } return newApplication; } catch (error: unknown) { @@ -99,11 +99,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess( - ctx.user.authId, - input.applicationId, - "access", - ); + await checkServiceAccess(ctx.user.id, input.applicationId, "access"); } const application = await findApplicationById(input.applicationId); if (application.project.userId !== ctx.user.ownerId) { @@ -145,11 +141,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess( - ctx.user.authId, - input.applicationId, - "delete", - ); + await checkServiceAccess(ctx.user.id, input.applicationId, "delete"); } const application = await findApplicationById(input.applicationId); diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index cc88f2c34..7f1382b49 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -16,6 +16,7 @@ import { createUser, findAuthByEmail, findAuthById, + findUserById, generate2FASecret, getUserByToken, lucia, @@ -24,6 +25,7 @@ import { sendDiscordNotification, sendEmailNotification, updateAuthById, + updateUser, validateRequest, verify2FA, } from "@dokploy/server"; @@ -252,19 +254,18 @@ export const authRouter = createTRPCRouter({ }), generateToken: protectedProcedure.mutation(async ({ ctx, input }) => { - const auth = await findAuthById(ctx.user.authId); + const auth = await findUserById(ctx.user.id); + console.log(auth); if (auth.token) { await luciaToken.invalidateSession(auth.token); } - const session = await luciaToken.createSession(auth?.id || "", { - expiresIn: 60 * 60 * 24 * 30, - }); - - await updateAuthById(auth.id, { - token: session.id, - }); - + // const session = await luciaToken.createSession(auth?.id || "", { + // expiresIn: 60 * 60 * 24 * 30, + // }); + // await updateUser(auth.id, { + // token: session.id, + // }); return auth; }), verifyToken: protectedProcedure.mutation(async () => { @@ -276,7 +277,7 @@ export const authRouter = createTRPCRouter({ }), generate2FASecret: protectedProcedure.query(async ({ ctx }) => { - return await generate2FASecret(ctx.user.authId); + return await generate2FASecret(ctx.user.id); }), verify2FASetup: protectedProcedure .input(apiVerify2FA) diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 5d5c11731..b3fbae93a 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -44,6 +44,7 @@ import { findDomainsByComposeId, findProjectById, findServerById, + findUserById, loadServices, randomizeComposeFile, randomizeIsolatedDeploymentComposeFile, @@ -61,7 +62,7 @@ export const composeRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -80,7 +81,7 @@ export const composeRouter = createTRPCRouter({ const newService = await createCompose(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newService.composeId); + await addNewService(ctx.user.id, newService.composeId); } return newService; @@ -93,7 +94,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.composeId, "access"); + await checkServiceAccess(ctx.user.id, input.composeId, "access"); } const compose = await findComposeById(input.composeId); @@ -122,7 +123,7 @@ export const composeRouter = createTRPCRouter({ .input(apiDeleteCompose) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.composeId, "delete"); + await checkServiceAccess(ctx.user.id, input.composeId, "delete"); } const composeResult = await findComposeById(input.composeId); @@ -376,7 +377,7 @@ export const composeRouter = createTRPCRouter({ .input(apiCreateComposeByTemplate) .mutation(async ({ ctx, input }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -390,7 +391,7 @@ export const composeRouter = createTRPCRouter({ const generate = await loadTemplateModule(input.id as TemplatesKeys); - const admin = await findAdminById(ctx.user.adminId); + const admin = await findUserById(ctx.user.ownerId); let serverIp = admin.serverIp || "127.0.0.1"; const project = await findProjectById(input.projectId); @@ -419,7 +420,7 @@ export const composeRouter = createTRPCRouter({ }); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, compose.composeId); + await addNewService(ctx.user.id, compose.composeId); } if (mounts && mounts?.length > 0) { diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 283455fae..9305395d3 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -38,7 +38,7 @@ export const mariadbRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -57,7 +57,7 @@ export const mariadbRouter = createTRPCRouter({ } const newMariadb = await createMariadb(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newMariadb.mariadbId); + await addNewService(ctx.user.id, newMariadb.mariadbId); } await createMount({ @@ -80,7 +80,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiFindOneMariaDB) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.mariadbId, "access"); + await checkServiceAccess(ctx.user.id, input.mariadbId, "access"); } const mariadb = await findMariadbById(input.mariadbId); if (mariadb.project.userId !== ctx.user.ownerId) { @@ -202,7 +202,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiFindOneMariaDB) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.mariadbId, "delete"); + await checkServiceAccess(ctx.user.id, input.mariadbId, "delete"); } const mongo = await findMariadbById(input.mariadbId); diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 94a012724..aed7b4c52 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -37,7 +37,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -56,7 +56,7 @@ export const mongoRouter = createTRPCRouter({ } const newMongo = await createMongo(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newMongo.mongoId); + await addNewService(ctx.user.id, newMongo.mongoId); } await createMount({ @@ -83,7 +83,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiFindOneMongo) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.mongoId, "access"); + await checkServiceAccess(ctx.user.id, input.mongoId, "access"); } const mongo = await findMongoById(input.mongoId); @@ -243,7 +243,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiFindOneMongo) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.mongoId, "delete"); + await checkServiceAccess(ctx.user.id, input.mongoId, "delete"); } const mongo = await findMongoById(input.mongoId); diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index efea72076..e66c30941 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -39,7 +39,7 @@ export const mysqlRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -59,7 +59,7 @@ export const mysqlRouter = createTRPCRouter({ const newMysql = await createMysql(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newMysql.mysqlId); + await addNewService(ctx.user.id, newMysql.mysqlId); } await createMount({ @@ -86,7 +86,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.mysqlId, "access"); + await checkServiceAccess(ctx.user.id, input.mysqlId, "access"); } const mysql = await findMySqlById(input.mysqlId); if (mysql.project.userId !== ctx.user.ownerId) { @@ -241,7 +241,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.mysqlId, "delete"); + await checkServiceAccess(ctx.user.id, input.mysqlId, "delete"); } const mongo = await findMySqlById(input.mysqlId); if (mongo.project.userId !== ctx.user.ownerId) { diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index a7d0de399..0aab4dc65 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -45,7 +45,7 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -64,7 +64,7 @@ export const postgresRouter = createTRPCRouter({ } const newPostgres = await createPostgres(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newPostgres.postgresId); + await addNewService(ctx.user.id, newPostgres.postgresId); } await createMount({ @@ -91,7 +91,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiFindOnePostgres) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.postgresId, "access"); + await checkServiceAccess(ctx.user.id, input.postgresId, "access"); } const postgres = await findPostgresById(input.postgresId); @@ -222,7 +222,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiFindOnePostgres) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.postgresId, "delete"); + await checkServiceAccess(ctx.user.id, input.postgresId, "delete"); } const postgres = await findPostgresById(input.postgresId); diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 1d2d20360..d4a30580a 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -25,9 +25,9 @@ import { checkProjectAccess, createProject, deleteProject, - findAdminById, findProjectById, findUserByAuthId, + findUserById, updateProjectById, } from "@dokploy/server"; @@ -37,10 +37,10 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { try { if (ctx.user.rol === "user") { - await checkProjectAccess(ctx.user.authId, "create"); + await checkProjectAccess(ctx.user.id, "create"); } - const admin = await findAdminById(ctx.user.adminId); + const admin = await findUserById(ctx.user.ownerId); if (admin.serversQuantity === 0 && IS_CLOUD) { throw new TRPCError({ @@ -49,9 +49,9 @@ export const projectRouter = createTRPCRouter({ }); } - const project = await createProject(input, ctx.user.adminId); + const project = await createProject(input, ctx.user.ownerId); if (ctx.user.rol === "user") { - await addNewProject(ctx.user.authId, project.projectId); + await addNewProject(ctx.user.id, project.projectId); } return project; @@ -68,9 +68,9 @@ export const projectRouter = createTRPCRouter({ .input(apiFindOneProject) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - const { accessedServices } = await findUserByAuthId(ctx.user.authId); + const { accessedServices } = await findUserByAuthId(ctx.user.id); - await checkProjectAccess(ctx.user.authId, "access", input.projectId); + await checkProjectAccess(ctx.user.id, "access", input.projectId); const project = await db.query.projects.findFirst({ where: and( @@ -126,7 +126,7 @@ export const projectRouter = createTRPCRouter({ all: protectedProcedure.query(async ({ ctx }) => { // console.log(ctx.user); if (ctx.user.rol === "user") { - const { accessedProjects, accessedServices } = await findUserByAuthId( + const { accessedProjects, accessedServices } = await findUserById( ctx.user.id, ); @@ -204,7 +204,7 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkProjectAccess(ctx.user.authId, "delete"); + await checkProjectAccess(ctx.user.id, "delete"); } const currentProject = await findProjectById(input.projectId); if (currentProject.userId !== ctx.user.ownerId) { diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index af1d4234a..46586e5e2 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -37,7 +37,7 @@ export const redisRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.projectId, "create"); + await checkServiceAccess(ctx.user.id, input.projectId, "create"); } if (IS_CLOUD && !input.serverId) { @@ -56,7 +56,7 @@ export const redisRouter = createTRPCRouter({ } const newRedis = await createRedis(input); if (ctx.user.rol === "user") { - await addNewService(ctx.user.authId, newRedis.redisId); + await addNewService(ctx.user.id, newRedis.redisId); } await createMount({ @@ -76,7 +76,7 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .query(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.redisId, "access"); + await checkServiceAccess(ctx.user.id, input.redisId, "access"); } const redis = await findRedisById(input.redisId); @@ -233,7 +233,7 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "user") { - await checkServiceAccess(ctx.user.authId, input.redisId, "delete"); + await checkServiceAccess(ctx.user.id, input.redisId, "delete"); } const redis = await findRedisById(input.redisId); diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts index 91fa44fd6..540820f25 100644 --- a/apps/dokploy/server/api/routers/stripe.ts +++ b/apps/dokploy/server/api/routers/stripe.ts @@ -120,8 +120,7 @@ export const stripeRouter = createTRPCRouter({ canCreateMoreServers: adminProcedure.query(async ({ ctx }) => { const user = await findUserById(ctx.user.ownerId); - console.log(user); - // const servers = await findServersByUserId(user.id); + const servers = await findServersByUserId(user.id); if (!IS_CLOUD) { return true; diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index e7a263fa0..432753fde 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,7 +1,7 @@ +import { relations } from "drizzle-orm"; import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { nanoid } from "nanoid"; import { users_temp } from "./user"; -import { relations } from "drizzle-orm"; export const account = pgTable("account", { id: text("id") diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 0a3535137..bf47967c3 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -13,6 +13,7 @@ import { z } from "zod"; import { admins } from "./admin"; import { auth } from "./auth"; import { certificateType } from "./shared"; +import { account } from "./account"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -185,10 +186,14 @@ export const users_temp = pgTable("user_temp", { serversQuantity: integer("serversQuantity").notNull().default(0), }); -export const usersRelations = relations(users, ({ one }) => ({ - auth: one(auth, { - fields: [users.authId], - references: [auth.id], +export const usersRelations = relations(users_temp, ({ one }) => ({ + // auth: one(auth, { + // fields: [users.authId], + // references: [auth.id], + // }), + account: one(account, { + fields: [users_temp.id], + references: [account.userId], }), // admin: one(admins, { // fields: [users.adminId], diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index c4b7650ba..ea87e4560 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -51,6 +51,9 @@ export const createInvitation = async ( export const findUserById = async (userId: string) => { const user = await db.query.users_temp.findFirst({ where: eq(users_temp.id, userId), + // with: { + // account: true, + // }, }); if (!user) { throw new TRPCError({ diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 8781f4f19..74cd04197 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -15,6 +15,7 @@ import encode from "hi-base32"; import { TOTP } from "otpauth"; import QRCode from "qrcode"; import { IS_CLOUD } from "../constants"; +import { findUserById } from "./admin"; export type Auth = typeof auth.$inferSelect; @@ -131,14 +132,14 @@ export const updateAuthById = async ( return result[0]; }; -export const generate2FASecret = async (authId: string) => { - const auth = await findAuthById(authId); +export const generate2FASecret = async (userId: string) => { + const user = await findUserById(userId); const base32_secret = generateBase32Secret(); const totp = new TOTP({ issuer: "Dokploy", - label: `${auth?.email}`, + label: `${user?.email}`, algorithm: "SHA1", digits: 6, secret: base32_secret, diff --git a/packages/server/src/services/server.ts b/packages/server/src/services/server.ts index 6aaa8becc..7702c90d6 100644 --- a/packages/server/src/services/server.ts +++ b/packages/server/src/services/server.ts @@ -14,6 +14,7 @@ export const createServer = async ( .values({ ...input, userId: userId, + createdAt: new Date().toISOString(), }) .returning() .then((value) => value[0]); diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 5a9898cde..c32ceef2c 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -2,21 +2,22 @@ import { db } from "@dokploy/server/db"; import type { users_temp } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; +import { findUserById } from "./admin"; export type User = typeof users_temp.$inferSelect; -export const findUserById = async (userId: string) => { - const userR = await db.query.user.findFirst({ - where: eq(user.userId, userId), - }); - if (!userR) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - return user; -}; +// export const findUserById = async (userId: string) => { +// // const userR = await db.query.user.findFirst({ +// // where: eq(user.userId, userId), +// // }); +// // if (!userR) { +// // throw new TRPCError({ +// // code: "NOT_FOUND", +// // message: "User not found", +// // }); +// // } +// // return user; +// }; export const findUserByAuthId = async (authId: string) => { const userR = await db.query.user.findFirst({ @@ -46,33 +47,32 @@ export const findUsers = async (adminId: string) => { return currentUsers; }; -export const addNewProject = async (authId: string, projectId: string) => { - const userR = await findUserByAuthId(authId); +export const addNewProject = async (userId: string, projectId: string) => { + const userR = await findUserById(userId); - await db - .update(user) - .set({ - accessedProjects: [...userR.accessedProjects, projectId], - }) - .where(eq(user.authId, authId)); + // await db + // .update(user) + // .set({ + // accessedProjects: [...userR.accessedProjects, projectId], + // }) + // .where(eq(user.authId, authId)); }; -export const addNewService = async (authId: string, serviceId: string) => { - const userR = await findUserByAuthId(authId); - await db - .update(user) - .set({ - accessedServices: [...userR.accessedServices, serviceId], - }) - .where(eq(user.authId, authId)); +export const addNewService = async (userId: string, serviceId: string) => { + const userR = await findUserById(userId); + // await db + // .update(user) + // .set({ + // accessedServices: [...userR.accessedServices, serviceId], + // }) + // .where(eq(user.userId, userId)); }; export const canPerformCreationService = async ( userId: string, projectId: string, ) => { - const { accessedProjects, canCreateServices } = - await findUserByAuthId(userId); + const { accessedProjects, canCreateServices } = await findUserById(userId); const haveAccessToProject = accessedProjects.includes(projectId); if (canCreateServices && haveAccessToProject) { @@ -86,7 +86,7 @@ export const canPerformAccessService = async ( userId: string, serviceId: string, ) => { - const { accessedServices } = await findUserByAuthId(userId); + const { accessedServices } = await findUserById(userId); const haveAccessToService = accessedServices.includes(serviceId); if (haveAccessToService) { @@ -97,11 +97,10 @@ export const canPerformAccessService = async ( }; export const canPeformDeleteService = async ( - authId: string, + userId: string, serviceId: string, ) => { - const { accessedServices, canDeleteServices } = - await findUserByAuthId(authId); + const { accessedServices, canDeleteServices } = await findUserById(userId); const haveAccessToService = accessedServices.includes(serviceId); if (canDeleteServices && haveAccessToService) { @@ -111,8 +110,8 @@ export const canPeformDeleteService = async ( return false; }; -export const canPerformCreationProject = async (authId: string) => { - const { canCreateProjects } = await findUserByAuthId(authId); +export const canPerformCreationProject = async (userId: string) => { + const { canCreateProjects } = await findUserById(userId); if (canCreateProjects) { return true; @@ -121,8 +120,8 @@ export const canPerformCreationProject = async (authId: string) => { return false; }; -export const canPerformDeleteProject = async (authId: string) => { - const { canDeleteProjects } = await findUserByAuthId(authId); +export const canPerformDeleteProject = async (userId: string) => { + const { canDeleteProjects } = await findUserById(userId); if (canDeleteProjects) { return true; @@ -132,10 +131,10 @@ export const canPerformDeleteProject = async (authId: string) => { }; export const canPerformAccessProject = async ( - authId: string, + userId: string, projectId: string, ) => { - const { accessedProjects } = await findUserByAuthId(authId); + const { accessedProjects } = await findUserById(userId); const haveAccessToProject = accessedProjects.includes(projectId); @@ -145,26 +144,26 @@ export const canPerformAccessProject = async ( return false; }; -export const canAccessToTraefikFiles = async (authId: string) => { - const { canAccessToTraefikFiles } = await findUserByAuthId(authId); +export const canAccessToTraefikFiles = async (userId: string) => { + const { canAccessToTraefikFiles } = await findUserById(userId); return canAccessToTraefikFiles; }; export const checkServiceAccess = async ( - authId: string, + userId: string, serviceId: string, action = "access" as "access" | "create" | "delete", ) => { let hasPermission = false; switch (action) { case "create": - hasPermission = await canPerformCreationService(authId, serviceId); + hasPermission = await canPerformCreationService(userId, serviceId); break; case "access": - hasPermission = await canPerformAccessService(authId, serviceId); + hasPermission = await canPerformAccessService(userId, serviceId); break; case "delete": - hasPermission = await canPeformDeleteService(authId, serviceId); + hasPermission = await canPeformDeleteService(userId, serviceId); break; default: hasPermission = false; diff --git a/packages/server/src/setup/monitoring-setup.ts b/packages/server/src/setup/monitoring-setup.ts index f72b22447..ea6c768ba 100644 --- a/packages/server/src/setup/monitoring-setup.ts +++ b/packages/server/src/setup/monitoring-setup.ts @@ -1,7 +1,7 @@ import { findServerById } from "@dokploy/server/services/server"; import type { ContainerCreateOptions } from "dockerode"; import { IS_CLOUD } from "../constants"; -import { findAdminById } from "../services/admin"; +import { findAdminById, findUserById } from "../services/admin"; import { getDokployImageTag } from "../services/settings"; import { pullImage, pullRemoteImage } from "../utils/docker/utils"; import { execAsync, execAsyncRemote } from "../utils/process/execAsync"; @@ -80,8 +80,8 @@ export const setupMonitoring = async (serverId: string) => { } }; -export const setupWebMonitoring = async (adminId: string) => { - const admin = await findAdminById(adminId); +export const setupWebMonitoring = async (userId: string) => { + const user = await findUserById(userId); const containerName = "dokploy-monitoring"; let imageName = "dokploy/monitoring:latest"; @@ -96,7 +96,7 @@ export const setupWebMonitoring = async (adminId: string) => { const settings: ContainerCreateOptions = { name: containerName, - Env: [`METRICS_CONFIG=${JSON.stringify(admin?.metricsConfig)}`], + Env: [`METRICS_CONFIG=${JSON.stringify(user?.metricsConfig)}`], Image: imageName, HostConfig: { // Memory: 100 * 1024 * 1024, // 100MB en bytes @@ -104,9 +104,9 @@ export const setupWebMonitoring = async (adminId: string) => { // CapAdd: ["NET_ADMIN", "SYS_ADMIN"], // Privileged: true, PortBindings: { - [`${admin.metricsConfig.server.port}/tcp`]: [ + [`${user?.metricsConfig?.server?.port}/tcp`]: [ { - HostPort: admin.metricsConfig.server.port.toString(), + HostPort: user?.metricsConfig?.server?.port.toString(), }, ], }, @@ -120,7 +120,7 @@ export const setupWebMonitoring = async (adminId: string) => { // NetworkMode: "host", }, ExposedPorts: { - [`${admin.metricsConfig.server.port}/tcp`]: {}, + [`${user?.metricsConfig?.server?.port}/tcp`]: {}, }, }; const docker = await getRemoteDocker(); From 55abac3f2f0a3011b5c624ff7293f20389df63a3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 14 Feb 2025 02:52:37 -0600 Subject: [PATCH 028/126] refactor: migrate endpoints --- apps/dokploy/pages/api/stripe/webhook.ts | 396 +++++++++--------- packages/server/src/db/schema/user.ts | 2 +- packages/server/src/services/application.ts | 22 +- packages/server/src/services/certificate.ts | 4 +- packages/server/src/services/compose.ts | 18 +- packages/server/src/services/destination.ts | 10 +- packages/server/src/services/domain.ts | 6 +- packages/server/src/services/gitlab.ts | 4 +- .../server/src/services/preview-deployment.ts | 8 +- packages/server/src/services/project.ts | 4 +- packages/server/src/utils/backups/mariadb.ts | 4 +- packages/server/src/utils/backups/mongo.ts | 4 +- packages/server/src/utils/backups/mysql.ts | 4 +- packages/server/src/utils/backups/postgres.ts | 4 +- .../src/utils/notifications/build-error.ts | 6 +- .../src/utils/notifications/build-success.ts | 6 +- .../utils/notifications/database-backup.ts | 6 +- .../src/utils/notifications/docker-cleanup.ts | 4 +- .../utils/notifications/server-threshold.ts | 4 +- 19 files changed, 260 insertions(+), 256 deletions(-) diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index 7701ebd98..e8416c5d0 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -1,7 +1,7 @@ import { buffer } from "node:stream/consumers"; import { db } from "@/server/db"; -import { admins, server } from "@/server/db/schema"; -import { findAdminById } from "@dokploy/server"; +import { admins, server, users_temp } from "@/server/db/schema"; +import { findAdminById, findUserById } from "@dokploy/server"; import { asc, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import Stripe from "stripe"; @@ -18,242 +18,246 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - // if (!endpointSecret) { - // return res.status(400).send("Webhook Error: Missing Stripe Secret Key"); - // } - // const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { - // apiVersion: "2024-09-30.acacia", - // maxNetworkRetries: 3, - // }); + if (!endpointSecret) { + return res.status(400).send("Webhook Error: Missing Stripe Secret Key"); + } + const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { + apiVersion: "2024-09-30.acacia", + maxNetworkRetries: 3, + }); - // const buf = await buffer(req); - // const sig = req.headers["stripe-signature"] as string; + const buf = await buffer(req); + const sig = req.headers["stripe-signature"] as string; - // let event: Stripe.Event; + let event: Stripe.Event; - // try { - // event = stripe.webhooks.constructEvent(buf, sig, endpointSecret); - // } catch (err) { - // console.error( - // "Webhook signature verification failed.", - // err instanceof Error ? err.message : err, - // ); - // return res.status(400).send("Webhook Error: "); - // } + try { + event = stripe.webhooks.constructEvent(buf, sig, endpointSecret); + } catch (err) { + console.error( + "Webhook signature verification failed.", + err instanceof Error ? err.message : err, + ); + return res.status(400).send("Webhook Error: "); + } - // const webhooksAllowed = [ - // "customer.subscription.created", - // "customer.subscription.deleted", - // "customer.subscription.updated", - // "invoice.payment_succeeded", - // "invoice.payment_failed", - // "customer.deleted", - // "checkout.session.completed", - // ]; + const webhooksAllowed = [ + "customer.subscription.created", + "customer.subscription.deleted", + "customer.subscription.updated", + "invoice.payment_succeeded", + "invoice.payment_failed", + "customer.deleted", + "checkout.session.completed", + ]; - // if (!webhooksAllowed.includes(event.type)) { - // return res.status(400).send("Webhook Error: Invalid Event Type"); - // } + if (!webhooksAllowed.includes(event.type)) { + return res.status(400).send("Webhook Error: Invalid Event Type"); + } - // switch (event.type) { - // case "checkout.session.completed": { - // const session = event.data.object as Stripe.Checkout.Session; - // const adminId = session?.metadata?.adminId as string; + switch (event.type) { + case "checkout.session.completed": { + const session = event.data.object as Stripe.Checkout.Session; + const adminId = session?.metadata?.adminId as string; - // const subscription = await stripe.subscriptions.retrieve( - // session.subscription as string, - // ); - // await db - // .update(admins) - // .set({ - // stripeCustomerId: session.customer as string, - // stripeSubscriptionId: session.subscription as string, - // serversQuantity: subscription?.items?.data?.[0]?.quantity ?? 0, - // }) - // .where(eq(admins.adminId, adminId)) - // .returning(); + const subscription = await stripe.subscriptions.retrieve( + session.subscription as string, + ); + await db + .update(users_temp) + .set({ + stripeCustomerId: session.customer as string, + stripeSubscriptionId: session.subscription as string, + serversQuantity: subscription?.items?.data?.[0]?.quantity ?? 0, + }) + .where(eq(users_temp.id, adminId)) + .returning(); - // const admin = await findAdminById(adminId); - // if (!admin) { - // return res.status(400).send("Webhook Error: Admin not found"); - // } - // const newServersQuantity = admin.serversQuantity; - // await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); - // break; - // } - // case "customer.subscription.created": { - // const newSubscription = event.data.object as Stripe.Subscription; + const admin = await findUserById(adminId); + if (!admin) { + return res.status(400).send("Webhook Error: Admin not found"); + } + const newServersQuantity = admin.serversQuantity; + await updateServersBasedOnQuantity(admin.id, newServersQuantity); + break; + } + case "customer.subscription.created": { + const newSubscription = event.data.object as Stripe.Subscription; - // await db - // .update(admins) - // .set({ - // stripeSubscriptionId: newSubscription.id, - // stripeCustomerId: newSubscription.customer as string, - // }) - // .where(eq(admins.stripeCustomerId, newSubscription.customer as string)) - // .returning(); + await db + .update(users_temp) + .set({ + stripeSubscriptionId: newSubscription.id, + stripeCustomerId: newSubscription.customer as string, + }) + .where( + eq(users_temp.stripeCustomerId, newSubscription.customer as string), + ) + .returning(); - // break; - // } + break; + } - // case "customer.subscription.deleted": { - // const newSubscription = event.data.object as Stripe.Subscription; + case "customer.subscription.deleted": { + const newSubscription = event.data.object as Stripe.Subscription; - // await db - // .update(admins) - // .set({ - // stripeSubscriptionId: null, - // serversQuantity: 0, - // }) - // .where(eq(admins.stripeCustomerId, newSubscription.customer as string)); + await db + .update(users_temp) + .set({ + stripeSubscriptionId: null, + serversQuantity: 0, + }) + .where( + eq(users_temp.stripeCustomerId, newSubscription.customer as string), + ); - // const admin = await findAdminByStripeCustomerId( - // newSubscription.customer as string, - // ); + const admin = await findUserByStripeCustomerId( + newSubscription.customer as string, + ); - // if (!admin) { - // return res.status(400).send("Webhook Error: Admin not found"); - // } + if (!admin) { + return res.status(400).send("Webhook Error: Admin not found"); + } - // await disableServers(admin.adminId); - // break; - // } - // case "customer.subscription.updated": { - // const newSubscription = event.data.object as Stripe.Subscription; + await disableServers(admin.id); + break; + } + case "customer.subscription.updated": { + const newSubscription = event.data.object as Stripe.Subscription; - // const admin = await findAdminByStripeCustomerId( - // newSubscription.customer as string, - // ); + const admin = await findUserByStripeCustomerId( + newSubscription.customer as string, + ); - // if (!admin) { - // return res.status(400).send("Webhook Error: Admin not found"); - // } + if (!admin) { + return res.status(400).send("Webhook Error: Admin not found"); + } - // if (newSubscription.status === "active") { - // await db - // .update(admins) - // .set({ - // serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, - // }) - // .where( - // eq(admins.stripeCustomerId, newSubscription.customer as string), - // ); + if (newSubscription.status === "active") { + await db + .update(users_temp) + .set({ + serversQuantity: newSubscription?.items?.data?.[0]?.quantity ?? 0, + }) + .where( + eq(users_temp.stripeCustomerId, newSubscription.customer as string), + ); - // const newServersQuantity = admin.serversQuantity; - // await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); - // } else { - // await disableServers(admin.adminId); - // await db - // .update(admins) - // .set({ serversQuantity: 0 }) - // .where( - // eq(admins.stripeCustomerId, newSubscription.customer as string), - // ); - // } + const newServersQuantity = admin.serversQuantity; + await updateServersBasedOnQuantity(admin.id, newServersQuantity); + } else { + await disableServers(admin.id); + await db + .update(users_temp) + .set({ serversQuantity: 0 }) + .where( + eq(users_temp.stripeCustomerId, newSubscription.customer as string), + ); + } - // break; - // } - // case "invoice.payment_succeeded": { - // const newInvoice = event.data.object as Stripe.Invoice; + break; + } + case "invoice.payment_succeeded": { + const newInvoice = event.data.object as Stripe.Invoice; - // const suscription = await stripe.subscriptions.retrieve( - // newInvoice.subscription as string, - // ); + const suscription = await stripe.subscriptions.retrieve( + newInvoice.subscription as string, + ); - // if (suscription.status !== "active") { - // console.log( - // `Skipping invoice.payment_succeeded for subscription ${suscription.id} with status ${suscription.status}`, - // ); - // break; - // } + if (suscription.status !== "active") { + console.log( + `Skipping invoice.payment_succeeded for subscription ${suscription.id} with status ${suscription.status}`, + ); + break; + } - // await db - // .update(admins) - // .set({ - // serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, - // }) - // .where(eq(admins.stripeCustomerId, suscription.customer as string)); + await db + .update(admins) + .set({ + serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, + }) + .where(eq(admins.stripeCustomerId, suscription.customer as string)); - // const admin = await findAdminByStripeCustomerId( - // suscription.customer as string, - // ); + const admin = await findUserByStripeCustomerId( + suscription.customer as string, + ); - // if (!admin) { - // return res.status(400).send("Webhook Error: Admin not found"); - // } - // const newServersQuantity = admin.serversQuantity; - // await updateServersBasedOnQuantity(admin.adminId, newServersQuantity); - // break; - // } - // case "invoice.payment_failed": { - // const newInvoice = event.data.object as Stripe.Invoice; + if (!admin) { + return res.status(400).send("Webhook Error: Admin not found"); + } + const newServersQuantity = admin.serversQuantity; + await updateServersBasedOnQuantity(admin.id, newServersQuantity); + break; + } + case "invoice.payment_failed": { + const newInvoice = event.data.object as Stripe.Invoice; - // const subscription = await stripe.subscriptions.retrieve( - // newInvoice.subscription as string, - // ); + const subscription = await stripe.subscriptions.retrieve( + newInvoice.subscription as string, + ); - // if (subscription.status !== "active") { - // const admin = await findAdminByStripeCustomerId( - // newInvoice.customer as string, - // ); + if (subscription.status !== "active") { + const admin = await findUserByStripeCustomerId( + newInvoice.customer as string, + ); - // if (!admin) { - // return res.status(400).send("Webhook Error: Admin not found"); - // } - // await db - // .update(admins) - // .set({ - // serversQuantity: 0, - // }) - // .where(eq(admins.stripeCustomerId, newInvoice.customer as string)); + if (!admin) { + return res.status(400).send("Webhook Error: Admin not found"); + } + await db + .update(admins) + .set({ + serversQuantity: 0, + }) + .where(eq(admins.stripeCustomerId, newInvoice.customer as string)); - // await disableServers(admin.adminId); - // } + await disableServers(admin.id); + } - // break; - // } + break; + } - // case "customer.deleted": { - // const customer = event.data.object as Stripe.Customer; + case "customer.deleted": { + const customer = event.data.object as Stripe.Customer; - // const admin = await findAdminByStripeCustomerId(customer.id); - // if (!admin) { - // return res.status(400).send("Webhook Error: Admin not found"); - // } + const admin = await findUserByStripeCustomerId(customer.id); + if (!admin) { + return res.status(400).send("Webhook Error: Admin not found"); + } - // await disableServers(admin.adminId); - // await db - // .update(admins) - // .set({ - // stripeCustomerId: null, - // stripeSubscriptionId: null, - // serversQuantity: 0, - // }) - // .where(eq(admins.stripeCustomerId, customer.id)); + await disableServers(admin.id); + await db + .update(users_temp) + .set({ + stripeCustomerId: null, + stripeSubscriptionId: null, + serversQuantity: 0, + }) + .where(eq(users_temp.stripeCustomerId, customer.id)); - // break; - // } - // default: - // console.log(`Unhandled event type: ${event.type}`); - // } + break; + } + default: + console.log(`Unhandled event type: ${event.type}`); + } return res.status(200).json({ received: true }); } -const disableServers = async (adminId: string) => { +const disableServers = async (userId: string) => { await db .update(server) .set({ serverStatus: "inactive", }) - .where(eq(server.adminId, adminId)); + .where(eq(server.userId, userId)); }; -const findAdminByStripeCustomerId = async (stripeCustomerId: string) => { - const admin = db.query.admins.findFirst({ - where: eq(admins.stripeCustomerId, stripeCustomerId), +const findUserByStripeCustomerId = async (stripeCustomerId: string) => { + const user = db.query.users_temp.findFirst({ + where: eq(users_temp.stripeCustomerId, stripeCustomerId), }); - return admin; + return user; }; const activateServer = async (serverId: string) => { @@ -270,19 +274,19 @@ const deactivateServer = async (serverId: string) => { .where(eq(server.serverId, serverId)); }; -export const findServersByAdminIdSorted = async (adminId: string) => { +export const findServersByUserIdSorted = async (userId: string) => { const servers = await db.query.server.findMany({ - where: eq(server.adminId, adminId), + where: eq(server.userId, userId), orderBy: asc(server.createdAt), }); return servers; }; export const updateServersBasedOnQuantity = async ( - adminId: string, + userId: string, newServersQuantity: number, ) => { - const servers = await findServersByAdminIdSorted(adminId); + const servers = await findServersByUserIdSorted(userId); if (servers.length > newServersQuantity) { for (const [index, server] of servers.entries()) { diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index bf47967c3..fba21e234 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -10,10 +10,10 @@ import { import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { account } from "./account"; import { admins } from "./admin"; import { auth } from "./auth"; import { certificateType } from "./shared"; -import { account } from "./account"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index c102e8ed7..b60665304 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -40,7 +40,7 @@ import { createTraefikConfig } from "@dokploy/server/utils/traefik/application"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; -import { findAdminById, getDokployUrl } from "./admin"; +import { findAdminById, findUserById, getDokployUrl } from "./admin"; import { createDeployment, createDeploymentPreview, @@ -185,7 +185,7 @@ export const deployApplication = async ({ }); try { - const admin = await findAdminById(application.project.adminId); + const admin = await findUserById(application.project.userId); if (admin.cleanupCacheApplications) { await cleanupFullDocker(application?.serverId); @@ -220,7 +220,7 @@ export const deployApplication = async ({ applicationName: application.name, applicationType: "application", buildLink, - adminId: application.project.adminId, + userId: application.project.userId, domains: application.domains, }); } catch (error) { @@ -233,7 +233,7 @@ export const deployApplication = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - adminId: application.project.adminId, + userId: application.project.userId, }); throw error; @@ -260,7 +260,7 @@ export const rebuildApplication = async ({ }); try { - const admin = await findAdminById(application.project.adminId); + const admin = await findUserById(application.project.userId); if (admin.cleanupCacheApplications) { await cleanupFullDocker(application?.serverId); @@ -309,7 +309,7 @@ export const deployRemoteApplication = async ({ try { if (application.serverId) { - const admin = await findAdminById(application.project.adminId); + const admin = await findUserById(application.project.userId); if (admin.cleanupCacheApplications) { await cleanupFullDocker(application?.serverId); @@ -352,7 +352,7 @@ export const deployRemoteApplication = async ({ applicationName: application.name, applicationType: "application", buildLink, - adminId: application.project.adminId, + userId: application.project.userId, domains: application.domains, }); } catch (error) { @@ -376,7 +376,7 @@ export const deployRemoteApplication = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - adminId: application.project.adminId, + userId: application.project.userId, }); throw error; @@ -454,7 +454,7 @@ export const deployPreviewApplication = async ({ application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`; application.buildArgs = application.previewBuildArgs; - const admin = await findAdminById(application.project.adminId); + const admin = await findUserById(application.project.userId); if (admin.cleanupCacheOnPreviews) { await cleanupFullDocker(application?.serverId); @@ -568,7 +568,7 @@ export const deployRemotePreviewApplication = async ({ application.buildArgs = application.previewBuildArgs; if (application.serverId) { - const admin = await findAdminById(application.project.adminId); + const admin = await findUserById(application.project.userId); if (admin.cleanupCacheOnPreviews) { await cleanupFullDocker(application?.serverId); @@ -637,7 +637,7 @@ export const rebuildRemoteApplication = async ({ try { if (application.serverId) { - const admin = await findAdminById(application.project.adminId); + const admin = await findUserById(application.project.userId); if (admin.cleanupCacheApplications) { await cleanupFullDocker(application?.serverId); diff --git a/packages/server/src/services/certificate.ts b/packages/server/src/services/certificate.ts index 231778621..dd83e61df 100644 --- a/packages/server/src/services/certificate.ts +++ b/packages/server/src/services/certificate.ts @@ -33,13 +33,13 @@ export const findCertificateById = async (certificateId: string) => { export const createCertificate = async ( certificateData: z.infer, - adminId: string, + userId: string, ) => { const certificate = await db .insert(certificates) .values({ ...certificateData, - adminId: adminId, + userId: userId, }) .returning(); diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 39bf423fa..a4126a0d0 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -44,7 +44,7 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; -import { findAdminById, getDokployUrl } from "./admin"; +import { findAdminById, findUserById, getDokployUrl } from "./admin"; import { createDeploymentCompose, updateDeploymentStatus } from "./deployment"; import { validUniqueServerAppName } from "./project"; import { cleanupFullDocker } from "./settings"; @@ -217,7 +217,7 @@ export const deployCompose = async ({ }); try { - const admin = await findAdminById(compose.project.adminId); + const admin = await findUserById(compose.project.userId); if (admin.cleanupCacheOnCompose) { await cleanupFullDocker(compose?.serverId); } @@ -247,7 +247,7 @@ export const deployCompose = async ({ applicationName: compose.name, applicationType: "compose", buildLink, - adminId: compose.project.adminId, + userId: compose.project.userId, domains: compose.domains, }); } catch (error) { @@ -262,7 +262,7 @@ export const deployCompose = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - adminId: compose.project.adminId, + userId: compose.project.userId, }); throw error; } @@ -286,7 +286,7 @@ export const rebuildCompose = async ({ }); try { - const admin = await findAdminById(compose.project.adminId); + const admin = await findUserById(compose.project.userId); if (admin.cleanupCacheOnCompose) { await cleanupFullDocker(compose?.serverId); } @@ -332,7 +332,7 @@ export const deployRemoteCompose = async ({ }); try { if (compose.serverId) { - const admin = await findAdminById(compose.project.adminId); + const admin = await findUserById(compose.project.userId); if (admin.cleanupCacheOnCompose) { await cleanupFullDocker(compose?.serverId); } @@ -381,7 +381,7 @@ export const deployRemoteCompose = async ({ applicationName: compose.name, applicationType: "compose", buildLink, - adminId: compose.project.adminId, + userId: compose.project.userId, domains: compose.domains, }); } catch (error) { @@ -406,7 +406,7 @@ export const deployRemoteCompose = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - adminId: compose.project.adminId, + userId: compose.project.userId, }); throw error; } @@ -430,7 +430,7 @@ export const rebuildRemoteCompose = async ({ }); try { - const admin = await findAdminById(compose.project.adminId); + const admin = await findUserById(compose.project.userId); if (admin.cleanupCacheOnCompose) { await cleanupFullDocker(compose?.serverId); } diff --git a/packages/server/src/services/destination.ts b/packages/server/src/services/destination.ts index 892c93541..add0a3dff 100644 --- a/packages/server/src/services/destination.ts +++ b/packages/server/src/services/destination.ts @@ -10,13 +10,13 @@ export type Destination = typeof destinations.$inferSelect; export const createDestintation = async ( input: typeof apiCreateDestination._type, - adminId: string, + userId: string, ) => { const newDestination = await db .insert(destinations) .values({ ...input, - adminId: adminId, + userId: userId, }) .returning() .then((value) => value[0]); @@ -46,14 +46,14 @@ export const findDestinationById = async (destinationId: string) => { export const removeDestinationById = async ( destinationId: string, - adminId: string, + userId: string, ) => { const result = await db .delete(destinations) .where( and( eq(destinations.destinationId, destinationId), - eq(destinations.adminId, adminId), + eq(destinations.userId, userId), ), ) .returning(); @@ -73,7 +73,7 @@ export const updateDestinationById = async ( .where( and( eq(destinations.destinationId, destinationId), - eq(destinations.adminId, destinationData.adminId || ""), + eq(destinations.userId, destinationData.userId || ""), ), ) .returning(); diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index b99c4869d..99dcde559 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -4,7 +4,7 @@ import { manageDomain } from "@dokploy/server/utils/traefik/domain"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { type apiCreateDomain, domains } from "../db/schema"; -import { findAdmin, findAdminById } from "./admin"; +import { findAdmin, findAdminById, findUserById } from "./admin"; import { findApplicationById } from "./application"; import { findServerById } from "./server"; @@ -40,7 +40,7 @@ export const createDomain = async (input: typeof apiCreateDomain._type) => { export const generateTraefikMeDomain = async ( appName: string, - adminId: string, + userId: string, serverId?: string, ) => { if (serverId) { @@ -57,7 +57,7 @@ export const generateTraefikMeDomain = async ( projectName: appName, }); } - const admin = await findAdminById(adminId); + const admin = await findUserById(userId); return generateRandomDomain({ serverIp: admin?.serverIp || "", projectName: appName, diff --git a/packages/server/src/services/gitlab.ts b/packages/server/src/services/gitlab.ts index 8e1362c94..c581c96ce 100644 --- a/packages/server/src/services/gitlab.ts +++ b/packages/server/src/services/gitlab.ts @@ -13,14 +13,14 @@ export type Gitlab = typeof gitlab.$inferSelect; export const createGitlab = async ( input: typeof apiCreateGitlab._type, - adminId: string, + userId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "gitlab", - adminId: adminId, + userId: userId, name: input.name, }) .returning() diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index ab38c17ca..69279b027 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -13,7 +13,7 @@ import { removeDirectoryCode } from "../utils/filesystem/directory"; import { authGithub } from "../utils/providers/github"; import { removeTraefikConfig } from "../utils/traefik/application"; import { manageDomain } from "../utils/traefik/domain"; -import { findAdminById } from "./admin"; +import { findAdminById, findUserById } from "./admin"; import { findApplicationById } from "./application"; import { removeDeployments, @@ -158,7 +158,7 @@ export const createPreviewDeployment = async ( application.previewWildcard || "*.traefik.me", appName, application.server?.ipAddress || "", - application.project.adminId, + application.project.userId, ); const octokit = authGithub(application?.github as Github); @@ -250,7 +250,7 @@ const generateWildcardDomain = async ( baseDomain: string, appName: string, serverIp: string, - adminId: string, + userId: string, ): Promise => { if (!baseDomain.startsWith("*.")) { throw new Error('The base domain must start with "*."'); @@ -268,7 +268,7 @@ const generateWildcardDomain = async ( } if (!ip) { - const admin = await findAdminById(adminId); + const admin = await findUserById(userId); ip = admin?.serverIp || ""; } diff --git a/packages/server/src/services/project.ts b/packages/server/src/services/project.ts index adaa07ea0..8b80738fd 100644 --- a/packages/server/src/services/project.ts +++ b/packages/server/src/services/project.ts @@ -16,13 +16,13 @@ export type Project = typeof projects.$inferSelect; export const createProject = async ( input: typeof apiCreateProject._type, - adminId: string, + userId: string, ) => { const newProject = await db .insert(projects) .values({ ...input, - adminId: adminId, + userId: userId, }) .returning() .then((value) => value[0]); diff --git a/packages/server/src/utils/backups/mariadb.ts b/packages/server/src/utils/backups/mariadb.ts index 79cba9c57..7ffa16e11 100644 --- a/packages/server/src/utils/backups/mariadb.ts +++ b/packages/server/src/utils/backups/mariadb.ts @@ -49,7 +49,7 @@ export const runMariadbBackup = async ( projectName: project.name, databaseType: "mariadb", type: "success", - adminId: project.adminId, + userId: project.userId, }); } catch (error) { console.log(error); @@ -60,7 +60,7 @@ export const runMariadbBackup = async ( type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - adminId: project.adminId, + userId: project.userId, }); throw error; } diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts index ddd1b8896..d6860a010 100644 --- a/packages/server/src/utils/backups/mongo.ts +++ b/packages/server/src/utils/backups/mongo.ts @@ -46,7 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { projectName: project.name, databaseType: "mongodb", type: "success", - adminId: project.adminId, + userId: project.userId, }); } catch (error) { console.log(error); @@ -57,7 +57,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - adminId: project.adminId, + userId: project.userId, }); throw error; } diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts index b505204c2..a73179417 100644 --- a/packages/server/src/utils/backups/mysql.ts +++ b/packages/server/src/utils/backups/mysql.ts @@ -46,7 +46,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => { projectName: project.name, databaseType: "mysql", type: "success", - adminId: project.adminId, + userId: project.userId, }); } catch (error) { console.log(error); @@ -57,7 +57,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => { type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - adminId: project.adminId, + userId: project.userId, }); throw error; } diff --git a/packages/server/src/utils/backups/postgres.ts b/packages/server/src/utils/backups/postgres.ts index e9609fc89..33c37b862 100644 --- a/packages/server/src/utils/backups/postgres.ts +++ b/packages/server/src/utils/backups/postgres.ts @@ -49,7 +49,7 @@ export const runPostgresBackup = async ( projectName: project.name, databaseType: "postgres", type: "success", - adminId: project.adminId, + userId: project.userId, }); } catch (error) { await sendDatabaseBackupNotifications({ @@ -59,7 +59,7 @@ export const runPostgresBackup = async ( type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - adminId: project.adminId, + userId: project.userId, }); throw error; diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 95936652c..4ed227e1e 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -18,7 +18,7 @@ interface Props { applicationType: string; errorMessage: string; buildLink: string; - adminId: string; + userId: string; } export const sendBuildErrorNotifications = async ({ @@ -27,14 +27,14 @@ export const sendBuildErrorNotifications = async ({ applicationType, errorMessage, buildLink, - adminId, + userId, }: Props) => { const date = new Date(); const unixDate = ~~(Number(date) / 1000); const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.appBuildError, true), - eq(notifications.adminId, adminId), + eq(notifications.userId, userId), ), with: { email: true, diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index 960f7a6a4..dfa4824f6 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -18,7 +18,7 @@ interface Props { applicationName: string; applicationType: string; buildLink: string; - adminId: string; + userId: string; domains: Domain[]; } @@ -27,7 +27,7 @@ export const sendBuildSuccessNotifications = async ({ applicationName, applicationType, buildLink, - adminId, + userId, domains, }: Props) => { const date = new Date(); @@ -35,7 +35,7 @@ export const sendBuildSuccessNotifications = async ({ const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.appDeploy, true), - eq(notifications.adminId, adminId), + eq(notifications.userId, userId), ), with: { email: true, diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 0b1d61f7e..3ce36aa9f 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -19,13 +19,13 @@ export const sendDatabaseBackupNotifications = async ({ databaseType, type, errorMessage, - adminId, + userId, }: { projectName: string; applicationName: string; databaseType: "postgres" | "mysql" | "mongodb" | "mariadb"; type: "error" | "success"; - adminId: string; + userId: string; errorMessage?: string; }) => { const date = new Date(); @@ -33,7 +33,7 @@ export const sendDatabaseBackupNotifications = async ({ const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.databaseBackup, true), - eq(notifications.adminId, adminId), + eq(notifications.userId, userId), ), with: { email: true, diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index b60e3b0ac..0acf1b6c4 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -13,7 +13,7 @@ import { } from "./utils"; export const sendDockerCleanupNotifications = async ( - adminId: string, + userId: string, message = "Docker cleanup for dokploy", ) => { const date = new Date(); @@ -21,7 +21,7 @@ export const sendDockerCleanupNotifications = async ( const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.dockerCleanup, true), - eq(notifications.adminId, adminId), + eq(notifications.userId, userId), ), with: { email: true, diff --git a/packages/server/src/utils/notifications/server-threshold.ts b/packages/server/src/utils/notifications/server-threshold.ts index 32ff9b55c..c606f0ef3 100644 --- a/packages/server/src/utils/notifications/server-threshold.ts +++ b/packages/server/src/utils/notifications/server-threshold.ts @@ -18,7 +18,7 @@ interface ServerThresholdPayload { } export const sendServerThresholdNotifications = async ( - adminId: string, + userId: string, payload: ServerThresholdPayload, ) => { const date = new Date(payload.Timestamp); @@ -27,7 +27,7 @@ export const sendServerThresholdNotifications = async ( const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.serverThreshold, true), - eq(notifications.adminId, adminId), + eq(notifications.userId, userId), ), with: { email: true, From 796e50ed5f43f9819c77d13cd494ee81581e8f62 Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sat, 15 Feb 2025 02:22:17 -0500 Subject: [PATCH 029/126] feat(template): add outline --- apps/dokploy/public/templates/outline.svg | 1 + .../templates/outline/docker-compose.yml | 57 ++++++++++++ apps/dokploy/templates/outline/index.ts | 90 +++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 ++++ 4 files changed, 163 insertions(+) create mode 100644 apps/dokploy/public/templates/outline.svg create mode 100644 apps/dokploy/templates/outline/docker-compose.yml create mode 100644 apps/dokploy/templates/outline/index.ts diff --git a/apps/dokploy/public/templates/outline.svg b/apps/dokploy/public/templates/outline.svg new file mode 100644 index 000000000..5dfa63d02 --- /dev/null +++ b/apps/dokploy/public/templates/outline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/dokploy/templates/outline/docker-compose.yml b/apps/dokploy/templates/outline/docker-compose.yml new file mode 100644 index 000000000..90ec6d975 --- /dev/null +++ b/apps/dokploy/templates/outline/docker-compose.yml @@ -0,0 +1,57 @@ +services: + outline: + image: outlinewiki/outline:0.81.0 + restart: always + depends_on: + - postgres + - redis + - dex + ports: + - 3000 + environment: + NODE_ENV: production + URL: ${URL} + FORCE_HTTPS: 'false' + SECRET_KEY: ${SECRET_KEY} + UTILS_SECRET: ${UTILS_SECRET} + DATABASE_URL: postgres://outline:${POSTGRES_PASSWORD}@postgres:5432/outline + PGSSLMODE: disable + REDIS_URL: redis://redis:6379 + OIDC_CLIENT_ID: outline + OIDC_CLIENT_SECRET: ${CLIENT_SECRET} + OIDC_AUTH_URI: ${DEX_URL}/auth + OIDC_TOKEN_URI: ${DEX_URL}/token + OIDC_USERINFO_URI: ${DEX_URL}/userinfo + + dex: + image: ghcr.io/dexidp/dex:v2.37.0 + restart: always + volumes: + - ../files/etc/dex/config.yaml:/etc/dex/config.yaml + command: + - dex + - serve + - /etc/dex/config.yaml + ports: + - 5556 + + postgres: + image: postgres:15 + restart: always + environment: + POSTGRES_DB: outline + POSTGRES_USER: outline + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres_data-test-outline-khufpx:/var/lib/postgresql/data + + redis: + image: redis:latest + restart: always + command: redis-server --appendonly yes + volumes: + - redis_data-test-outline-khufpx:/data + +volumes: + postgres_data-test-outline-khufpx: + redis_data-test-outline-khufpx: \ No newline at end of file diff --git a/apps/dokploy/templates/outline/index.ts b/apps/dokploy/templates/outline/index.ts new file mode 100644 index 000000000..8431e5687 --- /dev/null +++ b/apps/dokploy/templates/outline/index.ts @@ -0,0 +1,90 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const dexDomain = generateRandomDomain(schema); + const SECRET_KEY = generateBase64(32); + const UTILS_SECRET = generateBase64(32); + const CLIENT_SECRET = generateBase64(32); + const POSTGRES_PASSWORD = generatePassword(); + + const mainURL = `http://${mainDomain}`; + const dexURL = `http://${dexDomain}`; + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "outline", + }, + { + host: dexDomain, + port: 5556, + serviceName: "dex", + }, + ]; + + const mounts: Template["mounts"] = [ + { + filePath: "/etc/dex/config.yaml", + content: `issuer: ${dexURL} + +web: + http: 0.0.0.0:5556 + +storage: + type: memory + +enablePasswordDB: true + +frontend: + issuer: Outline + +logger: + level: debug + +staticPasswords: + - email: "admin@example.com" + # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2) + hash: "$2y$10$jsRWHw54uxTUIfhjgUrB9u8HSzPk7TUuQri9sXZrKzRXcScvwYor." + username: "admin" + userID: "1" + + +oauth2: + skipApprovalScreen: true + alwaysShowLoginScreen: false + passwordConnector: local + +staticClients: + - id: "outline" + redirectURIs: + - ${mainURL}/auth/oidc.callback + name: "Outline" + secret: "${CLIENT_SECRET}"`, + }, + ]; + + const envs = [ + `URL=${mainURL}`, + `DEX_URL=${dexURL}`, + `DOMAIN_NAME=${mainDomain}`, + `POSTGRES_PASSWORD=${POSTGRES_PASSWORD}`, + `SECRET_KEY=${SECRET_KEY}`, + `UTILS_SECRET=${UTILS_SECRET}`, + `CLIENT_SECRET=${CLIENT_SECRET}`, + ]; + + return { + domains, + envs, + mounts, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 8143bbb2f..d04394a36 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,6 +1,21 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ + { + id: "outline", + name: "Outline", + version: "0.81.0", + description: + "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", + links: { + github: "https://github.com/outline/outline", + website: "https://outline.com/", + docs: "https://docs.outline.com/", + }, + logo: "outline.svg", + load: () => import("./outline/index").then((m) => m.generate), + tags: ["documentation", "knowledge-base", "self-hosted"], + }, { id: "supabase", name: "SupaBase", From 9ace0f38cd4fbd0c638da16a236d8bc7518d2826 Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sat, 15 Feb 2025 02:22:51 -0500 Subject: [PATCH 030/126] chore(lint): fix lint --- apps/dokploy/pages/dashboard/project/[projectId].tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 26317ca92..ea23ad3a8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -70,9 +70,9 @@ import type { } from "next"; import Head from "next/head"; import { useRouter } from "next/router"; -import { useMemo, useState, type ReactElement } from "react"; -import superjson from "superjson"; +import { type ReactElement, useMemo, useState } from "react"; import { toast } from "sonner"; +import superjson from "superjson"; export type Services = { appName: string; From e7195c8acf6e7b32151d783eef797636833f7165 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:07:33 -0600 Subject: [PATCH 031/126] Update apps/dokploy/templates/trilium/docker-compose.yml --- apps/dokploy/templates/trilium/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/templates/trilium/docker-compose.yml b/apps/dokploy/templates/trilium/docker-compose.yml index c80c2acf1..f549d8204 100644 --- a/apps/dokploy/templates/trilium/docker-compose.yml +++ b/apps/dokploy/templates/trilium/docker-compose.yml @@ -2,7 +2,7 @@ services: trilium: image: zadam/trilium:latest ports: - - 8080:8080 + - 8080 networks: - dokploy-network restart: always From 6ec60b6bab2a7f0bc0c31ea230fcc0bd9c299db0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:14:48 -0600 Subject: [PATCH 032/126] refactor: update validation --- packages/server/src/lib/auth.ts | 132 ++++++------ packages/server/src/services/admin.ts | 292 +++++++++++++------------- 2 files changed, 213 insertions(+), 211 deletions(-) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 48e11f215..226ab0a8c 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -8,78 +8,80 @@ import { db } from "../db"; import * as schema from "../db/schema"; export const auth = betterAuth({ - database: drizzleAdapter(db, { - provider: "pg", - schema: schema, - }), + database: drizzleAdapter(db, { + provider: "pg", + schema: schema, + }), - emailAndPassword: { - enabled: true, + emailAndPassword: { + enabled: true, - password: { - async hash(password) { - return bcrypt.hashSync(password, 10); - }, - async verify({ hash, password }) { - return bcrypt.compareSync(password, hash); - }, - }, - }, - hooks: { - after: createAuthMiddleware(async (ctx) => { - if (ctx.path.startsWith("/sign-up")) { - const newSession = ctx.context.newSession; - await db - .update(schema.users_temp) - .set({ - role: "admin", - }) - .where(eq(schema.users_temp.id, newSession?.user?.id || "")); - } - }), - }, - user: { - modelName: "users_temp", - additionalFields: { - role: { - type: "string", - }, - ownerId: { - type: "string", - }, - }, - }, - plugins: [organization()], + password: { + async hash(password) { + return bcrypt.hashSync(password, 10); + }, + async verify({ hash, password }) { + return bcrypt.compareSync(password, hash); + }, + }, + }, + hooks: { + after: createAuthMiddleware(async (ctx) => { + if (ctx.path.startsWith("/sign-up")) { + const newSession = ctx.context.newSession; + await db + .update(schema.users_temp) + .set({ + role: "admin", + }) + .where(eq(schema.users_temp.id, newSession?.user?.id || "")); + } + }), + }, + user: { + modelName: "users_temp", + additionalFields: { + role: { + type: "string", + }, + ownerId: { + type: "string", + }, + }, + }, + plugins: [organization()], }); export const validateRequest = async (request: IncomingMessage) => { - const session = await auth.api.getSession({ - headers: new Headers({ - cookie: request.headers.cookie || "", - }), - }); + const session = await auth.api.getSession({ + headers: new Headers({ + cookie: request.headers.cookie || "", + }), + }); - if (session?.user.role === "user") { - const owner = await db.query.member.findFirst({ - where: eq(schema.member.userId, session.user.id), - with: { - organization: true, - }, - }); + if (!session?.session || !session.user) { + return { + session: null, + user: null, + }; + } - if (owner) { - session.user.ownerId = owner.organization.ownerId; - } - } else { - session.user.ownerId = session?.user.id; - } + if (session?.user) { + if (session?.user.role === "user") { + const owner = await db.query.member.findFirst({ + where: eq(schema.member.userId, session.user.id), + with: { + organization: true, + }, + }); - if (!session?.session || !session.user) { - return { - session: null, - user: null, - }; - } + if (owner) { + session.user.ownerId = owner.organization.ownerId; + } + } else { + session.user.ownerId = session?.user?.id || ""; + } + } - return session; + return session; }; diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index ea87e4560..e8a60498f 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -1,10 +1,10 @@ import { randomBytes } from "node:crypto"; import { db } from "@dokploy/server/db"; import { - admins, - type apiCreateUserInvitation, - auth, - users_temp, + admins, + type apiCreateUserInvitation, + auth, + users_temp, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; @@ -13,188 +13,188 @@ import { IS_CLOUD } from "../constants"; export type Admin = typeof users_temp.$inferSelect; export const createInvitation = async ( - input: typeof apiCreateUserInvitation._type, - adminId: string, + input: typeof apiCreateUserInvitation._type, + adminId: string ) => { - await db.transaction(async (tx) => { - const result = await tx - .insert(auth) - .values({ - email: input.email.toLowerCase(), - rol: "user", - password: bcrypt.hashSync("01231203012312", 10), - }) - .returning() - .then((res) => res[0]); + await db.transaction(async (tx) => { + const result = await tx + .insert(auth) + .values({ + email: input.email.toLowerCase(), + rol: "user", + password: bcrypt.hashSync("01231203012312", 10), + }) + .returning() + .then((res) => res[0]); - if (!result) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - }); - } - const expiresIn24Hours = new Date(); - expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); - const token = randomBytes(32).toString("hex"); - await tx - .insert(users) - .values({ - adminId: adminId, - authId: result.id, - token, - expirationDate: expiresIn24Hours.toISOString(), - }) - .returning(); - }); + if (!result) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error creating the user", + }); + } + const expiresIn24Hours = new Date(); + expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); + const token = randomBytes(32).toString("hex"); + await tx + .insert(users) + .values({ + adminId: adminId, + authId: result.id, + token, + expirationDate: expiresIn24Hours.toISOString(), + }) + .returning(); + }); }; export const findUserById = async (userId: string) => { - const user = await db.query.users_temp.findFirst({ - where: eq(users_temp.id, userId), - // with: { - // account: true, - // }, - }); - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - return user; + const user = await db.query.users_temp.findFirst({ + where: eq(users_temp.id, userId), + // with: { + // account: true, + // }, + }); + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } + return user; }; export const updateUser = async (userId: string, userData: Partial) => { - const user = await db - .update(users_temp) - .set({ - ...userData, - }) - .where(eq(users_temp.id, userId)) - .returning() - .then((res) => res[0]); + const user = await db + .update(users_temp) + .set({ + ...userData, + }) + .where(eq(users_temp.id, userId)) + .returning() + .then((res) => res[0]); - return user; + return user; }; export const updateAdminById = async ( - adminId: string, - adminData: Partial, + adminId: string, + adminData: Partial ) => { - const admin = await db - .update(admins) - .set({ - ...adminData, - }) - .where(eq(admins.adminId, adminId)) - .returning() - .then((res) => res[0]); + const admin = await db + .update(admins) + .set({ + ...adminData, + }) + .where(eq(admins.adminId, adminId)) + .returning() + .then((res) => res[0]); - return admin; + return admin; }; export const findAdminById = async (userId: string) => { - const admin = await db.query.admins.findFirst({ - where: eq(admins.userId, userId), - }); - return admin; + const admin = await db.query.admins.findFirst({ + where: eq(admins.userId, userId), + }); + return admin; }; export const isAdminPresent = async () => { - const admin = await db.query.user.findFirst({ - where: eq(user.role, "admin"), - }); - if (!admin) { - return false; - } - return true; + const admin = await db.query.users_temp.findFirst({ + where: eq(users_temp.role, "admin"), + }); + if (!admin) { + return false; + } + return true; }; export const findAdminByAuthId = async (authId: string) => { - const admin = await db.query.admins.findFirst({ - where: eq(admins.authId, authId), - with: { - users: true, - }, - }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } - return admin; + const admin = await db.query.admins.findFirst({ + where: eq(admins.authId, authId), + with: { + users: true, + }, + }); + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } + return admin; }; export const findAdmin = async () => { - const admin = await db.query.admins.findFirst({}); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } - return admin; + const admin = await db.query.admins.findFirst({}); + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } + return admin; }; export const getUserByToken = async (token: string) => { - const user = await db.query.users.findFirst({ - where: eq(users.token, token), - with: { - auth: { - columns: { - password: false, - }, - }, - }, - }); + const user = await db.query.users.findFirst({ + where: eq(users.token, token), + with: { + auth: { + columns: { + password: false, + }, + }, + }, + }); - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Invitation not found", - }); - } - return { - ...user, - isExpired: user.isRegistered, - }; + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Invitation not found", + }); + } + return { + ...user, + isExpired: user.isRegistered, + }; }; export const removeUserById = async (userId: string) => { - await db - .delete(users_temp) - .where(eq(users_temp.id, userId)) - .returning() - .then((res) => res[0]); + await db + .delete(users_temp) + .where(eq(users_temp.id, userId)) + .returning() + .then((res) => res[0]); }; export const removeAdminByAuthId = async (authId: string) => { - const admin = await findAdminByAuthId(authId); - if (!admin) return null; + const admin = await findAdminByAuthId(authId); + if (!admin) return null; - // First delete all associated users - const users = admin.users; + // First delete all associated users + const users = admin.users; - for (const user of users) { - await removeUserById(user.id); - } - // Then delete the auth record which will cascade delete the admin - return await db - .delete(auth) - .where(eq(auth.id, authId)) - .returning() - .then((res) => res[0]); + for (const user of users) { + await removeUserById(user.id); + } + // Then delete the auth record which will cascade delete the admin + return await db + .delete(auth) + .where(eq(auth.id, authId)) + .returning() + .then((res) => res[0]); }; export const getDokployUrl = async () => { - if (IS_CLOUD) { - return "https://app.dokploy.com"; - } - const admin = await findAdmin(); + if (IS_CLOUD) { + return "https://app.dokploy.com"; + } + const admin = await findAdmin(); - if (admin.host) { - return `https://${admin.host}`; - } - return `http://${admin.serverIp}:${process.env.PORT}`; + if (admin.host) { + return `https://${admin.host}`; + } + return `http://${admin.serverIp}:${process.env.PORT}`; }; From fd69b45e5e5ac5edfe12be3c1759670049f15604 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:15:32 -0600 Subject: [PATCH 033/126] refactor: update envs --- apps/dokploy/templates/convex/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/templates/convex/index.ts b/apps/dokploy/templates/convex/index.ts index 0b3a981ac..6a112cdee 100644 --- a/apps/dokploy/templates/convex/index.ts +++ b/apps/dokploy/templates/convex/index.ts @@ -29,9 +29,9 @@ export function generate(schema: Schema): Template { ]; const envs = [ - `NEXT_PUBLIC_DEPLOYMENT_URL=${backendDomain}`, - `CONVEX_CLOUD_ORIGIN=${backendDomain}`, - `CONVEX_SITE_ORIGIN=${actionsDomain}`, + `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`, + `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`, + `CONVEX_SITE_ORIGIN=http://${actionsDomain}`, ]; return { envs, domains }; From 3b1ade804f1cfeeb06481839a6ff1600ae17e7eb Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sat, 15 Feb 2025 14:16:30 -0500 Subject: [PATCH 034/126] style(monitoring): use status badges for compose monitoring --- .../free/container/show-free-compose-monitoring.tsx | 7 ++++++- .../paid/container/show-paid-compose-monitoring.tsx | 6 +++++- apps/dokploy/pages/dashboard/project/[projectId].tsx | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx index 99be6d9d9..25c72bab7 100644 --- a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx @@ -1,3 +1,5 @@ +import { badgeStateColor } from "@/components/dashboard/application/logs/show"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, @@ -96,7 +98,10 @@ export const ComposeFreeMonitoring = ({ key={container.containerId} value={container.name} > - {container.name} ({container.containerId}) {container.state} + {container.name} ({container.containerId}){" "} + + {container.state} + ))} Containers ({data?.length}) diff --git a/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-compose-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-compose-monitoring.tsx index 580d7ea1c..4ca461c21 100644 --- a/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-compose-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-compose-monitoring.tsx @@ -1,3 +1,5 @@ +import { badgeStateColor } from "@/components/dashboard/application/logs/show"; +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, @@ -102,7 +104,9 @@ export const ComposePaidMonitoring = ({ value={container.name} > {container.name} ({container.containerId}){" "} - {container.state} + + {container.state} + ))} Containers ({data?.length}) diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 26317ca92..ea23ad3a8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -70,9 +70,9 @@ import type { } from "next"; import Head from "next/head"; import { useRouter } from "next/router"; -import { useMemo, useState, type ReactElement } from "react"; -import superjson from "superjson"; +import { type ReactElement, useMemo, useState } from "react"; import { toast } from "sonner"; +import superjson from "superjson"; export type Services = { appName: string; From 8366219266489d44897bc436ee8af0750db3c9ff Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:18:54 -0600 Subject: [PATCH 035/126] refactor: format --- apps/dokploy/templates/templates.ts | 3020 +++++++++++++-------------- 1 file changed, 1510 insertions(+), 1510 deletions(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index e05edd0e9..527a7ab5b 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,1516 +1,1516 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ - { - id: "supabase", - name: "SupaBase", - version: "1.24.07", - description: - "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", - links: { - github: "https://github.com/supabase/supabase", - website: "https://supabase.com/", - docs: "https://supabase.com/docs/guides/self-hosting", - }, - logo: "supabase.svg", - load: () => import("./supabase/index").then((m) => m.generate), - tags: ["database", "firebase", "postgres"], - }, - { - id: "pocketbase", - name: "Pocketbase", - version: "v0.22.12", - description: - "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", - links: { - github: "https://github.com/pocketbase/pocketbase", - website: "https://pocketbase.io/", - docs: "https://pocketbase.io/docs/", - }, - logo: "pocketbase.svg", - load: () => import("./pocketbase/index").then((m) => m.generate), - tags: ["database", "cms", "headless"], - }, - { - id: "plausible", - name: "Plausible", - version: "v2.1.5", - description: - "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "plausible.svg", - links: { - github: "https://github.com/plausible/plausible", - website: "https://plausible.io/", - docs: "https://plausible.io/docs", - }, - tags: ["analytics"], - load: () => import("./plausible/index").then((m) => m.generate), - }, - { - id: "calcom", - name: "Calcom", - version: "v2.7.6", - description: - "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", + { + id: "supabase", + name: "SupaBase", + version: "1.24.07", + description: + "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", + links: { + github: "https://github.com/supabase/supabase", + website: "https://supabase.com/", + docs: "https://supabase.com/docs/guides/self-hosting", + }, + logo: "supabase.svg", + load: () => import("./supabase/index").then((m) => m.generate), + tags: ["database", "firebase", "postgres"], + }, + { + id: "pocketbase", + name: "Pocketbase", + version: "v0.22.12", + description: + "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", + links: { + github: "https://github.com/pocketbase/pocketbase", + website: "https://pocketbase.io/", + docs: "https://pocketbase.io/docs/", + }, + logo: "pocketbase.svg", + load: () => import("./pocketbase/index").then((m) => m.generate), + tags: ["database", "cms", "headless"], + }, + { + id: "plausible", + name: "Plausible", + version: "v2.1.5", + description: + "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "plausible.svg", + links: { + github: "https://github.com/plausible/plausible", + website: "https://plausible.io/", + docs: "https://plausible.io/docs", + }, + tags: ["analytics"], + load: () => import("./plausible/index").then((m) => m.generate), + }, + { + id: "calcom", + name: "Calcom", + version: "v2.7.6", + description: + "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", - links: { - github: "https://github.com/calcom/cal.com", - website: "https://cal.com/", - docs: "https://cal.com/docs", - }, - logo: "calcom.jpg", - tags: ["scheduling", "booking"], - load: () => import("./calcom/index").then((m) => m.generate), - }, - { - id: "grafana", - name: "Grafana", - version: "9.5.20", - description: - "Grafana is an open source platform for data visualization and monitoring.", - logo: "grafana.svg", - links: { - github: "https://github.com/grafana/grafana", - website: "https://grafana.com/", - docs: "https://grafana.com/docs/", - }, - tags: ["monitoring"], - load: () => import("./grafana/index").then((m) => m.generate), - }, - { - id: "directus", - name: "Directus", - version: "11.0.2", - description: - "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", - logo: "directus.jpg", - links: { - github: "https://github.com/directus/directus", - website: "https://directus.io/", - docs: "https://docs.directus.io/", - }, - tags: ["cms"], - load: () => import("./directus/index").then((m) => m.generate), - }, - { - id: "baserow", - name: "Baserow", - version: "1.25.2", - description: - "Baserow is an open source database management tool that allows you to create and manage databases.", - logo: "baserow.webp", - links: { - github: "https://github.com/Baserow/baserow", - website: "https://baserow.io/", - docs: "https://baserow.io/docs/index", - }, - tags: ["database"], - load: () => import("./baserow/index").then((m) => m.generate), - }, - { - id: "budibase", - name: "Budibase", - version: "3.2.25", - description: - "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", - logo: "budibase.svg", - links: { - github: "https://github.com/Budibase/budibase", - website: "https://budibase.com/", - docs: "https://docs.budibase.com/docs/", - }, - tags: ["database", "low-code", "nocode", "applications"], - load: () => import("./budibase/index").then((m) => m.generate), - }, - { - id: "ghost", - name: "Ghost", - version: "5.0.0", - description: - "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", - logo: "ghost.jpeg", - links: { - github: "https://github.com/TryGhost/Ghost", - website: "https://ghost.org/", - docs: "https://ghost.org/docs/", - }, - tags: ["cms"], - load: () => import("./ghost/index").then((m) => m.generate), - }, - { - id: "uptime-kuma", - name: "Uptime Kuma", - version: "1.23.15", - description: - "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", - logo: "uptime-kuma.png", - links: { - github: "https://github.com/louislam/uptime-kuma", - website: "https://uptime.kuma.pet/", - docs: "https://github.com/louislam/uptime-kuma/wiki", - }, - tags: ["monitoring"], - load: () => import("./uptime-kuma/index").then((m) => m.generate), - }, - { - id: "n8n", - name: "n8n", - version: "1.70.3", - description: - "n8n is an open source low-code platform for automating workflows and integrations.", - logo: "n8n.png", - links: { - github: "https://github.com/n8n-io/n8n", - website: "https://n8n.io/", - docs: "https://docs.n8n.io/", - }, - tags: ["automation"], - load: () => import("./n8n/index").then((m) => m.generate), - }, - { - id: "wordpress", - name: "Wordpress", - version: "6.7.1", - description: - "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", - logo: "wordpress.png", - links: { - github: "https://github.com/WordPress/WordPress", - website: "https://wordpress.org/", - docs: "https://wordpress.org/documentation/", - }, - tags: ["cms"], - load: () => import("./wordpress/index").then((m) => m.generate), - }, - { - id: "odoo", - name: "Odoo", - version: "16.0", - description: - "Odoo is a free and open source business management software that helps you manage your company's operations.", - logo: "odoo.png", - links: { - github: "https://github.com/odoo/odoo", - website: "https://odoo.com/", - docs: "https://www.odoo.com/documentation/", - }, - tags: ["cms"], - load: () => import("./odoo/index").then((m) => m.generate), - }, - { - id: "appsmith", - name: "Appsmith", - version: "v1.29", - description: - "Appsmith is a free and open source platform for building internal tools and applications.", - logo: "appsmith.png", - links: { - github: "https://github.com/appsmithorg/appsmith", - website: "https://appsmith.com/", - docs: "https://docs.appsmith.com/", - }, - tags: ["cms"], - load: () => import("./appsmith/index").then((m) => m.generate), - }, - { - id: "excalidraw", - name: "Excalidraw", - version: "latest", - description: - "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", - logo: "excalidraw.jpg", - links: { - github: "https://github.com/excalidraw/excalidraw", - website: "https://excalidraw.com/", - docs: "https://docs.excalidraw.com/", - }, - tags: ["drawing"], - load: () => import("./excalidraw/index").then((m) => m.generate), - }, - { - id: "documenso", - name: "Documenso", - version: "v1.5.6", - description: - "Documenso is the open source alternative to DocuSign for signing documents digitally", - links: { - github: "https://github.com/documenso/documenso", - website: "https://documenso.com/", - docs: "https://documenso.com/docs", - }, - logo: "documenso.png", - tags: ["document-signing"], - load: () => import("./documenso/index").then((m) => m.generate), - }, - { - id: "nocodb", - name: "NocoDB", - version: "0.257.2", - description: - "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", + links: { + github: "https://github.com/calcom/cal.com", + website: "https://cal.com/", + docs: "https://cal.com/docs", + }, + logo: "calcom.jpg", + tags: ["scheduling", "booking"], + load: () => import("./calcom/index").then((m) => m.generate), + }, + { + id: "grafana", + name: "Grafana", + version: "9.5.20", + description: + "Grafana is an open source platform for data visualization and monitoring.", + logo: "grafana.svg", + links: { + github: "https://github.com/grafana/grafana", + website: "https://grafana.com/", + docs: "https://grafana.com/docs/", + }, + tags: ["monitoring"], + load: () => import("./grafana/index").then((m) => m.generate), + }, + { + id: "directus", + name: "Directus", + version: "11.0.2", + description: + "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", + logo: "directus.jpg", + links: { + github: "https://github.com/directus/directus", + website: "https://directus.io/", + docs: "https://docs.directus.io/", + }, + tags: ["cms"], + load: () => import("./directus/index").then((m) => m.generate), + }, + { + id: "baserow", + name: "Baserow", + version: "1.25.2", + description: + "Baserow is an open source database management tool that allows you to create and manage databases.", + logo: "baserow.webp", + links: { + github: "https://github.com/Baserow/baserow", + website: "https://baserow.io/", + docs: "https://baserow.io/docs/index", + }, + tags: ["database"], + load: () => import("./baserow/index").then((m) => m.generate), + }, + { + id: "budibase", + name: "Budibase", + version: "3.2.25", + description: + "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", + logo: "budibase.svg", + links: { + github: "https://github.com/Budibase/budibase", + website: "https://budibase.com/", + docs: "https://docs.budibase.com/docs/", + }, + tags: ["database", "low-code", "nocode", "applications"], + load: () => import("./budibase/index").then((m) => m.generate), + }, + { + id: "ghost", + name: "Ghost", + version: "5.0.0", + description: + "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", + logo: "ghost.jpeg", + links: { + github: "https://github.com/TryGhost/Ghost", + website: "https://ghost.org/", + docs: "https://ghost.org/docs/", + }, + tags: ["cms"], + load: () => import("./ghost/index").then((m) => m.generate), + }, + { + id: "uptime-kuma", + name: "Uptime Kuma", + version: "1.23.15", + description: + "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", + logo: "uptime-kuma.png", + links: { + github: "https://github.com/louislam/uptime-kuma", + website: "https://uptime.kuma.pet/", + docs: "https://github.com/louislam/uptime-kuma/wiki", + }, + tags: ["monitoring"], + load: () => import("./uptime-kuma/index").then((m) => m.generate), + }, + { + id: "n8n", + name: "n8n", + version: "1.70.3", + description: + "n8n is an open source low-code platform for automating workflows and integrations.", + logo: "n8n.png", + links: { + github: "https://github.com/n8n-io/n8n", + website: "https://n8n.io/", + docs: "https://docs.n8n.io/", + }, + tags: ["automation"], + load: () => import("./n8n/index").then((m) => m.generate), + }, + { + id: "wordpress", + name: "Wordpress", + version: "6.7.1", + description: + "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", + logo: "wordpress.png", + links: { + github: "https://github.com/WordPress/WordPress", + website: "https://wordpress.org/", + docs: "https://wordpress.org/documentation/", + }, + tags: ["cms"], + load: () => import("./wordpress/index").then((m) => m.generate), + }, + { + id: "odoo", + name: "Odoo", + version: "16.0", + description: + "Odoo is a free and open source business management software that helps you manage your company's operations.", + logo: "odoo.png", + links: { + github: "https://github.com/odoo/odoo", + website: "https://odoo.com/", + docs: "https://www.odoo.com/documentation/", + }, + tags: ["cms"], + load: () => import("./odoo/index").then((m) => m.generate), + }, + { + id: "appsmith", + name: "Appsmith", + version: "v1.29", + description: + "Appsmith is a free and open source platform for building internal tools and applications.", + logo: "appsmith.png", + links: { + github: "https://github.com/appsmithorg/appsmith", + website: "https://appsmith.com/", + docs: "https://docs.appsmith.com/", + }, + tags: ["cms"], + load: () => import("./appsmith/index").then((m) => m.generate), + }, + { + id: "excalidraw", + name: "Excalidraw", + version: "latest", + description: + "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", + logo: "excalidraw.jpg", + links: { + github: "https://github.com/excalidraw/excalidraw", + website: "https://excalidraw.com/", + docs: "https://docs.excalidraw.com/", + }, + tags: ["drawing"], + load: () => import("./excalidraw/index").then((m) => m.generate), + }, + { + id: "documenso", + name: "Documenso", + version: "v1.5.6", + description: + "Documenso is the open source alternative to DocuSign for signing documents digitally", + links: { + github: "https://github.com/documenso/documenso", + website: "https://documenso.com/", + docs: "https://documenso.com/docs", + }, + logo: "documenso.png", + tags: ["document-signing"], + load: () => import("./documenso/index").then((m) => m.generate), + }, + { + id: "nocodb", + name: "NocoDB", + version: "0.257.2", + description: + "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", - links: { - github: "https://github.com/nocodb/nocodb", - website: "https://nocodb.com/", - docs: "https://docs.nocodb.com/", - }, - logo: "nocodb.png", - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./nocodb/index").then((m) => m.generate), - }, - { - id: "meilisearch", - name: "Meilisearch", - version: "v1.8.3", - description: - "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", - logo: "meilisearch.png", - links: { - github: "https://github.com/meilisearch/meilisearch", - website: "https://www.meilisearch.com/", - docs: "https://docs.meilisearch.com/", - }, - tags: ["search"], - load: () => import("./meilisearch/index").then((m) => m.generate), - }, - { - id: "phpmyadmin", - name: "Phpmyadmin", - version: "5.2.1", - description: - "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", - logo: "phpmyadmin.png", - links: { - github: "https://github.com/phpmyadmin/phpmyadmin", - website: "https://www.phpmyadmin.net/", - docs: "https://www.phpmyadmin.net/docs/", - }, - tags: ["database"], - load: () => import("./phpmyadmin/index").then((m) => m.generate), - }, - { - id: "rocketchat", - name: "Rocketchat", - version: "6.9.2", - description: - "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", - logo: "rocketchat.png", - links: { - github: "https://github.com/RocketChat/Rocket.Chat", - website: "https://rocket.chat/", - docs: "https://rocket.chat/docs/", - }, - tags: ["chat"], - load: () => import("./rocketchat/index").then((m) => m.generate), - }, - { - id: "minio", - name: "Minio", - description: - "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", - logo: "minio.png", - version: "latest", - links: { - github: "https://github.com/minio/minio", - website: "https://minio.io/", - docs: "https://docs.minio.io/", - }, - tags: ["storage"], - load: () => import("./minio/index").then((m) => m.generate), - }, - { - id: "metabase", - name: "Metabase", - version: "v0.50.8", - description: - "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", - logo: "metabase.png", - links: { - github: "https://github.com/metabase/metabase", - website: "https://www.metabase.com/", - docs: "https://www.metabase.com/docs/", - }, - tags: ["database", "dashboard"], - load: () => import("./metabase/index").then((m) => m.generate), - }, - { - id: "glitchtip", - name: "Glitchtip", - version: "v4.0", - description: "Glitchtip is simple, open source error tracking", - logo: "glitchtip.png", - links: { - github: "https://gitlab.com/glitchtip/", - website: "https://glitchtip.com/", - docs: "https://glitchtip.com/documentation", - }, - tags: ["hosting"], - load: () => import("./glitchtip/index").then((m) => m.generate), - }, - { - id: "open-webui", - name: "Open WebUI", - version: "v0.3.7", - description: - "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", - logo: "open-webui.png", - links: { - github: "https://github.com/open-webui/open-webui", - website: "https://openwebui.com/", - docs: "https://docs.openwebui.com/", - }, - tags: ["chat"], - load: () => import("./open-webui/index").then((m) => m.generate), - }, - { - id: "listmonk", - name: "Listmonk", - version: "v3.0.0", - description: - "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", - logo: "listmonk.png", - links: { - github: "https://github.com/knadh/listmonk", - website: "https://listmonk.app/", - docs: "https://listmonk.app/docs/", - }, - tags: ["email", "newsletter", "mailing-list"], - load: () => import("./listmonk/index").then((m) => m.generate), - }, - { - id: "doublezero", - name: "Double Zero", - version: "v0.2.1", - description: - "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", - logo: "doublezero.svg", - links: { - github: "https://github.com/technomancy-dev/00", - website: "https://www.double-zero.cloud/", - docs: "https://github.com/technomancy-dev/00", - }, - tags: ["email"], - load: () => import("./doublezero/index").then((m) => m.generate), - }, - { - id: "umami", - name: "Umami", - version: "v2.14.0", - description: - "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", - logo: "umami.png", - links: { - github: "https://github.com/umami-software/umami", - website: "https://umami.is", - docs: "https://umami.is/docs", - }, - tags: ["analytics"], - load: () => import("./umami/index").then((m) => m.generate), - }, - { - id: "jellyfin", - name: "jellyfin", - version: "v10.9.7", - description: - "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", - logo: "jellyfin.svg", - links: { - github: "https://github.com/jellyfin/jellyfin", - website: "https://jellyfin.org/", - docs: "https://jellyfin.org/docs/", - }, - tags: ["media system"], - load: () => import("./jellyfin/index").then((m) => m.generate), - }, - { - id: "teable", - name: "teable", - version: "v1.3.1-alpha-build.460", - description: - "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", - logo: "teable.png", - links: { - github: "https://github.com/teableio/teable", - website: "https://teable.io/", - docs: "https://help.teable.io/", - }, - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./teable/index").then((m) => m.generate), - }, - { - id: "zipline", - name: "Zipline", - version: "v3.7.9", - description: - "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", - logo: "zipline.png", - links: { - github: "https://github.com/diced/zipline", - website: "https://zipline.diced.sh/", - docs: "https://zipline.diced.sh/docs/", - }, - tags: ["media system", "storage"], - load: () => import("./zipline/index").then((m) => m.generate), - }, - { - id: "soketi", - name: "Soketi", - version: "v1.6.1-16", - description: - "Soketi is your simple, fast, and resilient open-source WebSockets server.", - logo: "soketi.png", - links: { - github: "https://github.com/soketi/soketi", - website: "https://soketi.app/", - docs: "https://docs.soketi.app/", - }, - tags: ["chat"], - load: () => import("./soketi/index").then((m) => m.generate), - }, - { - id: "aptabase", - name: "Aptabase", - version: "v1.0.0", - description: - "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "aptabase.svg", - links: { - github: "https://github.com/aptabase/aptabase", - website: "https://aptabase.com/", - docs: "https://github.com/aptabase/aptabase/blob/main/README.md", - }, - tags: ["analytics", "self-hosted"], - load: () => import("./aptabase/index").then((m) => m.generate), - }, - { - id: "typebot", - name: "Typebot", - version: "2.27.0", - description: "Typebot is an open-source chatbot builder platform.", - logo: "typebot.svg", - links: { - github: "https://github.com/baptisteArno/typebot.io", - website: "https://typebot.io/", - docs: "https://docs.typebot.io/get-started/introduction", - }, - tags: ["chatbot", "builder", "open-source"], - load: () => import("./typebot/index").then((m) => m.generate), - }, - { - id: "gitea", - name: "Gitea", - version: "1.22.3", - description: - "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", - logo: "gitea.png", - links: { - github: "https://github.com/go-gitea/gitea.git", - website: "https://gitea.com/", - docs: "https://docs.gitea.com/installation/install-with-docker", - }, - tags: ["self-hosted", "storage"], - load: () => import("./gitea/index").then((m) => m.generate), - }, - { - id: "roundcube", - name: "Roundcube", - version: "1.6.9", - description: - "Free and open source webmail software for the masses, written in PHP.", - logo: "roundcube.svg", - links: { - github: "https://github.com/roundcube/roundcubemail", - website: "https://roundcube.net/", - docs: "https://roundcube.net/about/", - }, - tags: ["self-hosted", "email", "webmail"], - load: () => import("./roundcube/index").then((m) => m.generate), - }, - { - id: "filebrowser", - name: "File Browser", - version: "2.31.2", - description: - "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", - logo: "filebrowser.svg", - links: { - github: "https://github.com/filebrowser/filebrowser", - website: "https://filebrowser.org/", - docs: "https://filebrowser.org/", - }, - tags: ["file-manager", "storage"], - load: () => import("./filebrowser/index").then((m) => m.generate), - }, - { - id: "tolgee", - name: "Tolgee", - version: "v3.80.4", - description: - "Developer & translator friendly web-based localization platform", - logo: "tolgee.svg", - links: { - github: "https://github.com/tolgee/tolgee-platform", - website: "https://tolgee.io", - docs: "https://tolgee.io/platform", - }, - tags: ["self-hosted", "i18n", "localization", "translations"], - load: () => import("./tolgee/index").then((m) => m.generate), - }, - { - id: "portainer", - name: "Portainer", - version: "2.21.4", - description: - "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", - logo: "portainer.svg", - links: { - github: "https://github.com/portainer/portainer", - website: "https://www.portainer.io/", - docs: "https://docs.portainer.io/", - }, - tags: ["cloud", "monitoring"], - load: () => import("./portainer/index").then((m) => m.generate), - }, - { - id: "influxdb", - name: "InfluxDB", - version: "2.7.10", - description: - "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", - logo: "influxdb.png", - links: { - github: "https://github.com/influxdata/influxdb", - website: "https://www.influxdata.com/", - docs: "https://docs.influxdata.com/influxdb/v2/", - }, - tags: ["self-hosted", "open-source", "storage", "database"], - load: () => import("./influxdb/index").then((m) => m.generate), - }, - { - id: "infisical", - name: "Infisical", - version: "0.90.1", - description: - "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", - logo: "infisical.jpg", - links: { - github: "https://github.com/Infisical/infisical", - website: "https://infisical.com/", - docs: "https://infisical.com/docs/documentation/getting-started/introduction", - }, - tags: ["self-hosted", "open-source"], - load: () => import("./infisical/index").then((m) => m.generate), - }, - { - id: "docmost", - name: "Docmost", - version: "0.4.1", - description: - "Docmost, is an open-source collaborative wiki and documentation software.", - logo: "docmost.png", - links: { - github: "https://github.com/docmost/docmost", - website: "https://docmost.com/", - docs: "https://docmost.com/docs/", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./docmost/index").then((m) => m.generate), - }, - { - id: "vaultwarden", - name: "Vaultwarden", - version: "1.32.7", - description: - "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", - logo: "vaultwarden.svg", - links: { - github: "https://github.com/dani-garcia/vaultwarden", - website: "", - docs: "https://github.com/dani-garcia/vaultwarden/wiki", - }, - tags: ["open-source"], - load: () => import("./vaultwarden/index").then((m) => m.generate), - }, - { - id: "hi-events", - name: "Hi.events", - version: "0.8.0-beta.1", - description: - "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", - logo: "hi-events.svg", - links: { - github: "https://github.com/HiEventsDev/hi.events", - website: "https://hi.events/", - docs: "https://hi.events/docs", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./hi-events/index").then((m) => m.generate), - }, - { - id: "windows", - name: "Windows (dockerized)", - version: "4.00", - description: "Windows inside a Docker container.", - logo: "windows.png", - links: { - github: "https://github.com/dockur/windows", - website: "", - docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./windows/index").then((m) => m.generate), - }, - { - id: "macos", - name: "MacOS (dockerized)", - version: "1.14", - description: "MacOS inside a Docker container.", - logo: "macos.png", - links: { - github: "https://github.com/dockur/macos", - website: "", - docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./macos/index").then((m) => m.generate), - }, - { - id: "coder", - name: "Coder", - version: "2.15.3", - description: - "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", - logo: "coder.svg", - links: { - github: "https://github.com/coder/coder", - website: "https://coder.com/", - docs: "https://coder.com/docs", - }, - tags: ["self-hosted", "open-source", "builder"], - load: () => import("./coder/index").then((m) => m.generate), - }, - { - id: "stirling", - name: "Stirling PDF", - version: "0.30.1", - description: "A locally hosted one-stop shop for all your PDF needs", - logo: "stirling.svg", - links: { - github: "https://github.com/Stirling-Tools/Stirling-PDF", - website: "https://www.stirlingpdf.com/", - docs: "https://docs.stirlingpdf.com/", - }, - tags: ["pdf", "tools"], - load: () => import("./stirling/index").then((m) => m.generate), - }, - { - id: "lobe-chat", - name: "Lobe Chat", - version: "v1.26.1", - description: "Lobe Chat - an open-source, modern-design AI chat framework.", - logo: "lobe-chat.png", - links: { - github: "https://github.com/lobehub/lobe-chat", - website: "https://chat-preview.lobehub.com/", - docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", - }, - tags: ["IA", "chat"], - load: () => import("./lobe-chat/index").then((m) => m.generate), - }, - { - id: "peppermint", - name: "Peppermint", - version: "latest", - description: - "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", - logo: "peppermint.svg", - links: { - github: "https://github.com/Peppermint-Lab/peppermint", - website: "https://peppermint.sh/", - docs: "https://docs.peppermint.sh/", - }, - tags: ["api", "development", "documentation"], - load: () => import("./peppermint/index").then((m) => m.generate), - }, - { - id: "windmill", - name: "Windmill", - version: "latest", - description: - "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", - logo: "windmill.svg", - links: { - github: "https://github.com/windmill-labs/windmill", - website: "https://www.windmill.dev/", - docs: "https://docs.windmill.dev/", - }, - tags: ["workflow", "automation", "development"], - load: () => import("./windmill/index").then((m) => m.generate), - }, - { - id: "activepieces", - name: "Activepieces", - version: "0.35.0", - description: - "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", - logo: "activepieces.svg", - links: { - github: "https://github.com/activepieces/activepieces", - website: "https://www.activepieces.com/", - docs: "https://www.activepieces.com/docs", - }, - tags: ["automation", "workflow", "no-code"], - load: () => import("./activepieces/index").then((m) => m.generate), - }, - { - id: "invoiceshelf", - name: "InvoiceShelf", - version: "latest", - description: - "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", - logo: "invoiceshelf.png", - links: { - github: "https://github.com/InvoiceShelf/invoiceshelf", - website: "https://invoiceshelf.com", - docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./invoiceshelf/index").then((m) => m.generate), - }, - { - id: "postiz", - name: "Postiz", - version: "latest", - description: - "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", - logo: "postiz.png", - links: { - github: "https://github.com/gitroomhq/postiz", - website: "https://postiz.com", - docs: "https://docs.postiz.com", - }, - tags: ["cms", "content-management", "publishing"], - load: () => import("./postiz/index").then((m) => m.generate), - }, - { - id: "slash", - name: "Slash", - version: "latest", - description: - "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", - logo: "slash.png", - links: { - github: "https://github.com/yourselfhosted/slash", - website: "https://github.com/yourselfhosted/slash#readme", - docs: "https://github.com/yourselfhosted/slash/wiki", - }, - tags: ["bookmarks", "link-shortener", "self-hosted"], - load: () => import("./slash/index").then((m) => m.generate), - }, - { - id: "discord-tickets", - name: "Discord Tickets", - version: "4.0.21", - description: - "An open-source Discord bot for creating and managing support ticket channels.", - logo: "discord-tickets.png", - links: { - github: "https://github.com/discord-tickets/bot", - website: "https://discordtickets.app", - docs: "https://discordtickets.app/self-hosting/installation/docker/", - }, - tags: ["discord", "tickets", "support"], - load: () => import("./discord-tickets/index").then((m) => m.generate), - }, - { - id: "nextcloud-aio", - name: "Nextcloud All in One", - version: "30.0.2", - description: - "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", - logo: "nextcloud-aio.svg", - links: { - github: "https://github.com/nextcloud/docker", - website: "https://nextcloud.com/", - docs: "https://docs.nextcloud.com/", - }, - tags: ["file-manager", "sync"], - load: () => import("./nextcloud-aio/index").then((m) => m.generate), - }, - { - id: "blender", - name: "Blender", - version: "latest", - description: - "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", - logo: "blender.svg", - links: { - github: "https://github.com/linuxserver/docker-blender", - website: "https://www.blender.org/", - docs: "https://docs.blender.org/", - }, - tags: ["3d", "rendering", "animation"], - load: () => import("./blender/index").then((m) => m.generate), - }, - { - id: "heyform", - name: "HeyForm", - version: "latest", - description: - "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", - logo: "heyform.svg", - links: { - github: "https://github.com/heyform/heyform", - website: "https://heyform.net", - docs: "https://docs.heyform.net", - }, - tags: ["form", "builder", "questionnaire", "quiz", "survey"], - load: () => import("./heyform/index").then((m) => m.generate), - }, - { - id: "chatwoot", - name: "Chatwoot", - version: "v3.14.1", - description: - "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", - logo: "chatwoot.svg", - links: { - github: "https://github.com/chatwoot/chatwoot", - website: "https://www.chatwoot.com", - docs: "https://www.chatwoot.com/docs", - }, - tags: ["support", "chat", "customer-service"], - load: () => import("./chatwoot/index").then((m) => m.generate), - }, - { - id: "discourse", - name: "Discourse", - version: "3.3.2", - description: - "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", - logo: "discourse.svg", - links: { - github: "https://github.com/discourse/discourse", - website: "https://www.discourse.org/", - docs: "https://meta.discourse.org/", - }, - tags: ["forum", "community", "discussion"], - load: () => import("./discourse/index").then((m) => m.generate), - }, - { - id: "immich", - name: "Immich", - version: "v1.121.0", - description: - "High performance self-hosted photo and video backup solution directly from your mobile phone.", - logo: "immich.svg", - links: { - github: "https://github.com/immich-app/immich", - website: "https://immich.app/", - docs: "https://immich.app/docs/overview/introduction", - }, - tags: ["photos", "videos", "backup", "media"], - load: () => import("./immich/index").then((m) => m.generate), - }, - { - id: "twenty", - name: "Twenty CRM", - version: "latest", - description: - "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", - logo: "twenty.svg", - links: { - github: "https://github.com/twentyhq/twenty", - website: "https://twenty.com", - docs: "https://docs.twenty.com", - }, - tags: ["crm", "sales", "business"], - load: () => import("./twenty/index").then((m) => m.generate), - }, - { - id: "yourls", - name: "YOURLS", - version: "1.9.2", - description: - "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", - logo: "yourls.svg", - links: { - github: "https://github.com/YOURLS/YOURLS", - website: "https://yourls.org/", - docs: "https://yourls.org/#documentation", - }, - tags: ["url-shortener", "php"], - load: () => import("./yourls/index").then((m) => m.generate), - }, - { - id: "ryot", - name: "Ryot", - version: "v7.10", - description: - "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", - logo: "ryot.png", - links: { - github: "https://github.com/IgnisDa/ryot", - website: "https://ryot.io/", - docs: "https://docs.ryot.io/", - }, - tags: ["media", "tracking", "self-hosted"], - load: () => import("./ryot/index").then((m) => m.generate), - }, - { - id: "photoprism", - name: "Photoprism", - version: "latest", - description: - "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", - logo: "photoprism.svg", - links: { - github: "https://github.com/photoprism/photoprism", - website: "https://www.photoprism.app/", - docs: "https://docs.photoprism.app/", - }, - tags: ["media", "photos", "self-hosted"], - load: () => import("./photoprism/index").then((m) => m.generate), - }, - { - id: "ontime", - name: "Ontime", - version: "v3.8.0", - description: - "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", - logo: "ontime.png", - links: { - github: "https://github.com/cpvalente/ontime/", - website: "https://getontime.no", - docs: "https://docs.getontime.no", - }, - tags: ["event"], - load: () => import("./ontime/index").then((m) => m.generate), - }, - { - id: "triggerdotdev", - name: "Trigger.dev", - version: "v3", - description: - "Trigger is a platform for building event-driven applications.", - logo: "triggerdotdev.svg", - links: { - github: "https://github.com/triggerdotdev/trigger.dev", - website: "https://trigger.dev/", - docs: "https://trigger.dev/docs", - }, - tags: ["event-driven", "applications"], - load: () => import("./triggerdotdev/index").then((m) => m.generate), - }, - { - id: "browserless", - name: "Browserless", - version: "2.23.0", - description: - "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", - logo: "browserless.svg", - links: { - github: "https://github.com/browserless/browserless", - website: "https://www.browserless.io/", - docs: "https://docs.browserless.io/", - }, - tags: ["browser", "automation"], - load: () => import("./browserless/index").then((m) => m.generate), - }, - { - id: "drawio", - name: "draw.io", - version: "24.7.17", - description: - "draw.io is a configurable diagramming/whiteboarding visualization application.", - logo: "drawio.svg", - links: { - github: "https://github.com/jgraph/drawio", - website: "https://draw.io/", - docs: "https://www.drawio.com/doc/", - }, - tags: ["drawing", "diagrams"], - load: () => import("./drawio/index").then((m) => m.generate), - }, - { - id: "kimai", - name: "Kimai", - version: "2.26.0", - description: - "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", - logo: "kimai.svg", - links: { - github: "https://github.com/kimai/kimai", - website: "https://www.kimai.org", - docs: "https://www.kimai.org/documentation", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./kimai/index").then((m) => m.generate), - }, - { - id: "logto", - name: "Logto", - version: "1.22.0", - description: - "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", - logo: "logto.png", - links: { - github: "https://github.com/logto-io/logto", - website: "https://logto.io/", - docs: "https://docs.logto.io/introduction", - }, - tags: ["identity", "auth"], - load: () => import("./logto/index").then((m) => m.generate), - }, - { - id: "penpot", - name: "Penpot", - version: "2.3.2", - description: - "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", - logo: "penpot.svg", - links: { - github: "https://github.com/penpot/penpot", - website: "https://penpot.app/", - docs: "https://docs.penpot.app/", - }, - tags: ["design", "collaboration"], - load: () => import("./penpot/index").then((m) => m.generate), - }, - { - id: "huly", - name: "Huly", - version: "0.6.377", - description: - "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", - logo: "huly.svg", - links: { - github: "https://github.com/hcengineering/huly-selfhost", - website: "https://huly.io/", - docs: "https://docs.huly.io/", - }, - tags: ["project-management", "community", "discussion"], - load: () => import("./huly/index").then((m) => m.generate), - }, - { - id: "unsend", - name: "Unsend", - version: "v1.3.2", - description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", - logo: "unsend.png", - links: { - github: "https://github.com/unsend-dev/unsend", - website: "https://unsend.dev/", - docs: "https://docs.unsend.dev/get-started/", - }, - tags: ["e-mail", "marketing", "business"], - load: () => import("./unsend/index").then((m) => m.generate), - }, - { - id: "langflow", - name: "Langflow", - version: "1.1.1", - description: - "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", - logo: "langflow.svg", - links: { - github: "https://github.com/langflow-ai/langflow/tree/main", - website: "https://www.langflow.org/", - docs: "https://docs.langflow.org/", - }, - tags: ["ai"], - load: () => import("./langflow/index").then((m) => m.generate), - }, - { - id: "elastic-search", - name: "Elasticsearch", - version: "8.10.2", - description: - "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", - logo: "elasticsearch.svg", - links: { - github: "https://github.com/elastic/elasticsearch", - website: "https://www.elastic.co/elasticsearch/", - docs: "https://docs.elastic.co/elasticsearch/", - }, - tags: ["search", "analytics"], - load: () => import("./elastic-search/index").then((m) => m.generate), - }, - { - id: "onedev", - name: "OneDev", - version: "11.6.6", - description: - "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", - logo: "onedev.png", - links: { - github: "https://github.com/theonedev/onedev/", - website: "https://onedev.io/", - docs: "https://docs.onedev.io/", - }, - tags: ["self-hosted", "development"], - load: () => import("./onedev/index").then((m) => m.generate), - }, - { - id: "unifi", - name: "Unifi Network", - version: "11.6.6", - description: - "Unifi Network is an open-source enterprise network management platform for wireless networks.", - logo: "unifi.webp", - links: { - github: "https://github.com/ubiquiti", - website: "https://www.ui.com/", - docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", - }, - tags: ["self-hosted", "networking"], - load: () => import("./unifi/index").then((m) => m.generate), - }, - { - id: "glpi", - name: "GLPI Project", - version: "10.0.16", - description: "The most complete open source service management software", - logo: "glpi.webp", - links: { - github: "https://github.com/glpi-project/glpi", - website: "https://glpi-project.org/", - docs: "https://glpi-project.org/documentation/", - }, - tags: ["self-hosted", "project-management", "management"], - load: () => import("./glpi/index").then((m) => m.generate), - }, - { - id: "checkmate", - name: "Checkmate", - version: "2.0.1", - description: - "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", - logo: "checkmate.png", - links: { - github: "https://github.com/bluewave-labs/checkmate", - website: "https://bluewavelabs.ca", - docs: "https://bluewavelabs.gitbook.io/checkmate", - }, - tags: ["self-hosted", "monitoring", "uptime"], - load: () => import("./checkmate/index").then((m) => m.generate), - }, - { - id: "gotenberg", - name: "Gotenberg", - version: "latest", - description: "Gotenberg is a Docker-powered stateless API for PDF files.", - logo: "gotenberg.png", - links: { - github: "https://github.com/gotenberg/gotenberg", - website: "https://gotenberg.dev", - docs: "https://gotenberg.dev/docs/getting-started/introduction", - }, - tags: ["api", "backend", "pdf", "tools"], - load: () => import("./gotenberg/index").then((m) => m.generate), - }, - { - id: "actualbudget", - name: "Actual Budget", - version: "latest", - description: - "A super fast and privacy-focused app for managing your finances.", - logo: "actualbudget.png", - links: { - github: "https://github.com/actualbudget/actual", - website: "https://actualbudget.org", - docs: "https://actualbudget.org/docs", - }, - tags: ["budgeting", "finance", "money"], - load: () => import("./actualbudget/index").then((m) => m.generate), - }, - { - id: "conduit", - name: "Conduit", - version: "v0.9.0", - description: - "Conduit is a simple, fast and reliable chat server powered by Matrix", - logo: "conduit.svg", - links: { - github: "https://gitlab.com/famedly/conduit", - website: "https://conduit.rs/", - docs: "https://docs.conduit.rs/", - }, - tags: ["matrix", "communication"], - load: () => import("./conduit/index").then((m) => m.generate), - }, - { - id: "evolutionapi", - name: "Evolution API", - version: "v2.1.2", - description: - "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", - logo: "evolutionapi.png", - links: { - github: "https://github.com/EvolutionAPI/evolution-api", - docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", - website: "https://evolution-api.com/opensource-whatsapp-api/", - }, - tags: ["api", "whatsapp", "messaging"], - load: () => import("./evolutionapi/index").then((m) => m.generate), - }, - { - id: "conduwuit", - name: "Conduwuit", - version: "latest", - description: - "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", - logo: "conduwuit.svg", - links: { - github: "https://github.com/girlbossceo/conduwuit", - website: "https://conduwuit.puppyirl.gay", - docs: "https://conduwuit.puppyirl.gay/configuration.html", - }, - tags: ["backend", "chat", "communication", "matrix", "server"], - load: () => import("./conduwuit/index").then((m) => m.generate), - }, - { - id: "cloudflared", - name: "Cloudflared", - version: "latest", - description: - "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", - logo: "cloudflared.svg", - links: { - github: "https://github.com/cloudflare/cloudflared", - website: - "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", - docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", - }, - tags: ["cloud", "networking", "security", "tunnel"], - load: () => import("./cloudflared/index").then((m) => m.generate), - }, - { - id: "couchdb", - name: "CouchDB", - version: "latest", - description: - "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", - logo: "couchdb.png", - links: { - github: "https://github.com/apache/couchdb", - website: "https://couchdb.apache.org/", - docs: "https://docs.couchdb.org/en/stable/", - }, - tags: ["database", "storage"], - load: () => import("./couchdb/index").then((m) => m.generate), - }, - { - id: "it-tools", - name: "IT Tools", - version: "latest", - description: "A collection of handy online it-tools for developers.", - logo: "it-tools.svg", - links: { - github: "https://github.com/CorentinTh/it-tools", - website: "https://it-tools.tech", - }, - tags: ["developer", "tools"], - load: () => import("./it-tools/index").then((m) => m.generate), - }, - { - id: "superset", - name: "Superset (Unofficial)", - version: "latest", - description: "Data visualization and data exploration platform.", - logo: "superset.svg", - links: { - github: "https://github.com/amancevice/docker-superset", - website: "https://superset.apache.org", - docs: "https://superset.apache.org/docs/intro", - }, - tags: ["analytics", "bi", "dashboard", "database", "sql"], - load: () => import("./superset/index").then((m) => m.generate), - }, - { - id: "glance", - name: "Glance", - version: "latest", - description: - "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", - logo: "glance.png", - links: { - github: "https://github.com/glanceapp/glance", - docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", - }, - tags: ["dashboard", "monitoring", "widgets", "rss"], - load: () => import("./glance/index").then((m) => m.generate), - }, - { - id: "homarr", - name: "Homarr", - version: "latest", - description: - "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", - logo: "homarr.png", - links: { - github: "https://github.com/homarr-labs/homarr", - docs: "https://homarr.dev/docs/getting-started/installation/docker", - website: "https://homarr.dev/", - }, - tags: ["dashboard", "monitoring"], - load: () => import("./homarr/index").then((m) => m.generate), - }, - { - id: "erpnext", - name: "ERPNext", - version: "version-15", - description: "100% Open Source and highly customizable ERP software.", - logo: "erpnext.svg", - links: { - github: "https://github.com/frappe/erpnext", - docs: "https://docs.frappe.io/erpnext", - website: "https://erpnext.com", - }, - tags: [ - "erp", - "accounts", - "manufacturing", - "retail", - "sales", - "pos", - "hrms", - ], - load: () => import("./erpnext/index").then((m) => m.generate), - }, - { - id: "maybe", - name: "Maybe", - version: "latest", - description: - "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", - logo: "maybe.svg", - links: { - github: "https://github.com/maybe-finance/maybe", - website: "https://maybe.finance/", - docs: "https://docs.maybe.finance/", - }, - tags: ["finance", "self-hosted"], - load: () => import("./maybe/index").then((m) => m.generate), - }, - { - id: "spacedrive", - name: "Spacedrive", - version: "latest", - description: - "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", - links: { - github: "https://github.com/spacedriveapp/spacedrive", - website: "https://spacedrive.com/", - docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", - }, - logo: "spacedrive.png", - tags: ["file-manager", "vdfs", "storage"], - load: () => import("./spacedrive/index").then((m) => m.generate), - }, - { - id: "alist", - name: "AList", - version: "v3.41.0", - description: - "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", - logo: "alist.svg", - links: { - github: "https://github.com/AlistGo/alist", - website: "https://alist.nn.ci", - docs: "https://alist.nn.ci/guide/install/docker.html", - }, - tags: ["file", "webdav", "storage"], - load: () => import("./alist/index").then((m) => m.generate), - }, - { - id: "answer", - name: "Answer", - version: "v1.4.1", - description: - "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", - logo: "answer.png", - links: { - github: "https://github.com/apache/answer", - website: "https://answer.apache.org/", - docs: "https://answer.apache.org/docs", - }, - tags: ["q&a", "self-hosted"], - load: () => import("./answer/index").then((m) => m.generate), - }, - { - id: "shlink", - name: "Shlink", - version: "stable", - description: - "URL shortener that can be used to serve shortened URLs under your own domain.", - logo: "shlink.svg", - links: { - github: "https://github.com/shlinkio/shlink", - website: "https://shlink.io", - docs: "https://shlink.io/documentation", - }, - tags: ["sharing", "shortener", "url"], - load: () => import("./shlink/index").then((m) => m.generate), - }, - { - id: "frappe-hr", - name: "Frappe HR", - version: "version-15", - description: - "Feature rich HR & Payroll software. 100% FOSS and customizable.", - logo: "frappe-hr.svg", - links: { - github: "https://github.com/frappe/hrms", - docs: "https://docs.frappe.io/hr", - website: "https://frappe.io/hr", - }, - tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], - load: () => import("./frappe-hr/index").then((m) => m.generate), - }, - { - id: "formbricks", - name: "Formbricks", - version: "v3.1.3", - description: - "Formbricks is an open-source survey and form platform for collecting user data.", - logo: "formbricks.png", - links: { - github: "https://github.com/formbricks/formbricks", - website: "https://formbricks.com/", - docs: "https://formbricks.com/docs", - }, - tags: ["forms", "analytics"], - load: () => import("./formbricks/index").then((m) => m.generate), - }, - { - id: "trilium", - name: "Trilium", - description: - "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", - logo: "trilium.png", - version: "latest", - links: { - github: "https://github.com/zadam/trilium", - website: "https://github.com/zadam/trilium", - docs: "https://github.com/zadam/trilium/wiki/", - }, - tags: ["self-hosted", "productivity", "personal-use"], - load: () => import("./trilium/index").then((m) => m.generate), - }, - { - id: "convex", - name: "Convex", - version: "latest", - description: - "Convex is an open-source reactive database designed to make life easy for web app developers.", - logo: "convex.svg", - links: { - github: "https://github.com/get-convex/convex", - website: "https://www.convex.dev/", - docs: "https://www.convex.dev/docs", - }, - tags: ["backend", "database", "api"], - load: () => import("./convex/index").then((m) => m.generate), - }, + links: { + github: "https://github.com/nocodb/nocodb", + website: "https://nocodb.com/", + docs: "https://docs.nocodb.com/", + }, + logo: "nocodb.png", + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./nocodb/index").then((m) => m.generate), + }, + { + id: "meilisearch", + name: "Meilisearch", + version: "v1.8.3", + description: + "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", + logo: "meilisearch.png", + links: { + github: "https://github.com/meilisearch/meilisearch", + website: "https://www.meilisearch.com/", + docs: "https://docs.meilisearch.com/", + }, + tags: ["search"], + load: () => import("./meilisearch/index").then((m) => m.generate), + }, + { + id: "phpmyadmin", + name: "Phpmyadmin", + version: "5.2.1", + description: + "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", + logo: "phpmyadmin.png", + links: { + github: "https://github.com/phpmyadmin/phpmyadmin", + website: "https://www.phpmyadmin.net/", + docs: "https://www.phpmyadmin.net/docs/", + }, + tags: ["database"], + load: () => import("./phpmyadmin/index").then((m) => m.generate), + }, + { + id: "rocketchat", + name: "Rocketchat", + version: "6.9.2", + description: + "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", + logo: "rocketchat.png", + links: { + github: "https://github.com/RocketChat/Rocket.Chat", + website: "https://rocket.chat/", + docs: "https://rocket.chat/docs/", + }, + tags: ["chat"], + load: () => import("./rocketchat/index").then((m) => m.generate), + }, + { + id: "minio", + name: "Minio", + description: + "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", + logo: "minio.png", + version: "latest", + links: { + github: "https://github.com/minio/minio", + website: "https://minio.io/", + docs: "https://docs.minio.io/", + }, + tags: ["storage"], + load: () => import("./minio/index").then((m) => m.generate), + }, + { + id: "metabase", + name: "Metabase", + version: "v0.50.8", + description: + "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", + logo: "metabase.png", + links: { + github: "https://github.com/metabase/metabase", + website: "https://www.metabase.com/", + docs: "https://www.metabase.com/docs/", + }, + tags: ["database", "dashboard"], + load: () => import("./metabase/index").then((m) => m.generate), + }, + { + id: "glitchtip", + name: "Glitchtip", + version: "v4.0", + description: "Glitchtip is simple, open source error tracking", + logo: "glitchtip.png", + links: { + github: "https://gitlab.com/glitchtip/", + website: "https://glitchtip.com/", + docs: "https://glitchtip.com/documentation", + }, + tags: ["hosting"], + load: () => import("./glitchtip/index").then((m) => m.generate), + }, + { + id: "open-webui", + name: "Open WebUI", + version: "v0.3.7", + description: + "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", + logo: "open-webui.png", + links: { + github: "https://github.com/open-webui/open-webui", + website: "https://openwebui.com/", + docs: "https://docs.openwebui.com/", + }, + tags: ["chat"], + load: () => import("./open-webui/index").then((m) => m.generate), + }, + { + id: "listmonk", + name: "Listmonk", + version: "v3.0.0", + description: + "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", + logo: "listmonk.png", + links: { + github: "https://github.com/knadh/listmonk", + website: "https://listmonk.app/", + docs: "https://listmonk.app/docs/", + }, + tags: ["email", "newsletter", "mailing-list"], + load: () => import("./listmonk/index").then((m) => m.generate), + }, + { + id: "doublezero", + name: "Double Zero", + version: "v0.2.1", + description: + "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", + logo: "doublezero.svg", + links: { + github: "https://github.com/technomancy-dev/00", + website: "https://www.double-zero.cloud/", + docs: "https://github.com/technomancy-dev/00", + }, + tags: ["email"], + load: () => import("./doublezero/index").then((m) => m.generate), + }, + { + id: "umami", + name: "Umami", + version: "v2.14.0", + description: + "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", + logo: "umami.png", + links: { + github: "https://github.com/umami-software/umami", + website: "https://umami.is", + docs: "https://umami.is/docs", + }, + tags: ["analytics"], + load: () => import("./umami/index").then((m) => m.generate), + }, + { + id: "jellyfin", + name: "jellyfin", + version: "v10.9.7", + description: + "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", + logo: "jellyfin.svg", + links: { + github: "https://github.com/jellyfin/jellyfin", + website: "https://jellyfin.org/", + docs: "https://jellyfin.org/docs/", + }, + tags: ["media system"], + load: () => import("./jellyfin/index").then((m) => m.generate), + }, + { + id: "teable", + name: "teable", + version: "v1.3.1-alpha-build.460", + description: + "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", + logo: "teable.png", + links: { + github: "https://github.com/teableio/teable", + website: "https://teable.io/", + docs: "https://help.teable.io/", + }, + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./teable/index").then((m) => m.generate), + }, + { + id: "zipline", + name: "Zipline", + version: "v3.7.9", + description: + "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", + logo: "zipline.png", + links: { + github: "https://github.com/diced/zipline", + website: "https://zipline.diced.sh/", + docs: "https://zipline.diced.sh/docs/", + }, + tags: ["media system", "storage"], + load: () => import("./zipline/index").then((m) => m.generate), + }, + { + id: "soketi", + name: "Soketi", + version: "v1.6.1-16", + description: + "Soketi is your simple, fast, and resilient open-source WebSockets server.", + logo: "soketi.png", + links: { + github: "https://github.com/soketi/soketi", + website: "https://soketi.app/", + docs: "https://docs.soketi.app/", + }, + tags: ["chat"], + load: () => import("./soketi/index").then((m) => m.generate), + }, + { + id: "aptabase", + name: "Aptabase", + version: "v1.0.0", + description: + "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "aptabase.svg", + links: { + github: "https://github.com/aptabase/aptabase", + website: "https://aptabase.com/", + docs: "https://github.com/aptabase/aptabase/blob/main/README.md", + }, + tags: ["analytics", "self-hosted"], + load: () => import("./aptabase/index").then((m) => m.generate), + }, + { + id: "typebot", + name: "Typebot", + version: "2.27.0", + description: "Typebot is an open-source chatbot builder platform.", + logo: "typebot.svg", + links: { + github: "https://github.com/baptisteArno/typebot.io", + website: "https://typebot.io/", + docs: "https://docs.typebot.io/get-started/introduction", + }, + tags: ["chatbot", "builder", "open-source"], + load: () => import("./typebot/index").then((m) => m.generate), + }, + { + id: "gitea", + name: "Gitea", + version: "1.22.3", + description: + "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", + logo: "gitea.png", + links: { + github: "https://github.com/go-gitea/gitea.git", + website: "https://gitea.com/", + docs: "https://docs.gitea.com/installation/install-with-docker", + }, + tags: ["self-hosted", "storage"], + load: () => import("./gitea/index").then((m) => m.generate), + }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", + }, + tags: ["self-hosted", "email", "webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + }, + { + id: "filebrowser", + name: "File Browser", + version: "2.31.2", + description: + "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", + logo: "filebrowser.svg", + links: { + github: "https://github.com/filebrowser/filebrowser", + website: "https://filebrowser.org/", + docs: "https://filebrowser.org/", + }, + tags: ["file-manager", "storage"], + load: () => import("./filebrowser/index").then((m) => m.generate), + }, + { + id: "tolgee", + name: "Tolgee", + version: "v3.80.4", + description: + "Developer & translator friendly web-based localization platform", + logo: "tolgee.svg", + links: { + github: "https://github.com/tolgee/tolgee-platform", + website: "https://tolgee.io", + docs: "https://tolgee.io/platform", + }, + tags: ["self-hosted", "i18n", "localization", "translations"], + load: () => import("./tolgee/index").then((m) => m.generate), + }, + { + id: "portainer", + name: "Portainer", + version: "2.21.4", + description: + "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", + logo: "portainer.svg", + links: { + github: "https://github.com/portainer/portainer", + website: "https://www.portainer.io/", + docs: "https://docs.portainer.io/", + }, + tags: ["cloud", "monitoring"], + load: () => import("./portainer/index").then((m) => m.generate), + }, + { + id: "influxdb", + name: "InfluxDB", + version: "2.7.10", + description: + "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", + logo: "influxdb.png", + links: { + github: "https://github.com/influxdata/influxdb", + website: "https://www.influxdata.com/", + docs: "https://docs.influxdata.com/influxdb/v2/", + }, + tags: ["self-hosted", "open-source", "storage", "database"], + load: () => import("./influxdb/index").then((m) => m.generate), + }, + { + id: "infisical", + name: "Infisical", + version: "0.90.1", + description: + "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", + logo: "infisical.jpg", + links: { + github: "https://github.com/Infisical/infisical", + website: "https://infisical.com/", + docs: "https://infisical.com/docs/documentation/getting-started/introduction", + }, + tags: ["self-hosted", "open-source"], + load: () => import("./infisical/index").then((m) => m.generate), + }, + { + id: "docmost", + name: "Docmost", + version: "0.4.1", + description: + "Docmost, is an open-source collaborative wiki and documentation software.", + logo: "docmost.png", + links: { + github: "https://github.com/docmost/docmost", + website: "https://docmost.com/", + docs: "https://docmost.com/docs/", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./docmost/index").then((m) => m.generate), + }, + { + id: "vaultwarden", + name: "Vaultwarden", + version: "1.32.7", + description: + "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", + logo: "vaultwarden.svg", + links: { + github: "https://github.com/dani-garcia/vaultwarden", + website: "", + docs: "https://github.com/dani-garcia/vaultwarden/wiki", + }, + tags: ["open-source"], + load: () => import("./vaultwarden/index").then((m) => m.generate), + }, + { + id: "hi-events", + name: "Hi.events", + version: "0.8.0-beta.1", + description: + "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", + logo: "hi-events.svg", + links: { + github: "https://github.com/HiEventsDev/hi.events", + website: "https://hi.events/", + docs: "https://hi.events/docs", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./hi-events/index").then((m) => m.generate), + }, + { + id: "windows", + name: "Windows (dockerized)", + version: "4.00", + description: "Windows inside a Docker container.", + logo: "windows.png", + links: { + github: "https://github.com/dockur/windows", + website: "", + docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./windows/index").then((m) => m.generate), + }, + { + id: "macos", + name: "MacOS (dockerized)", + version: "1.14", + description: "MacOS inside a Docker container.", + logo: "macos.png", + links: { + github: "https://github.com/dockur/macos", + website: "", + docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./macos/index").then((m) => m.generate), + }, + { + id: "coder", + name: "Coder", + version: "2.15.3", + description: + "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", + logo: "coder.svg", + links: { + github: "https://github.com/coder/coder", + website: "https://coder.com/", + docs: "https://coder.com/docs", + }, + tags: ["self-hosted", "open-source", "builder"], + load: () => import("./coder/index").then((m) => m.generate), + }, + { + id: "stirling", + name: "Stirling PDF", + version: "0.30.1", + description: "A locally hosted one-stop shop for all your PDF needs", + logo: "stirling.svg", + links: { + github: "https://github.com/Stirling-Tools/Stirling-PDF", + website: "https://www.stirlingpdf.com/", + docs: "https://docs.stirlingpdf.com/", + }, + tags: ["pdf", "tools"], + load: () => import("./stirling/index").then((m) => m.generate), + }, + { + id: "lobe-chat", + name: "Lobe Chat", + version: "v1.26.1", + description: "Lobe Chat - an open-source, modern-design AI chat framework.", + logo: "lobe-chat.png", + links: { + github: "https://github.com/lobehub/lobe-chat", + website: "https://chat-preview.lobehub.com/", + docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", + }, + tags: ["IA", "chat"], + load: () => import("./lobe-chat/index").then((m) => m.generate), + }, + { + id: "peppermint", + name: "Peppermint", + version: "latest", + description: + "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", + logo: "peppermint.svg", + links: { + github: "https://github.com/Peppermint-Lab/peppermint", + website: "https://peppermint.sh/", + docs: "https://docs.peppermint.sh/", + }, + tags: ["api", "development", "documentation"], + load: () => import("./peppermint/index").then((m) => m.generate), + }, + { + id: "windmill", + name: "Windmill", + version: "latest", + description: + "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", + logo: "windmill.svg", + links: { + github: "https://github.com/windmill-labs/windmill", + website: "https://www.windmill.dev/", + docs: "https://docs.windmill.dev/", + }, + tags: ["workflow", "automation", "development"], + load: () => import("./windmill/index").then((m) => m.generate), + }, + { + id: "activepieces", + name: "Activepieces", + version: "0.35.0", + description: + "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", + logo: "activepieces.svg", + links: { + github: "https://github.com/activepieces/activepieces", + website: "https://www.activepieces.com/", + docs: "https://www.activepieces.com/docs", + }, + tags: ["automation", "workflow", "no-code"], + load: () => import("./activepieces/index").then((m) => m.generate), + }, + { + id: "invoiceshelf", + name: "InvoiceShelf", + version: "latest", + description: + "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", + logo: "invoiceshelf.png", + links: { + github: "https://github.com/InvoiceShelf/invoiceshelf", + website: "https://invoiceshelf.com", + docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./invoiceshelf/index").then((m) => m.generate), + }, + { + id: "postiz", + name: "Postiz", + version: "latest", + description: + "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", + logo: "postiz.png", + links: { + github: "https://github.com/gitroomhq/postiz", + website: "https://postiz.com", + docs: "https://docs.postiz.com", + }, + tags: ["cms", "content-management", "publishing"], + load: () => import("./postiz/index").then((m) => m.generate), + }, + { + id: "slash", + name: "Slash", + version: "latest", + description: + "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", + logo: "slash.png", + links: { + github: "https://github.com/yourselfhosted/slash", + website: "https://github.com/yourselfhosted/slash#readme", + docs: "https://github.com/yourselfhosted/slash/wiki", + }, + tags: ["bookmarks", "link-shortener", "self-hosted"], + load: () => import("./slash/index").then((m) => m.generate), + }, + { + id: "discord-tickets", + name: "Discord Tickets", + version: "4.0.21", + description: + "An open-source Discord bot for creating and managing support ticket channels.", + logo: "discord-tickets.png", + links: { + github: "https://github.com/discord-tickets/bot", + website: "https://discordtickets.app", + docs: "https://discordtickets.app/self-hosting/installation/docker/", + }, + tags: ["discord", "tickets", "support"], + load: () => import("./discord-tickets/index").then((m) => m.generate), + }, + { + id: "nextcloud-aio", + name: "Nextcloud All in One", + version: "30.0.2", + description: + "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", + logo: "nextcloud-aio.svg", + links: { + github: "https://github.com/nextcloud/docker", + website: "https://nextcloud.com/", + docs: "https://docs.nextcloud.com/", + }, + tags: ["file-manager", "sync"], + load: () => import("./nextcloud-aio/index").then((m) => m.generate), + }, + { + id: "blender", + name: "Blender", + version: "latest", + description: + "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + logo: "blender.svg", + links: { + github: "https://github.com/linuxserver/docker-blender", + website: "https://www.blender.org/", + docs: "https://docs.blender.org/", + }, + tags: ["3d", "rendering", "animation"], + load: () => import("./blender/index").then((m) => m.generate), + }, + { + id: "heyform", + name: "HeyForm", + version: "latest", + description: + "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", + logo: "heyform.svg", + links: { + github: "https://github.com/heyform/heyform", + website: "https://heyform.net", + docs: "https://docs.heyform.net", + }, + tags: ["form", "builder", "questionnaire", "quiz", "survey"], + load: () => import("./heyform/index").then((m) => m.generate), + }, + { + id: "chatwoot", + name: "Chatwoot", + version: "v3.14.1", + description: + "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", + logo: "chatwoot.svg", + links: { + github: "https://github.com/chatwoot/chatwoot", + website: "https://www.chatwoot.com", + docs: "https://www.chatwoot.com/docs", + }, + tags: ["support", "chat", "customer-service"], + load: () => import("./chatwoot/index").then((m) => m.generate), + }, + { + id: "discourse", + name: "Discourse", + version: "3.3.2", + description: + "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", + logo: "discourse.svg", + links: { + github: "https://github.com/discourse/discourse", + website: "https://www.discourse.org/", + docs: "https://meta.discourse.org/", + }, + tags: ["forum", "community", "discussion"], + load: () => import("./discourse/index").then((m) => m.generate), + }, + { + id: "immich", + name: "Immich", + version: "v1.121.0", + description: + "High performance self-hosted photo and video backup solution directly from your mobile phone.", + logo: "immich.svg", + links: { + github: "https://github.com/immich-app/immich", + website: "https://immich.app/", + docs: "https://immich.app/docs/overview/introduction", + }, + tags: ["photos", "videos", "backup", "media"], + load: () => import("./immich/index").then((m) => m.generate), + }, + { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { + id: "yourls", + name: "YOURLS", + version: "1.9.2", + description: + "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", + logo: "yourls.svg", + links: { + github: "https://github.com/YOURLS/YOURLS", + website: "https://yourls.org/", + docs: "https://yourls.org/#documentation", + }, + tags: ["url-shortener", "php"], + load: () => import("./yourls/index").then((m) => m.generate), + }, + { + id: "ryot", + name: "Ryot", + version: "v7.10", + description: + "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", + logo: "ryot.png", + links: { + github: "https://github.com/IgnisDa/ryot", + website: "https://ryot.io/", + docs: "https://docs.ryot.io/", + }, + tags: ["media", "tracking", "self-hosted"], + load: () => import("./ryot/index").then((m) => m.generate), + }, + { + id: "photoprism", + name: "Photoprism", + version: "latest", + description: + "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + logo: "photoprism.svg", + links: { + github: "https://github.com/photoprism/photoprism", + website: "https://www.photoprism.app/", + docs: "https://docs.photoprism.app/", + }, + tags: ["media", "photos", "self-hosted"], + load: () => import("./photoprism/index").then((m) => m.generate), + }, + { + id: "ontime", + name: "Ontime", + version: "v3.8.0", + description: + "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", + logo: "ontime.png", + links: { + github: "https://github.com/cpvalente/ontime/", + website: "https://getontime.no", + docs: "https://docs.getontime.no", + }, + tags: ["event"], + load: () => import("./ontime/index").then((m) => m.generate), + }, + { + id: "triggerdotdev", + name: "Trigger.dev", + version: "v3", + description: + "Trigger is a platform for building event-driven applications.", + logo: "triggerdotdev.svg", + links: { + github: "https://github.com/triggerdotdev/trigger.dev", + website: "https://trigger.dev/", + docs: "https://trigger.dev/docs", + }, + tags: ["event-driven", "applications"], + load: () => import("./triggerdotdev/index").then((m) => m.generate), + }, + { + id: "browserless", + name: "Browserless", + version: "2.23.0", + description: + "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", + logo: "browserless.svg", + links: { + github: "https://github.com/browserless/browserless", + website: "https://www.browserless.io/", + docs: "https://docs.browserless.io/", + }, + tags: ["browser", "automation"], + load: () => import("./browserless/index").then((m) => m.generate), + }, + { + id: "drawio", + name: "draw.io", + version: "24.7.17", + description: + "draw.io is a configurable diagramming/whiteboarding visualization application.", + logo: "drawio.svg", + links: { + github: "https://github.com/jgraph/drawio", + website: "https://draw.io/", + docs: "https://www.drawio.com/doc/", + }, + tags: ["drawing", "diagrams"], + load: () => import("./drawio/index").then((m) => m.generate), + }, + { + id: "kimai", + name: "Kimai", + version: "2.26.0", + description: + "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", + logo: "kimai.svg", + links: { + github: "https://github.com/kimai/kimai", + website: "https://www.kimai.org", + docs: "https://www.kimai.org/documentation", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./kimai/index").then((m) => m.generate), + }, + { + id: "logto", + name: "Logto", + version: "1.22.0", + description: + "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", + logo: "logto.png", + links: { + github: "https://github.com/logto-io/logto", + website: "https://logto.io/", + docs: "https://docs.logto.io/introduction", + }, + tags: ["identity", "auth"], + load: () => import("./logto/index").then((m) => m.generate), + }, + { + id: "penpot", + name: "Penpot", + version: "2.3.2", + description: + "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", + logo: "penpot.svg", + links: { + github: "https://github.com/penpot/penpot", + website: "https://penpot.app/", + docs: "https://docs.penpot.app/", + }, + tags: ["design", "collaboration"], + load: () => import("./penpot/index").then((m) => m.generate), + }, + { + id: "huly", + name: "Huly", + version: "0.6.377", + description: + "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", + logo: "huly.svg", + links: { + github: "https://github.com/hcengineering/huly-selfhost", + website: "https://huly.io/", + docs: "https://docs.huly.io/", + }, + tags: ["project-management", "community", "discussion"], + load: () => import("./huly/index").then((m) => m.generate), + }, + { + id: "unsend", + name: "Unsend", + version: "v1.3.2", + description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", + logo: "unsend.png", + links: { + github: "https://github.com/unsend-dev/unsend", + website: "https://unsend.dev/", + docs: "https://docs.unsend.dev/get-started/", + }, + tags: ["e-mail", "marketing", "business"], + load: () => import("./unsend/index").then((m) => m.generate), + }, + { + id: "langflow", + name: "Langflow", + version: "1.1.1", + description: + "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", + logo: "langflow.svg", + links: { + github: "https://github.com/langflow-ai/langflow/tree/main", + website: "https://www.langflow.org/", + docs: "https://docs.langflow.org/", + }, + tags: ["ai"], + load: () => import("./langflow/index").then((m) => m.generate), + }, + { + id: "elastic-search", + name: "Elasticsearch", + version: "8.10.2", + description: + "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", + logo: "elasticsearch.svg", + links: { + github: "https://github.com/elastic/elasticsearch", + website: "https://www.elastic.co/elasticsearch/", + docs: "https://docs.elastic.co/elasticsearch/", + }, + tags: ["search", "analytics"], + load: () => import("./elastic-search/index").then((m) => m.generate), + }, + { + id: "onedev", + name: "OneDev", + version: "11.6.6", + description: + "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", + logo: "onedev.png", + links: { + github: "https://github.com/theonedev/onedev/", + website: "https://onedev.io/", + docs: "https://docs.onedev.io/", + }, + tags: ["self-hosted", "development"], + load: () => import("./onedev/index").then((m) => m.generate), + }, + { + id: "unifi", + name: "Unifi Network", + version: "11.6.6", + description: + "Unifi Network is an open-source enterprise network management platform for wireless networks.", + logo: "unifi.webp", + links: { + github: "https://github.com/ubiquiti", + website: "https://www.ui.com/", + docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", + }, + tags: ["self-hosted", "networking"], + load: () => import("./unifi/index").then((m) => m.generate), + }, + { + id: "glpi", + name: "GLPI Project", + version: "10.0.16", + description: "The most complete open source service management software", + logo: "glpi.webp", + links: { + github: "https://github.com/glpi-project/glpi", + website: "https://glpi-project.org/", + docs: "https://glpi-project.org/documentation/", + }, + tags: ["self-hosted", "project-management", "management"], + load: () => import("./glpi/index").then((m) => m.generate), + }, + { + id: "checkmate", + name: "Checkmate", + version: "2.0.1", + description: + "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", + logo: "checkmate.png", + links: { + github: "https://github.com/bluewave-labs/checkmate", + website: "https://bluewavelabs.ca", + docs: "https://bluewavelabs.gitbook.io/checkmate", + }, + tags: ["self-hosted", "monitoring", "uptime"], + load: () => import("./checkmate/index").then((m) => m.generate), + }, + { + id: "gotenberg", + name: "Gotenberg", + version: "latest", + description: "Gotenberg is a Docker-powered stateless API for PDF files.", + logo: "gotenberg.png", + links: { + github: "https://github.com/gotenberg/gotenberg", + website: "https://gotenberg.dev", + docs: "https://gotenberg.dev/docs/getting-started/introduction", + }, + tags: ["api", "backend", "pdf", "tools"], + load: () => import("./gotenberg/index").then((m) => m.generate), + }, + { + id: "actualbudget", + name: "Actual Budget", + version: "latest", + description: + "A super fast and privacy-focused app for managing your finances.", + logo: "actualbudget.png", + links: { + github: "https://github.com/actualbudget/actual", + website: "https://actualbudget.org", + docs: "https://actualbudget.org/docs", + }, + tags: ["budgeting", "finance", "money"], + load: () => import("./actualbudget/index").then((m) => m.generate), + }, + { + id: "conduit", + name: "Conduit", + version: "v0.9.0", + description: + "Conduit is a simple, fast and reliable chat server powered by Matrix", + logo: "conduit.svg", + links: { + github: "https://gitlab.com/famedly/conduit", + website: "https://conduit.rs/", + docs: "https://docs.conduit.rs/", + }, + tags: ["matrix", "communication"], + load: () => import("./conduit/index").then((m) => m.generate), + }, + { + id: "evolutionapi", + name: "Evolution API", + version: "v2.1.2", + description: + "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", + logo: "evolutionapi.png", + links: { + github: "https://github.com/EvolutionAPI/evolution-api", + docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", + website: "https://evolution-api.com/opensource-whatsapp-api/", + }, + tags: ["api", "whatsapp", "messaging"], + load: () => import("./evolutionapi/index").then((m) => m.generate), + }, + { + id: "conduwuit", + name: "Conduwuit", + version: "latest", + description: + "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", + logo: "conduwuit.svg", + links: { + github: "https://github.com/girlbossceo/conduwuit", + website: "https://conduwuit.puppyirl.gay", + docs: "https://conduwuit.puppyirl.gay/configuration.html", + }, + tags: ["backend", "chat", "communication", "matrix", "server"], + load: () => import("./conduwuit/index").then((m) => m.generate), + }, + { + id: "cloudflared", + name: "Cloudflared", + version: "latest", + description: + "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", + logo: "cloudflared.svg", + links: { + github: "https://github.com/cloudflare/cloudflared", + website: + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", + docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", + }, + tags: ["cloud", "networking", "security", "tunnel"], + load: () => import("./cloudflared/index").then((m) => m.generate), + }, + { + id: "couchdb", + name: "CouchDB", + version: "latest", + description: + "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", + logo: "couchdb.png", + links: { + github: "https://github.com/apache/couchdb", + website: "https://couchdb.apache.org/", + docs: "https://docs.couchdb.org/en/stable/", + }, + tags: ["database", "storage"], + load: () => import("./couchdb/index").then((m) => m.generate), + }, + { + id: "it-tools", + name: "IT Tools", + version: "latest", + description: "A collection of handy online it-tools for developers.", + logo: "it-tools.svg", + links: { + github: "https://github.com/CorentinTh/it-tools", + website: "https://it-tools.tech", + }, + tags: ["developer", "tools"], + load: () => import("./it-tools/index").then((m) => m.generate), + }, + { + id: "superset", + name: "Superset (Unofficial)", + version: "latest", + description: "Data visualization and data exploration platform.", + logo: "superset.svg", + links: { + github: "https://github.com/amancevice/docker-superset", + website: "https://superset.apache.org", + docs: "https://superset.apache.org/docs/intro", + }, + tags: ["analytics", "bi", "dashboard", "database", "sql"], + load: () => import("./superset/index").then((m) => m.generate), + }, + { + id: "glance", + name: "Glance", + version: "latest", + description: + "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", + logo: "glance.png", + links: { + github: "https://github.com/glanceapp/glance", + docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", + }, + tags: ["dashboard", "monitoring", "widgets", "rss"], + load: () => import("./glance/index").then((m) => m.generate), + }, + { + id: "homarr", + name: "Homarr", + version: "latest", + description: + "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", + logo: "homarr.png", + links: { + github: "https://github.com/homarr-labs/homarr", + docs: "https://homarr.dev/docs/getting-started/installation/docker", + website: "https://homarr.dev/", + }, + tags: ["dashboard", "monitoring"], + load: () => import("./homarr/index").then((m) => m.generate), + }, + { + id: "erpnext", + name: "ERPNext", + version: "version-15", + description: "100% Open Source and highly customizable ERP software.", + logo: "erpnext.svg", + links: { + github: "https://github.com/frappe/erpnext", + docs: "https://docs.frappe.io/erpnext", + website: "https://erpnext.com", + }, + tags: [ + "erp", + "accounts", + "manufacturing", + "retail", + "sales", + "pos", + "hrms", + ], + load: () => import("./erpnext/index").then((m) => m.generate), + }, + { + id: "maybe", + name: "Maybe", + version: "latest", + description: + "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", + logo: "maybe.svg", + links: { + github: "https://github.com/maybe-finance/maybe", + website: "https://maybe.finance/", + docs: "https://docs.maybe.finance/", + }, + tags: ["finance", "self-hosted"], + load: () => import("./maybe/index").then((m) => m.generate), + }, + { + id: "spacedrive", + name: "Spacedrive", + version: "latest", + description: + "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", + links: { + github: "https://github.com/spacedriveapp/spacedrive", + website: "https://spacedrive.com/", + docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", + }, + logo: "spacedrive.png", + tags: ["file-manager", "vdfs", "storage"], + load: () => import("./spacedrive/index").then((m) => m.generate), + }, + { + id: "alist", + name: "AList", + version: "v3.41.0", + description: + "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", + logo: "alist.svg", + links: { + github: "https://github.com/AlistGo/alist", + website: "https://alist.nn.ci", + docs: "https://alist.nn.ci/guide/install/docker.html", + }, + tags: ["file", "webdav", "storage"], + load: () => import("./alist/index").then((m) => m.generate), + }, + { + id: "answer", + name: "Answer", + version: "v1.4.1", + description: + "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", + logo: "answer.png", + links: { + github: "https://github.com/apache/answer", + website: "https://answer.apache.org/", + docs: "https://answer.apache.org/docs", + }, + tags: ["q&a", "self-hosted"], + load: () => import("./answer/index").then((m) => m.generate), + }, + { + id: "shlink", + name: "Shlink", + version: "stable", + description: + "URL shortener that can be used to serve shortened URLs under your own domain.", + logo: "shlink.svg", + links: { + github: "https://github.com/shlinkio/shlink", + website: "https://shlink.io", + docs: "https://shlink.io/documentation", + }, + tags: ["sharing", "shortener", "url"], + load: () => import("./shlink/index").then((m) => m.generate), + }, + { + id: "frappe-hr", + name: "Frappe HR", + version: "version-15", + description: + "Feature rich HR & Payroll software. 100% FOSS and customizable.", + logo: "frappe-hr.svg", + links: { + github: "https://github.com/frappe/hrms", + docs: "https://docs.frappe.io/hr", + website: "https://frappe.io/hr", + }, + tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], + load: () => import("./frappe-hr/index").then((m) => m.generate), + }, + { + id: "formbricks", + name: "Formbricks", + version: "v3.1.3", + description: + "Formbricks is an open-source survey and form platform for collecting user data.", + logo: "formbricks.png", + links: { + github: "https://github.com/formbricks/formbricks", + website: "https://formbricks.com/", + docs: "https://formbricks.com/docs", + }, + tags: ["forms", "analytics"], + load: () => import("./formbricks/index").then((m) => m.generate), + }, + { + id: "trilium", + name: "Trilium", + description: + "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", + logo: "trilium.png", + version: "latest", + links: { + github: "https://github.com/zadam/trilium", + website: "https://github.com/zadam/trilium", + docs: "https://github.com/zadam/trilium/wiki/", + }, + tags: ["self-hosted", "productivity", "personal-use"], + load: () => import("./trilium/index").then((m) => m.generate), + }, + { + id: "convex", + name: "Convex", + version: "latest", + description: + "Convex is an open-source reactive database designed to make life easy for web app developers.", + logo: "convex.svg", + links: { + github: "https://github.com/get-convex/convex", + website: "https://www.convex.dev/", + docs: "https://www.convex.dev/docs", + }, + tags: ["backend", "database", "api"], + load: () => import("./convex/index").then((m) => m.generate), + }, ]; From 23b59076b8546341571f526bb2f42cdd9489f234 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 13:35:25 -0600 Subject: [PATCH 036/126] refactor: add missing path option --- apps/dokploy/templates/utils/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/templates/utils/index.ts b/apps/dokploy/templates/utils/index.ts index b5369b916..941afc806 100644 --- a/apps/dokploy/templates/utils/index.ts +++ b/apps/dokploy/templates/utils/index.ts @@ -12,7 +12,9 @@ export interface Schema { projectName: string; } -export type DomainSchema = Pick; +export type DomainSchema = Pick & { + path?: string; +}; export interface Template { envs?: string[]; From 871931b460b17b2f984120ba67e44fa59889eb02 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 14:23:54 -0600 Subject: [PATCH 037/126] fix: handle race condition to catch recreation base containers --- packages/server/src/setup/postgres-setup.ts | 10 ++++++++-- packages/server/src/setup/redis-setup.ts | 9 ++++++++- packages/server/src/setup/traefik-setup.ts | 9 ++++++++- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/server/src/setup/postgres-setup.ts b/packages/server/src/setup/postgres-setup.ts index b5794c2b2..8e11e8cc9 100644 --- a/packages/server/src/setup/postgres-setup.ts +++ b/packages/server/src/setup/postgres-setup.ts @@ -54,10 +54,16 @@ export const initializePostgres = async () => { version: Number.parseInt(inspect.Version.Index), ...settings, }); - console.log("Postgres Started ✅"); } catch (error) { - await docker.createService(settings); + try { + await docker.createService(settings); + } catch (error: any) { + if (error?.statusCode !== 409) { + throw error; + } + console.log("Postgres service already exists, continuing..."); + } console.log("Postgres Not Found: Starting ✅"); } }; diff --git a/packages/server/src/setup/redis-setup.ts b/packages/server/src/setup/redis-setup.ts index 1c3b545a5..0a191d11b 100644 --- a/packages/server/src/setup/redis-setup.ts +++ b/packages/server/src/setup/redis-setup.ts @@ -53,7 +53,14 @@ export const initializeRedis = async () => { }); console.log("Redis Started ✅"); } catch (error) { - await docker.createService(settings); + try { + await docker.createService(settings); + } catch (error: any) { + if (error?.statusCode !== 409) { + throw error; + } + console.log("Redis service already exists, continuing..."); + } console.log("Redis Not Found: Starting ✅"); } }; diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 1d60e577c..21caa5cf2 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -128,7 +128,14 @@ export const initializeTraefik = async ({ console.log("Traefik Started ✅"); } catch (error) { - await docker.createService(settings); + try { + await docker.createService(settings); + } catch (error: any) { + if (error?.statusCode !== 409) { + throw error; + } + console.log("Traefik service already exists, continuing..."); + } console.log("Traefik Not Found: Starting ✅"); } }; From 450302b2c27ce03d99335ff69a675a064ae2d2bd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 15:23:33 -0600 Subject: [PATCH 038/126] fix: inherit security & redirects from application --- packages/server/src/utils/traefik/domain.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/packages/server/src/utils/traefik/domain.ts b/packages/server/src/utils/traefik/domain.ts index a6c878e72..1ae3c05a1 100644 --- a/packages/server/src/utils/traefik/domain.ts +++ b/packages/server/src/utils/traefik/domain.ts @@ -122,13 +122,25 @@ export const createRouterConfig = async ( if ((entryPoint === "websecure" && https) || !https) { // redirects for (const redirect of redirects) { - const middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`; + let middlewareName = `redirect-${appName}-${redirect.uniqueConfigKey}`; + if (domain.domainType === "preview") { + middlewareName = `redirect-${appName.replace( + /^preview-(.+)-[^-]+$/, + "$1", + )}-${redirect.uniqueConfigKey}`; + } routerConfig.middlewares?.push(middlewareName); } // security if (security.length > 0) { - const middlewareName = `auth-${appName}`; + let middlewareName = `auth-${appName}`; + if (domain.domainType === "preview") { + middlewareName = `auth-${appName.replace( + /^preview-(.+)-[^-]+$/, + "$1", + )}`; + } routerConfig.middlewares?.push(middlewareName); } } From 0baf9d8962c3f331338a02198de32e4157ced48d Mon Sep 17 00:00:00 2001 From: Krobys <> Date: Sat, 15 Feb 2025 21:46:18 +0000 Subject: [PATCH 039/126] feat(template): add Appwrite --- apps/dokploy/public/templates/appwrite.svg | 9 + .../templates/appwrite/docker-compose.yml | 887 ++++++++++++++++++ apps/dokploy/templates/appwrite/index.ts | 153 +++ apps/dokploy/templates/templates.ts | 16 + 4 files changed, 1065 insertions(+) create mode 100644 apps/dokploy/public/templates/appwrite.svg create mode 100644 apps/dokploy/templates/appwrite/docker-compose.yml create mode 100644 apps/dokploy/templates/appwrite/index.ts diff --git a/apps/dokploy/public/templates/appwrite.svg b/apps/dokploy/public/templates/appwrite.svg new file mode 100644 index 000000000..2034a812a --- /dev/null +++ b/apps/dokploy/public/templates/appwrite.svg @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/appwrite/docker-compose.yml b/apps/dokploy/templates/appwrite/docker-compose.yml new file mode 100644 index 000000000..163cb3d03 --- /dev/null +++ b/apps/dokploy/templates/appwrite/docker-compose.yml @@ -0,0 +1,887 @@ +version: "3.8" + +x-logging: &x-logging + logging: + driver: "json-file" + options: + max-file: "5" + max-size: "10m" + +services: + appwrite: + image: appwrite/appwrite:1.6.0 + container_name: appwrite + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + labels: + - traefik.enable=true + - traefik.constraint-label-stack=appwrite + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-cache:/storage/cache:rw + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + - appwrite-functions:/storage/functions:rw + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_LOCALE + - _APP_CONSOLE_WHITELIST_ROOT + - _APP_CONSOLE_WHITELIST_EMAILS + - _APP_CONSOLE_SESSION_ALERTS + - _APP_CONSOLE_WHITELIST_IPS + - _APP_CONSOLE_HOSTNAMES + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_EMAIL_SECURITY + - _APP_SYSTEM_RESPONSE_FORMAT + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_USAGE_STATS + - _APP_STORAGE_LIMIT + - _APP_STORAGE_PREVIEW_LIMIT + - _APP_STORAGE_ANTIVIRUS + - _APP_STORAGE_ANTIVIRUS_HOST + - _APP_STORAGE_ANTIVIRUS_PORT + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_FUNCTIONS_SIZE_LIMIT + - _APP_FUNCTIONS_TIMEOUT + - _APP_FUNCTIONS_BUILD_TIMEOUT + - _APP_FUNCTIONS_CPUS + - _APP_FUNCTIONS_MEMORY + - _APP_FUNCTIONS_RUNTIMES + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_LOGGING_CONFIG + - _APP_MAINTENANCE_INTERVAL + - _APP_MAINTENANCE_DELAY + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_MAINTENANCE_RETENTION_CACHE + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY + - _APP_MAINTENANCE_RETENTION_SCHEDULES + - _APP_SMS_PROVIDER + - _APP_SMS_FROM + - _APP_GRAPHQL_MAX_BATCH_SIZE + - _APP_GRAPHQL_MAX_COMPLEXITY + - _APP_GRAPHQL_MAX_DEPTH + - _APP_VCS_GITHUB_APP_NAME + - _APP_VCS_GITHUB_PRIVATE_KEY + - _APP_VCS_GITHUB_APP_ID + - _APP_VCS_GITHUB_WEBHOOK_SECRET + - _APP_VCS_GITHUB_CLIENT_SECRET + - _APP_VCS_GITHUB_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + - _APP_ASSISTANT_OPENAI_API_KEY + + appwrite-console: + image: appwrite/console:5.0.12 + container_name: appwrite-console + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_LOCALE + - _APP_CONSOLE_WHITELIST_ROOT + - _APP_CONSOLE_WHITELIST_EMAILS + - _APP_CONSOLE_SESSION_ALERTS + - _APP_CONSOLE_WHITELIST_IPS + - _APP_CONSOLE_HOSTNAMES + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_EMAIL_SECURITY + - _APP_SYSTEM_RESPONSE_FORMAT + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_USAGE_STATS + - _APP_STORAGE_LIMIT + - _APP_STORAGE_PREVIEW_LIMIT + - _APP_STORAGE_ANTIVIRUS + - _APP_STORAGE_ANTIVIRUS_HOST + - _APP_STORAGE_ANTIVIRUS_PORT + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + + appwrite-realtime: + image: appwrite/appwrite:1.6.0 + entrypoint: realtime + container_name: appwrite-realtime + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - mariadb + - redis + labels: + - "traefik.enable=true" + - "traefik.constraint-label-stack=appwrite" + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + + appwrite-worker-audits: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-audits + <<: *x-logging + container_name: appwrite-worker-audits + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-webhooks: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-webhooks + <<: *x-logging + container_name: appwrite-worker-webhooks + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_EMAIL_SECURITY + - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-deletes: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-deletes + <<: *x-logging + container_name: appwrite-worker-deletes + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + volumes: + - appwrite-uploads:/storage/uploads:rw + - appwrite-cache:/storage/cache:rw + - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw + - appwrite-certificates:/storage/certificates:rw + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + - _APP_LOGGING_CONFIG + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_EXECUTION + + appwrite-worker-databases: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-databases + <<: *x-logging + container_name: appwrite-worker-databases + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-builds: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-builds + <<: *x-logging + container_name: appwrite-worker-builds + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + volumes: + - appwrite-functions:/storage/functions:rw + - appwrite-builds:/storage/builds:rw + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_VCS_GITHUB_APP_NAME + - _APP_VCS_GITHUB_PRIVATE_KEY + - _APP_VCS_GITHUB_APP_ID + - _APP_FUNCTIONS_TIMEOUT + - _APP_FUNCTIONS_BUILD_TIMEOUT + - _APP_FUNCTIONS_CPUS + - _APP_FUNCTIONS_MEMORY + - _APP_FUNCTIONS_SIZE_LIMIT + - _APP_OPTIONS_FORCE_HTTPS + - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS + - _APP_DOMAIN + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + + appwrite-worker-certificates: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-certificates + <<: *x-logging + container_name: appwrite-worker-certificates + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + volumes: + - appwrite-config:/storage/config:rw + - appwrite-certificates:/storage/certificates:rw + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_EMAIL_CERTIFICATES + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + + appwrite-worker-functions: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-functions + <<: *x-logging + container_name: appwrite-worker-functions + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + - openruntimes-executor + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_FUNCTIONS_TIMEOUT + - _APP_FUNCTIONS_BUILD_TIMEOUT + - _APP_FUNCTIONS_CPUS + - _APP_FUNCTIONS_MEMORY + - _APP_EXECUTOR_SECRET + - _APP_EXECUTOR_HOST + - _APP_USAGE_STATS + - _APP_DOCKER_HUB_USERNAME + - _APP_DOCKER_HUB_PASSWORD + - _APP_LOGGING_CONFIG + + appwrite-worker-mails: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-mails + <<: *x-logging + container_name: appwrite-worker-mails + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_SYSTEM_EMAIL_NAME + - _APP_SYSTEM_EMAIL_ADDRESS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_SMTP_HOST + - _APP_SMTP_PORT + - _APP_SMTP_SECURE + - _APP_SMTP_USERNAME + - _APP_SMTP_PASSWORD + - _APP_LOGGING_CONFIG + + appwrite-worker-messaging: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-messaging + container_name: appwrite-worker-messaging + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + volumes: + - appwrite-uploads:/storage/uploads:rw + depends_on: + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_SMS_FROM + - _APP_SMS_PROVIDER + - _APP_STORAGE_DEVICE + - _APP_STORAGE_S3_ACCESS_KEY + - _APP_STORAGE_S3_SECRET + - _APP_STORAGE_S3_REGION + - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_DO_SPACES_ACCESS_KEY + - _APP_STORAGE_DO_SPACES_SECRET + - _APP_STORAGE_DO_SPACES_REGION + - _APP_STORAGE_DO_SPACES_BUCKET + - _APP_STORAGE_BACKBLAZE_ACCESS_KEY + - _APP_STORAGE_BACKBLAZE_SECRET + - _APP_STORAGE_BACKBLAZE_REGION + - _APP_STORAGE_BACKBLAZE_BUCKET + - _APP_STORAGE_LINODE_ACCESS_KEY + - _APP_STORAGE_LINODE_SECRET + - _APP_STORAGE_LINODE_REGION + - _APP_STORAGE_LINODE_BUCKET + - _APP_STORAGE_WASABI_ACCESS_KEY + - _APP_STORAGE_WASABI_SECRET + - _APP_STORAGE_WASABI_REGION + - _APP_STORAGE_WASABI_BUCKET + + appwrite-worker-migrations: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-migrations + <<: *x-logging + container_name: appwrite-worker-migrations + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_EMAIL_SECURITY + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_LOGGING_CONFIG + - _APP_MIGRATIONS_FIREBASE_CLIENT_ID + - _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET + + appwrite-task-maintenance: + image: appwrite/appwrite:1.6.0 + entrypoint: maintenance + <<: *x-logging + container_name: appwrite-task-maintenance + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_DOMAIN + - _APP_DOMAIN_TARGET + - _APP_DOMAIN_FUNCTIONS + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_MAINTENANCE_INTERVAL + - _APP_MAINTENANCE_RETENTION_EXECUTION + - _APP_MAINTENANCE_RETENTION_CACHE + - _APP_MAINTENANCE_RETENTION_ABUSE + - _APP_MAINTENANCE_RETENTION_AUDIT + - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY + - _APP_MAINTENANCE_RETENTION_SCHEDULES + + appwrite-worker-usage: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-usage + container_name: appwrite-worker-usage + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + + appwrite-worker-usage-dump: + image: appwrite/appwrite:1.6.0 + entrypoint: worker-usage-dump + container_name: appwrite-worker-usage-dump + <<: *x-logging + networks: + - dokploy-network + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + + appwrite-task-scheduler-functions: + image: appwrite/appwrite:1.6.0 + entrypoint: schedule-functions + container_name: appwrite-task-scheduler-functions + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-task-scheduler-executions: + image: appwrite/appwrite:1.6.0 + entrypoint: schedule-executions + container_name: appwrite-task-scheduler-executions + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-task-scheduler-messages: + image: appwrite/appwrite:1.6.0 + entrypoint: schedule-messages + container_name: appwrite-task-scheduler-messages + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + depends_on: + - mariadb + - redis + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + + appwrite-assistant: + image: appwrite/assistant:0.4.0 + container_name: appwrite-assistant + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + environment: + - _APP_ASSISTANT_OPENAI_API_KEY + + openruntimes-executor: + container_name: openruntimes-executor + hostname: exc1 + <<: *x-logging + restart: unless-stopped + stop_signal: SIGINT + image: openruntimes/executor:0.6.11 + networks: + - dokploy-network + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - appwrite-builds:/storage/builds:rw + - appwrite-functions:/storage/functions:rw + - /tmp:/tmp:rw + environment: + - OPR_EXECUTOR_INACTIVE_TRESHOLD=$_APP_FUNCTIONS_INACTIVE_THRESHOLD + - OPR_EXECUTOR_MAINTENANCE_INTERVAL=$_APP_FUNCTIONS_MAINTENANCE_INTERVAL + - OPR_EXECUTOR_NETWORK=$_APP_FUNCTIONS_RUNTIMES_NETWORK + - OPR_EXECUTOR_DOCKER_HUB_USERNAME=$_APP_DOCKER_HUB_USERNAME + - OPR_EXECUTOR_DOCKER_HUB_PASSWORD=$_APP_DOCKER_HUB_PASSWORD + - OPR_EXECUTOR_ENV=$_APP_ENV + - OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES + - OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET + - OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG + - OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE + - OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET + - OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION + - OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET + - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET + - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION + - OPR_EXECUTOR_STORAGE_DO_SPACES_BUCKET=$_APP_STORAGE_DO_SPACES_BUCKET + - OPR_EXECUTOR_STORAGE_BACKBLAZE_ACCESS_KEY=$_APP_STORAGE_BACKBLAZE_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_BACKBLAZE_SECRET=$_APP_STORAGE_BACKBLAZE_SECRET + - OPR_EXECUTOR_STORAGE_BACKBLAZE_REGION=$_APP_STORAGE_BACKBLAZE_REGION + - OPR_EXECUTOR_STORAGE_BACKBLAZE_BUCKET=$_APP_STORAGE_BACKBLAZE_BUCKET + - OPR_EXECUTOR_STORAGE_LINODE_ACCESS_KEY=$_APP_STORAGE_LINODE_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_LINODE_SECRET=$_APP_STORAGE_LINODE_SECRET + - OPR_EXECUTOR_STORAGE_LINODE_REGION=$_APP_STORAGE_LINODE_REGION + - OPR_EXECUTOR_STORAGE_LINODE_BUCKET=$_APP_STORAGE_LINODE_BUCKET + - OPR_EXECUTOR_STORAGE_WASABI_ACCESS_KEY=$_APP_STORAGE_WASABI_ACCESS_KEY + - OPR_EXECUTOR_STORAGE_WASABI_SECRET=$_APP_STORAGE_WASABI_SECRET + - OPR_EXECUTOR_STORAGE_WASABI_REGION=$_APP_STORAGE_WASABI_REGION + - OPR_EXECUTOR_STORAGE_WASABI_BUCKET=$_APP_STORAGE_WASABI_BUCKET + + mariadb: + image: mariadb:10.11 + container_name: appwrite-mariadb + <<: *x-logging + restart: unless-stopped + networks: + - dokploy-network + volumes: + - appwrite-mariadb:/var/lib/mysql:rw + environment: + - MYSQL_ROOT_PASSWORD=${_APP_DB_ROOT_PASS} + - MYSQL_DATABASE=${_APP_DB_SCHEMA} + - MYSQL_USER=${_APP_DB_USER} + - MYSQL_PASSWORD=${_APP_DB_PASS} + - MARIADB_AUTO_UPGRADE=1 + command: "mysqld --innodb-flush-method=fsync" + + redis: + image: redis:7.2.4-alpine + container_name: appwrite-redis + <<: *x-logging + restart: unless-stopped + command: > + redis-server + --maxmemory 512mb + --maxmemory-policy allkeys-lru + --maxmemory-samples 5 + networks: + - dokploy-network + volumes: + - appwrite-redis:/data:rw + +# Uncomment and configure if ClamAV is needed +# clamav: +# image: appwrite/clamav:1.2.0 +# container_name: appwrite-clamav +# restart: unless-stopped +# networks: +# - dokploy-network +# volumes: +# - appwrite-uploads:/storage/uploads + +volumes: + appwrite-mariadb: + appwrite-redis: + appwrite-cache: + appwrite-uploads: + appwrite-certificates: + appwrite-functions: + appwrite-builds: + appwrite-config: + +networks: + dokploy-network: + external: true diff --git a/apps/dokploy/templates/appwrite/index.ts b/apps/dokploy/templates/appwrite/index.ts new file mode 100644 index 000000000..4e671324f --- /dev/null +++ b/apps/dokploy/templates/appwrite/index.ts @@ -0,0 +1,153 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { host: mainDomain, port: 80, serviceName: "appwrite", path: "/" }, + { + host: mainDomain, + port: 80, + serviceName: "appwrite-console", + path: "/console", + }, + { + host: mainDomain, + port: 80, + serviceName: "appwrite-realtime", + path: "/v1/realtime", + }, + ]; + + const envs = [ + "_APP_ENV=production", + "_APP_LOCALE=en", + "_APP_OPTIONS_ABUSE=enabled", + "_APP_OPTIONS_FORCE_HTTPS=disabled", + "_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled", + "_APP_OPTIONS_ROUTER_PROTECTION=disabled", + "_APP_OPENSSL_KEY_V1=your-secret-key", + `_APP_DOMAIN=${mainDomain}`, + `_APP_DOMAIN_FUNCTIONS=${mainDomain}`, + `_APP_DOMAIN_TARGET=${mainDomain}`, + "_APP_CONSOLE_WHITELIST_ROOT=enabled", + "_APP_CONSOLE_WHITELIST_EMAILS=", + "_APP_CONSOLE_WHITELIST_IPS=", + "_APP_CONSOLE_HOSTNAMES=", + "_APP_SYSTEM_EMAIL_NAME=Appwrite", + "_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io", + "_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io", + "_APP_SYSTEM_RESPONSE_FORMAT=", + "_APP_SYSTEM_SECURITY_EMAIL_ADDRESS=certs@appwrite.io", + "_APP_EMAIL_SECURITY=", + "_APP_EMAIL_CERTIFICATES=", + "_APP_USAGE_STATS=enabled", + "_APP_LOGGING_PROVIDER=", + "_APP_LOGGING_CONFIG=", + "_APP_USAGE_AGGREGATION_INTERVAL=30", + "_APP_USAGE_TIMESERIES_INTERVAL=30", + "_APP_USAGE_DATABASE_INTERVAL=900", + "_APP_WORKER_PER_CORE=6", + "_APP_CONSOLE_SESSION_ALERTS=disabled", + "_APP_REDIS_HOST=redis", + "_APP_REDIS_PORT=6379", + "_APP_REDIS_USER=", + "_APP_REDIS_PASS=", + "_APP_DB_HOST=mariadb", + "_APP_DB_PORT=3306", + "_APP_DB_SCHEMA=appwrite", + "_APP_DB_USER=user", + "_APP_DB_PASS=password", + "_APP_DB_ROOT_PASS=rootsecretpassword", + "_APP_INFLUXDB_HOST=influxdb", + "_APP_INFLUXDB_PORT=8086", + "_APP_STATSD_HOST=telegraf", + "_APP_STATSD_PORT=8125", + "_APP_SMTP_HOST=", + "_APP_SMTP_PORT=", + "_APP_SMTP_SECURE=", + "_APP_SMTP_USERNAME=", + "_APP_SMTP_PASSWORD=", + "_APP_SMS_PROVIDER=", + "_APP_SMS_FROM=", + "_APP_STORAGE_LIMIT=30000000", + "_APP_STORAGE_PREVIEW_LIMIT=20000000", + "_APP_STORAGE_ANTIVIRUS=disabled", + "_APP_STORAGE_ANTIVIRUS_HOST=clamav", + "_APP_STORAGE_ANTIVIRUS_PORT=3310", + "_APP_STORAGE_DEVICE=local", + "_APP_STORAGE_S3_ACCESS_KEY=", + "_APP_STORAGE_S3_SECRET=", + "_APP_STORAGE_S3_REGION=us-east-1", + "_APP_STORAGE_S3_BUCKET=", + "_APP_STORAGE_DO_SPACES_ACCESS_KEY=", + "_APP_STORAGE_DO_SPACES_SECRET=", + "_APP_STORAGE_DO_SPACES_REGION=us-east-1", + "_APP_STORAGE_DO_SPACES_BUCKET=", + "_APP_STORAGE_BACKBLAZE_ACCESS_KEY=", + "_APP_STORAGE_BACKBLAZE_SECRET=", + "_APP_STORAGE_BACKBLAZE_REGION=us-west-004", + "_APP_STORAGE_BACKBLAZE_BUCKET=", + "_APP_STORAGE_LINODE_ACCESS_KEY=", + "_APP_STORAGE_LINODE_SECRET=", + "_APP_STORAGE_LINODE_REGION=eu-central-1", + "_APP_STORAGE_LINODE_BUCKET=", + "_APP_STORAGE_WASABI_ACCESS_KEY=", + "_APP_STORAGE_WASABI_SECRET=", + "_APP_STORAGE_WASABI_REGION=eu-central-1", + "_APP_STORAGE_WASABI_BUCKET=", + "_APP_FUNCTIONS_SIZE_LIMIT=30000000", + "_APP_FUNCTIONS_BUILD_SIZE_LIMIT=2000000000", + "_APP_FUNCTIONS_TIMEOUT=900", + "_APP_FUNCTIONS_BUILD_TIMEOUT=900", + "_APP_FUNCTIONS_CONTAINERS=10", + "_APP_FUNCTIONS_CPUS=0", + "_APP_FUNCTIONS_MEMORY=0", + "_APP_FUNCTIONS_MEMORY_SWAP=0", + "_APP_FUNCTIONS_RUNTIMES=node-16.0,php-8.0,python-3.9,ruby-3.0", + "_APP_EXECUTOR_SECRET=your-secret-key", + "_APP_EXECUTOR_HOST=http://exc1/v1", + "_APP_EXECUTOR_RUNTIME_NETWORK=appwrite_runtimes", + "_APP_FUNCTIONS_ENVS=node-16.0,php-7.4,python-3.9,ruby-3.0", + "_APP_FUNCTIONS_INACTIVE_THRESHOLD=60", + "DOCKERHUB_PULL_USERNAME=", + "DOCKERHUB_PULL_PASSWORD=", + "DOCKERHUB_PULL_EMAIL=", + "OPEN_RUNTIMES_NETWORK=appwrite_runtimes", + "_APP_FUNCTIONS_RUNTIMES_NETWORK=runtimes", + "_APP_DOCKER_HUB_USERNAME=", + "_APP_DOCKER_HUB_PASSWORD=", + "_APP_FUNCTIONS_MAINTENANCE_INTERVAL=3600", + "_APP_VCS_GITHUB_APP_NAME=", + "_APP_VCS_GITHUB_PRIVATE_KEY=", + "_APP_VCS_GITHUB_APP_ID=", + "_APP_VCS_GITHUB_CLIENT_ID=", + "_APP_VCS_GITHUB_CLIENT_SECRET=", + "_APP_VCS_GITHUB_WEBHOOK_SECRET=", + "_APP_MAINTENANCE_INTERVAL=86400", + "_APP_MAINTENANCE_DELAY=0", + "_APP_MAINTENANCE_RETENTION_CACHE=2592000", + "_APP_MAINTENANCE_RETENTION_EXECUTION=1209600", + "_APP_MAINTENANCE_RETENTION_AUDIT=1209600", + "_APP_MAINTENANCE_RETENTION_ABUSE=86400", + "_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000", + "_APP_MAINTENANCE_RETENTION_SCHEDULES=86400", + "_APP_GRAPHQL_MAX_BATCH_SIZE=10", + "_APP_GRAPHQL_MAX_COMPLEXITY=250", + "_APP_GRAPHQL_MAX_DEPTH=3", + "_APP_MIGRATIONS_FIREBASE_CLIENT_ID=", + "_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET=", + "_APP_ASSISTANT_OPENAI_API_KEY=", + ]; + + return { + domains, + envs, + mounts: [], + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index cf3de8739..17498e035 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,6 +1,22 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ + { + id: "appwrite", + name: "Appwrite", + version: "1.6.0", + description: + "Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.\n" + + "Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", + links: { + github: "https://github.com/appwrite/appwrite", + website: "https://appwrite.io/", + docs: "https://appwrite.io/docs", + }, + logo: "appwrite.svg", + tags: ["database", "firebase", "postgres"], + load: () => import("./appwrite/index").then((m) => m.generate), + }, { id: "outline", name: "Outline", From 1bbb4c9b6429f45889f84c798fa8fb58be91aa08 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:13:20 -0600 Subject: [PATCH 040/126] refactor: update migration --- apps/dokploy/drizzle/0066_yielding_echo.sql | 1 - apps/dokploy/drizzle/0067_migrate-data.sql | 27 +- apps/dokploy/drizzle/0069_broad_ken_ellis.sql | 2 + apps/dokploy/drizzle/0069_known_aqueduct.sql | 1 - .../drizzle/0070_overrated_the_stranger.sql | 1 - apps/dokploy/drizzle/meta/0066_snapshot.json | 6 - apps/dokploy/drizzle/meta/0067_snapshot.json | 6 - apps/dokploy/drizzle/meta/0068_snapshot.json | 6 - apps/dokploy/drizzle/meta/0069_snapshot.json | 11 +- apps/dokploy/drizzle/meta/0070_snapshot.json | 5286 ----------------- apps/dokploy/drizzle/meta/_journal.json | 11 +- apps/dokploy/drizzle/old_migration | 278 - packages/server/src/db/schema/user.ts | 1 - 13 files changed, 28 insertions(+), 5609 deletions(-) create mode 100644 apps/dokploy/drizzle/0069_broad_ken_ellis.sql delete mode 100644 apps/dokploy/drizzle/0069_known_aqueduct.sql delete mode 100644 apps/dokploy/drizzle/0070_overrated_the_stranger.sql delete mode 100644 apps/dokploy/drizzle/meta/0070_snapshot.json delete mode 100644 apps/dokploy/drizzle/old_migration diff --git a/apps/dokploy/drizzle/0066_yielding_echo.sql b/apps/dokploy/drizzle/0066_yielding_echo.sql index 20f9d07b9..c66e2318b 100644 --- a/apps/dokploy/drizzle/0066_yielding_echo.sql +++ b/apps/dokploy/drizzle/0066_yielding_echo.sql @@ -19,7 +19,6 @@ CREATE TABLE "user_temp" ( "email" text NOT NULL, "email_verified" boolean NOT NULL, "image" text, - "role" text, "banned" boolean, "ban_reason" text, "ban_expires" timestamp, diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql index 2057bd965..074de9fa6 100644 --- a/apps/dokploy/drizzle/0067_migrate-data.sql +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -8,7 +8,6 @@ WITH inserted_users AS ( token, "email_verified", "updated_at", - role, "serverIp", image, "certificateType", @@ -34,7 +33,6 @@ WITH inserted_users AS ( COALESCE(auth.token, ''), true, CURRENT_TIMESTAMP, - 'admin', a."serverIp", auth.image, a."certificateType", @@ -109,7 +107,6 @@ inserted_members AS ( token, "email_verified", "updated_at", - role, image, "createdAt", "canAccessToAPI", @@ -131,7 +128,6 @@ inserted_members AS ( COALESCE(u.token, ''), true, CURRENT_TIMESTAMP, - 'user', auth.image, NOW(), COALESCE(u."canAccessToAPI", false), @@ -176,8 +172,27 @@ inserted_member_accounts AS ( JOIN admin a ON u."adminId" = a."adminId" JOIN auth ON auth.id = u."authId" RETURNING * +), +inserted_admin_members AS ( + -- Insertar miembros en las organizaciones (admins como owners) + INSERT INTO member ( + id, + "organization_id", + "user_id", + role, + "created_at" + ) + SELECT + gen_random_uuid(), + o.id, + a."adminId", + 'owner', + NOW() + FROM admin a + JOIN inserted_orgs o ON o."owner_id" = a."adminId" + RETURNING * ) --- Insertar miembros en las organizaciones +-- Insertar miembros regulares en las organizaciones INSERT INTO member ( id, "organization_id", @@ -189,7 +204,7 @@ SELECT gen_random_uuid(), o.id, u."userId", - 'admin', + 'member', NOW() FROM "user" u JOIN admin a ON u."adminId" = a."adminId" diff --git a/apps/dokploy/drizzle/0069_broad_ken_ellis.sql b/apps/dokploy/drizzle/0069_broad_ken_ellis.sql new file mode 100644 index 000000000..3e2c918bd --- /dev/null +++ b/apps/dokploy/drizzle/0069_broad_ken_ellis.sql @@ -0,0 +1,2 @@ +ALTER TABLE "user_temp" ALTER COLUMN "token" SET DEFAULT '';--> statement-breakpoint +ALTER TABLE "user_temp" ADD COLUMN "created_at" timestamp DEFAULT now(); \ No newline at end of file diff --git a/apps/dokploy/drizzle/0069_known_aqueduct.sql b/apps/dokploy/drizzle/0069_known_aqueduct.sql deleted file mode 100644 index d6f4dea76..000000000 --- a/apps/dokploy/drizzle/0069_known_aqueduct.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE "user_temp" ADD COLUMN "created_at" timestamp DEFAULT now(); \ No newline at end of file diff --git a/apps/dokploy/drizzle/0070_overrated_the_stranger.sql b/apps/dokploy/drizzle/0070_overrated_the_stranger.sql deleted file mode 100644 index dab91327b..000000000 --- a/apps/dokploy/drizzle/0070_overrated_the_stranger.sql +++ /dev/null @@ -1 +0,0 @@ -ALTER TABLE "user_temp" ALTER COLUMN "token" SET DEFAULT ''; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json index c51e03851..bcb7807a7 100644 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -1028,12 +1028,6 @@ "primaryKey": false, "notNull": false }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, "banned": { "name": "banned", "type": "boolean", diff --git a/apps/dokploy/drizzle/meta/0067_snapshot.json b/apps/dokploy/drizzle/meta/0067_snapshot.json index 33fc48703..69d4a4ee8 100644 --- a/apps/dokploy/drizzle/meta/0067_snapshot.json +++ b/apps/dokploy/drizzle/meta/0067_snapshot.json @@ -1028,12 +1028,6 @@ "primaryKey": false, "notNull": false }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, "banned": { "name": "banned", "type": "boolean", diff --git a/apps/dokploy/drizzle/meta/0068_snapshot.json b/apps/dokploy/drizzle/meta/0068_snapshot.json index c9f50b5ee..02e33084f 100644 --- a/apps/dokploy/drizzle/meta/0068_snapshot.json +++ b/apps/dokploy/drizzle/meta/0068_snapshot.json @@ -1028,12 +1028,6 @@ "primaryKey": false, "notNull": false }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, "banned": { "name": "banned", "type": "boolean", diff --git a/apps/dokploy/drizzle/meta/0069_snapshot.json b/apps/dokploy/drizzle/meta/0069_snapshot.json index 084216167..41b53582e 100644 --- a/apps/dokploy/drizzle/meta/0069_snapshot.json +++ b/apps/dokploy/drizzle/meta/0069_snapshot.json @@ -1,5 +1,5 @@ { - "id": "82a6a31d-7611-410d-8a32-31a36ab369c5", + "id": "e0842a94-530d-4a3c-a6b1-16cf37618b8b", "prevId": "c2e134de-9865-4892-a24d-048aa5be22e8", "version": "7", "dialect": "postgresql", @@ -912,7 +912,8 @@ "name": "token", "type": "text", "primaryKey": false, - "notNull": true + "notNull": true, + "default": "''" }, "isRegistered": { "name": "isRegistered", @@ -1035,12 +1036,6 @@ "primaryKey": false, "notNull": false }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, "banned": { "name": "banned", "type": "boolean", diff --git a/apps/dokploy/drizzle/meta/0070_snapshot.json b/apps/dokploy/drizzle/meta/0070_snapshot.json deleted file mode 100644 index b0a1eea7c..000000000 --- a/apps/dokploy/drizzle/meta/0070_snapshot.json +++ /dev/null @@ -1,5286 +0,0 @@ -{ - "id": "c01cab17-e046-4691-b5ab-43f6a92879d4", - "prevId": "82a6a31d-7611-410d-8a32-31a36ab369c5", - "version": "7", - "dialect": "postgresql", - "tables": { - "public.application": { - "name": "application", - "schema": "", - "columns": { - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewEnv": { - "name": "previewEnv", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewBuildArgs": { - "name": "previewBuildArgs", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewWildcard": { - "name": "previewWildcard", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewPort": { - "name": "previewPort", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 3000 - }, - "previewHttps": { - "name": "previewHttps", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "previewPath": { - "name": "previewPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - }, - "previewLimit": { - "name": "previewLimit", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 3 - }, - "isPreviewDeploymentsActive": { - "name": "isPreviewDeploymentsActive", - "type": "boolean", - "primaryKey": false, - "notNull": false, - "default": false - }, - "buildArgs": { - "name": "buildArgs", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "enabled": { - "name": "enabled", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "subtitle": { - "name": "subtitle", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refreshToken": { - "name": "refreshToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sourceType": { - "name": "sourceType", - "type": "sourceType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'github'" - }, - "repository": { - "name": "repository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "owner": { - "name": "owner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "branch": { - "name": "branch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "buildPath": { - "name": "buildPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "autoDeploy": { - "name": "autoDeploy", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "gitlabProjectId": { - "name": "gitlabProjectId", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "gitlabRepository": { - "name": "gitlabRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabOwner": { - "name": "gitlabOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabBranch": { - "name": "gitlabBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabBuildPath": { - "name": "gitlabBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "gitlabPathNamespace": { - "name": "gitlabPathNamespace", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketRepository": { - "name": "bitbucketRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketOwner": { - "name": "bitbucketOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketBranch": { - "name": "bitbucketBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketBuildPath": { - "name": "bitbucketBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "registryUrl": { - "name": "registryUrl", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitUrl": { - "name": "customGitUrl", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitBranch": { - "name": "customGitBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitBuildPath": { - "name": "customGitBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitSSHKeyId": { - "name": "customGitSSHKeyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerfile": { - "name": "dockerfile", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerContextPath": { - "name": "dockerContextPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerBuildStage": { - "name": "dockerBuildStage", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dropBuildPath": { - "name": "dropBuildPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "healthCheckSwarm": { - "name": "healthCheckSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "restartPolicySwarm": { - "name": "restartPolicySwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "placementSwarm": { - "name": "placementSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "updateConfigSwarm": { - "name": "updateConfigSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "rollbackConfigSwarm": { - "name": "rollbackConfigSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "modeSwarm": { - "name": "modeSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "labelsSwarm": { - "name": "labelsSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "networkSwarm": { - "name": "networkSwarm", - "type": "json", - "primaryKey": false, - "notNull": false - }, - "replicas": { - "name": "replicas", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 1 - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "buildType": { - "name": "buildType", - "type": "buildType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'nixpacks'" - }, - "herokuVersion": { - "name": "herokuVersion", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'24'" - }, - "publishDirectory": { - "name": "publishDirectory", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "registryId": { - "name": "registryId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "githubId": { - "name": "githubId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabId": { - "name": "gitlabId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketId": { - "name": "bitbucketId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { - "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", - "tableFrom": "application", - "tableTo": "ssh-key", - "columnsFrom": [ - "customGitSSHKeyId" - ], - "columnsTo": [ - "sshKeyId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_registryId_registry_registryId_fk": { - "name": "application_registryId_registry_registryId_fk", - "tableFrom": "application", - "tableTo": "registry", - "columnsFrom": [ - "registryId" - ], - "columnsTo": [ - "registryId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_projectId_project_projectId_fk": { - "name": "application_projectId_project_projectId_fk", - "tableFrom": "application", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "application_githubId_github_githubId_fk": { - "name": "application_githubId_github_githubId_fk", - "tableFrom": "application", - "tableTo": "github", - "columnsFrom": [ - "githubId" - ], - "columnsTo": [ - "githubId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_gitlabId_gitlab_gitlabId_fk": { - "name": "application_gitlabId_gitlab_gitlabId_fk", - "tableFrom": "application", - "tableTo": "gitlab", - "columnsFrom": [ - "gitlabId" - ], - "columnsTo": [ - "gitlabId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_bitbucketId_bitbucket_bitbucketId_fk": { - "name": "application_bitbucketId_bitbucket_bitbucketId_fk", - "tableFrom": "application", - "tableTo": "bitbucket", - "columnsFrom": [ - "bitbucketId" - ], - "columnsTo": [ - "bitbucketId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "application_serverId_server_serverId_fk": { - "name": "application_serverId_server_serverId_fk", - "tableFrom": "application", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "application_appName_unique": { - "name": "application_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.postgres": { - "name": "postgres", - "schema": "", - "columns": { - "postgresId": { - "name": "postgresId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseName": { - "name": "databaseName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "postgres_projectId_project_projectId_fk": { - "name": "postgres_projectId_project_projectId_fk", - "tableFrom": "postgres", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "postgres_serverId_server_serverId_fk": { - "name": "postgres_serverId_server_serverId_fk", - "tableFrom": "postgres", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "postgres_appName_unique": { - "name": "postgres_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.user": { - "name": "user", - "schema": "", - "columns": { - "userId": { - "name": "userId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "isRegistered": { - "name": "isRegistered", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "expirationDate": { - "name": "expirationDate", - "type": "timestamp(3)", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "canCreateProjects": { - "name": "canCreateProjects", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToSSHKeys": { - "name": "canAccessToSSHKeys", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canCreateServices": { - "name": "canCreateServices", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canDeleteProjects": { - "name": "canDeleteProjects", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canDeleteServices": { - "name": "canDeleteServices", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToDocker": { - "name": "canAccessToDocker", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToAPI": { - "name": "canAccessToAPI", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToGitProviders": { - "name": "canAccessToGitProviders", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToTraefikFiles": { - "name": "canAccessToTraefikFiles", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "accesedProjects": { - "name": "accesedProjects", - "type": "text[]", - "primaryKey": false, - "notNull": true, - "default": "ARRAY[]::text[]" - }, - "accesedServices": { - "name": "accesedServices", - "type": "text[]", - "primaryKey": false, - "notNull": true, - "default": "ARRAY[]::text[]" - }, - "adminId": { - "name": "adminId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "authId": { - "name": "authId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "user_adminId_admin_adminId_fk": { - "name": "user_adminId_admin_adminId_fk", - "tableFrom": "user", - "tableTo": "admin", - "columnsFrom": [ - "adminId" - ], - "columnsTo": [ - "adminId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "user_authId_auth_id_fk": { - "name": "user_authId_auth_id_fk", - "tableFrom": "user", - "tableTo": "auth", - "columnsFrom": [ - "authId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.user_temp": { - "name": "user_temp", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "isRegistered": { - "name": "isRegistered", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "expirationDate": { - "name": "expirationDate", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false, - "default": "now()" - }, - "canCreateProjects": { - "name": "canCreateProjects", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToSSHKeys": { - "name": "canAccessToSSHKeys", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canCreateServices": { - "name": "canCreateServices", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canDeleteProjects": { - "name": "canDeleteProjects", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canDeleteServices": { - "name": "canDeleteServices", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToDocker": { - "name": "canAccessToDocker", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToAPI": { - "name": "canAccessToAPI", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToGitProviders": { - "name": "canAccessToGitProviders", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "canAccessToTraefikFiles": { - "name": "canAccessToTraefikFiles", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "accesedProjects": { - "name": "accesedProjects", - "type": "text[]", - "primaryKey": false, - "notNull": true, - "default": "ARRAY[]::text[]" - }, - "accesedServices": { - "name": "accesedServices", - "type": "text[]", - "primaryKey": false, - "notNull": true, - "default": "ARRAY[]::text[]" - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "email_verified": { - "name": "email_verified", - "type": "boolean", - "primaryKey": false, - "notNull": true - }, - "image": { - "name": "image", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "banned": { - "name": "banned", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "ban_reason": { - "name": "ban_reason", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "ban_expires": { - "name": "ban_expires", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "serverIp": { - "name": "serverIp", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - }, - "host": { - "name": "host", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "letsEncryptEmail": { - "name": "letsEncryptEmail", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sshPrivateKey": { - "name": "sshPrivateKey", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "enableDockerCleanup": { - "name": "enableDockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "enableLogRotation": { - "name": "enableLogRotation", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "enablePaidFeatures": { - "name": "enablePaidFeatures", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "metricsConfig": { - "name": "metricsConfig", - "type": "jsonb", - "primaryKey": false, - "notNull": true, - "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" - }, - "cleanupCacheApplications": { - "name": "cleanupCacheApplications", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "cleanupCacheOnPreviews": { - "name": "cleanupCacheOnPreviews", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "cleanupCacheOnCompose": { - "name": "cleanupCacheOnCompose", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "stripeCustomerId": { - "name": "stripeCustomerId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "stripeSubscriptionId": { - "name": "stripeSubscriptionId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serversQuantity": { - "name": "serversQuantity", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "user_temp_email_unique": { - "name": "user_temp_email_unique", - "nullsNotDistinct": false, - "columns": [ - "email" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.admin": { - "name": "admin", - "schema": "", - "columns": { - "adminId": { - "name": "adminId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "serverIp": { - "name": "serverIp", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - }, - "host": { - "name": "host", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "letsEncryptEmail": { - "name": "letsEncryptEmail", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sshPrivateKey": { - "name": "sshPrivateKey", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "enableDockerCleanup": { - "name": "enableDockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "enableLogRotation": { - "name": "enableLogRotation", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "authId": { - "name": "authId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "stripeCustomerId": { - "name": "stripeCustomerId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "stripeSubscriptionId": { - "name": "stripeSubscriptionId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serversQuantity": { - "name": "serversQuantity", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 0 - }, - "enablePaidFeatures": { - "name": "enablePaidFeatures", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "metricsConfig": { - "name": "metricsConfig", - "type": "jsonb", - "primaryKey": false, - "notNull": true, - "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" - }, - "cleanupCacheApplications": { - "name": "cleanupCacheApplications", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "cleanupCacheOnPreviews": { - "name": "cleanupCacheOnPreviews", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "cleanupCacheOnCompose": { - "name": "cleanupCacheOnCompose", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - } - }, - "indexes": {}, - "foreignKeys": { - "admin_authId_auth_id_fk": { - "name": "admin_authId_auth_id_fk", - "tableFrom": "admin", - "tableTo": "auth", - "columnsFrom": [ - "authId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.auth": { - "name": "auth", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "rol": { - "name": "rol", - "type": "Roles", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "image": { - "name": "image", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "secret": { - "name": "secret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "is2FAEnabled": { - "name": "is2FAEnabled", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "resetPasswordToken": { - "name": "resetPasswordToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "resetPasswordExpiresAt": { - "name": "resetPasswordExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationToken": { - "name": "confirmationToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationExpiresAt": { - "name": "confirmationExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "auth_email_unique": { - "name": "auth_email_unique", - "nullsNotDistinct": false, - "columns": [ - "email" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.project": { - "name": "project", - "schema": "", - "columns": { - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - } - }, - "indexes": {}, - "foreignKeys": { - "project_userId_user_temp_id_fk": { - "name": "project_userId_user_temp_id_fk", - "tableFrom": "project", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.domain": { - "name": "domain", - "schema": "", - "columns": { - "domainId": { - "name": "domainId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "host": { - "name": "host", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "https": { - "name": "https", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "port": { - "name": "port", - "type": "integer", - "primaryKey": false, - "notNull": false, - "default": 3000 - }, - "path": { - "name": "path", - "type": "text", - "primaryKey": false, - "notNull": false, - "default": "'/'" - }, - "serviceName": { - "name": "serviceName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "domainType": { - "name": "domainType", - "type": "domainType", - "typeSchema": "public", - "primaryKey": false, - "notNull": false, - "default": "'application'" - }, - "uniqueConfigKey": { - "name": "uniqueConfigKey", - "type": "serial", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "previewDeploymentId": { - "name": "previewDeploymentId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "certificateType": { - "name": "certificateType", - "type": "certificateType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'none'" - } - }, - "indexes": {}, - "foreignKeys": { - "domain_composeId_compose_composeId_fk": { - "name": "domain_composeId_compose_composeId_fk", - "tableFrom": "domain", - "tableTo": "compose", - "columnsFrom": [ - "composeId" - ], - "columnsTo": [ - "composeId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "domain_applicationId_application_applicationId_fk": { - "name": "domain_applicationId_application_applicationId_fk", - "tableFrom": "domain", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { - "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", - "tableFrom": "domain", - "tableTo": "preview_deployments", - "columnsFrom": [ - "previewDeploymentId" - ], - "columnsTo": [ - "previewDeploymentId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mariadb": { - "name": "mariadb", - "schema": "", - "columns": { - "mariadbId": { - "name": "mariadbId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "databaseName": { - "name": "databaseName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "rootPassword": { - "name": "rootPassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mariadb_projectId_project_projectId_fk": { - "name": "mariadb_projectId_project_projectId_fk", - "tableFrom": "mariadb", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mariadb_serverId_server_serverId_fk": { - "name": "mariadb_serverId_server_serverId_fk", - "tableFrom": "mariadb", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "mariadb_appName_unique": { - "name": "mariadb_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mongo": { - "name": "mongo", - "schema": "", - "columns": { - "mongoId": { - "name": "mongoId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "replicaSets": { - "name": "replicaSets", - "type": "boolean", - "primaryKey": false, - "notNull": false, - "default": false - } - }, - "indexes": {}, - "foreignKeys": { - "mongo_projectId_project_projectId_fk": { - "name": "mongo_projectId_project_projectId_fk", - "tableFrom": "mongo", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mongo_serverId_server_serverId_fk": { - "name": "mongo_serverId_server_serverId_fk", - "tableFrom": "mongo", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "mongo_appName_unique": { - "name": "mongo_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mysql": { - "name": "mysql", - "schema": "", - "columns": { - "mysqlId": { - "name": "mysqlId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "databaseName": { - "name": "databaseName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseUser": { - "name": "databaseUser", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databasePassword": { - "name": "databasePassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "rootPassword": { - "name": "rootPassword", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mysql_projectId_project_projectId_fk": { - "name": "mysql_projectId_project_projectId_fk", - "tableFrom": "mysql", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mysql_serverId_server_serverId_fk": { - "name": "mysql_serverId_server_serverId_fk", - "tableFrom": "mysql", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "mysql_appName_unique": { - "name": "mysql_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.backup": { - "name": "backup", - "schema": "", - "columns": { - "backupId": { - "name": "backupId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "schedule": { - "name": "schedule", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "enabled": { - "name": "enabled", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "database": { - "name": "database", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "prefix": { - "name": "prefix", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "destinationId": { - "name": "destinationId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "databaseType": { - "name": "databaseType", - "type": "databaseType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "postgresId": { - "name": "postgresId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mariadbId": { - "name": "mariadbId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mysqlId": { - "name": "mysqlId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mongoId": { - "name": "mongoId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "backup_destinationId_destination_destinationId_fk": { - "name": "backup_destinationId_destination_destinationId_fk", - "tableFrom": "backup", - "tableTo": "destination", - "columnsFrom": [ - "destinationId" - ], - "columnsTo": [ - "destinationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_postgresId_postgres_postgresId_fk": { - "name": "backup_postgresId_postgres_postgresId_fk", - "tableFrom": "backup", - "tableTo": "postgres", - "columnsFrom": [ - "postgresId" - ], - "columnsTo": [ - "postgresId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_mariadbId_mariadb_mariadbId_fk": { - "name": "backup_mariadbId_mariadb_mariadbId_fk", - "tableFrom": "backup", - "tableTo": "mariadb", - "columnsFrom": [ - "mariadbId" - ], - "columnsTo": [ - "mariadbId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_mysqlId_mysql_mysqlId_fk": { - "name": "backup_mysqlId_mysql_mysqlId_fk", - "tableFrom": "backup", - "tableTo": "mysql", - "columnsFrom": [ - "mysqlId" - ], - "columnsTo": [ - "mysqlId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "backup_mongoId_mongo_mongoId_fk": { - "name": "backup_mongoId_mongo_mongoId_fk", - "tableFrom": "backup", - "tableTo": "mongo", - "columnsFrom": [ - "mongoId" - ], - "columnsTo": [ - "mongoId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.destination": { - "name": "destination", - "schema": "", - "columns": { - "destinationId": { - "name": "destinationId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "provider": { - "name": "provider", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "accessKey": { - "name": "accessKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "secretAccessKey": { - "name": "secretAccessKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "bucket": { - "name": "bucket", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "region": { - "name": "region", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "endpoint": { - "name": "endpoint", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "destination_userId_user_temp_id_fk": { - "name": "destination_userId_user_temp_id_fk", - "tableFrom": "destination", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.deployment": { - "name": "deployment", - "schema": "", - "columns": { - "deploymentId": { - "name": "deploymentId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "title": { - "name": "title", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "status": { - "name": "status", - "type": "deploymentStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": false, - "default": "'running'" - }, - "logPath": { - "name": "logPath", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "isPreviewDeployment": { - "name": "isPreviewDeployment", - "type": "boolean", - "primaryKey": false, - "notNull": false, - "default": false - }, - "previewDeploymentId": { - "name": "previewDeploymentId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "errorMessage": { - "name": "errorMessage", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "deployment_applicationId_application_applicationId_fk": { - "name": "deployment_applicationId_application_applicationId_fk", - "tableFrom": "deployment", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "deployment_composeId_compose_composeId_fk": { - "name": "deployment_composeId_compose_composeId_fk", - "tableFrom": "deployment", - "tableTo": "compose", - "columnsFrom": [ - "composeId" - ], - "columnsTo": [ - "composeId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "deployment_serverId_server_serverId_fk": { - "name": "deployment_serverId_server_serverId_fk", - "tableFrom": "deployment", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { - "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", - "tableFrom": "deployment", - "tableTo": "preview_deployments", - "columnsFrom": [ - "previewDeploymentId" - ], - "columnsTo": [ - "previewDeploymentId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.mount": { - "name": "mount", - "schema": "", - "columns": { - "mountId": { - "name": "mountId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "type": { - "name": "type", - "type": "mountType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "hostPath": { - "name": "hostPath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "volumeName": { - "name": "volumeName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "filePath": { - "name": "filePath", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "content": { - "name": "content", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serviceType": { - "name": "serviceType", - "type": "serviceType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'application'" - }, - "mountPath": { - "name": "mountPath", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "postgresId": { - "name": "postgresId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mariadbId": { - "name": "mariadbId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mongoId": { - "name": "mongoId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "mysqlId": { - "name": "mysqlId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "redisId": { - "name": "redisId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "mount_applicationId_application_applicationId_fk": { - "name": "mount_applicationId_application_applicationId_fk", - "tableFrom": "mount", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_postgresId_postgres_postgresId_fk": { - "name": "mount_postgresId_postgres_postgresId_fk", - "tableFrom": "mount", - "tableTo": "postgres", - "columnsFrom": [ - "postgresId" - ], - "columnsTo": [ - "postgresId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_mariadbId_mariadb_mariadbId_fk": { - "name": "mount_mariadbId_mariadb_mariadbId_fk", - "tableFrom": "mount", - "tableTo": "mariadb", - "columnsFrom": [ - "mariadbId" - ], - "columnsTo": [ - "mariadbId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_mongoId_mongo_mongoId_fk": { - "name": "mount_mongoId_mongo_mongoId_fk", - "tableFrom": "mount", - "tableTo": "mongo", - "columnsFrom": [ - "mongoId" - ], - "columnsTo": [ - "mongoId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_mysqlId_mysql_mysqlId_fk": { - "name": "mount_mysqlId_mysql_mysqlId_fk", - "tableFrom": "mount", - "tableTo": "mysql", - "columnsFrom": [ - "mysqlId" - ], - "columnsTo": [ - "mysqlId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_redisId_redis_redisId_fk": { - "name": "mount_redisId_redis_redisId_fk", - "tableFrom": "mount", - "tableTo": "redis", - "columnsFrom": [ - "redisId" - ], - "columnsTo": [ - "redisId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "mount_composeId_compose_composeId_fk": { - "name": "mount_composeId_compose_composeId_fk", - "tableFrom": "mount", - "tableTo": "compose", - "columnsFrom": [ - "composeId" - ], - "columnsTo": [ - "composeId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.certificate": { - "name": "certificate", - "schema": "", - "columns": { - "certificateId": { - "name": "certificateId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "certificateData": { - "name": "certificateData", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "privateKey": { - "name": "privateKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "certificatePath": { - "name": "certificatePath", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "autoRenew": { - "name": "autoRenew", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "certificate_userId_user_temp_id_fk": { - "name": "certificate_userId_user_temp_id_fk", - "tableFrom": "certificate", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "certificate_serverId_server_serverId_fk": { - "name": "certificate_serverId_server_serverId_fk", - "tableFrom": "certificate", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "certificate_certificatePath_unique": { - "name": "certificate_certificatePath_unique", - "nullsNotDistinct": false, - "columns": [ - "certificatePath" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.session_temp": { - "name": "session_temp", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "ip_address": { - "name": "ip_address", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "user_agent": { - "name": "user_agent", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "impersonated_by": { - "name": "impersonated_by", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "active_organization_id": { - "name": "active_organization_id", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "session_temp_user_id_user_temp_id_fk": { - "name": "session_temp_user_id_user_temp_id_fk", - "tableFrom": "session_temp", - "tableTo": "user_temp", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "session_temp_token_unique": { - "name": "session_temp_token_unique", - "nullsNotDistinct": false, - "columns": [ - "token" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.session": { - "name": "session", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp with time zone", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "session_user_id_auth_id_fk": { - "name": "session_user_id_auth_id_fk", - "tableFrom": "session", - "tableTo": "auth", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.redirect": { - "name": "redirect", - "schema": "", - "columns": { - "redirectId": { - "name": "redirectId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "regex": { - "name": "regex", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "replacement": { - "name": "replacement", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "permanent": { - "name": "permanent", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "uniqueConfigKey": { - "name": "uniqueConfigKey", - "type": "serial", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "redirect_applicationId_application_applicationId_fk": { - "name": "redirect_applicationId_application_applicationId_fk", - "tableFrom": "redirect", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.security": { - "name": "security", - "schema": "", - "columns": { - "securityId": { - "name": "securityId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "security_applicationId_application_applicationId_fk": { - "name": "security_applicationId_application_applicationId_fk", - "tableFrom": "security", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "security_username_applicationId_unique": { - "name": "security_username_applicationId_unique", - "nullsNotDistinct": false, - "columns": [ - "username", - "applicationId" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.port": { - "name": "port", - "schema": "", - "columns": { - "portId": { - "name": "portId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "publishedPort": { - "name": "publishedPort", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "targetPort": { - "name": "targetPort", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "protocol": { - "name": "protocol", - "type": "protocolType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "port_applicationId_application_applicationId_fk": { - "name": "port_applicationId_application_applicationId_fk", - "tableFrom": "port", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.redis": { - "name": "redis", - "schema": "", - "columns": { - "redisId": { - "name": "redisId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "dockerImage": { - "name": "dockerImage", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryReservation": { - "name": "memoryReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "memoryLimit": { - "name": "memoryLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuReservation": { - "name": "cpuReservation", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "cpuLimit": { - "name": "cpuLimit", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "externalPort": { - "name": "externalPort", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationStatus": { - "name": "applicationStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "redis_projectId_project_projectId_fk": { - "name": "redis_projectId_project_projectId_fk", - "tableFrom": "redis", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "redis_serverId_server_serverId_fk": { - "name": "redis_serverId_server_serverId_fk", - "tableFrom": "redis", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "redis_appName_unique": { - "name": "redis_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.compose": { - "name": "compose", - "schema": "", - "columns": { - "composeId": { - "name": "composeId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "env": { - "name": "env", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "composeFile": { - "name": "composeFile", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "refreshToken": { - "name": "refreshToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "sourceType": { - "name": "sourceType", - "type": "sourceTypeCompose", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'github'" - }, - "composeType": { - "name": "composeType", - "type": "composeType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'docker-compose'" - }, - "repository": { - "name": "repository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "owner": { - "name": "owner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "branch": { - "name": "branch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "autoDeploy": { - "name": "autoDeploy", - "type": "boolean", - "primaryKey": false, - "notNull": false - }, - "gitlabProjectId": { - "name": "gitlabProjectId", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "gitlabRepository": { - "name": "gitlabRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabOwner": { - "name": "gitlabOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabBranch": { - "name": "gitlabBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabPathNamespace": { - "name": "gitlabPathNamespace", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketRepository": { - "name": "bitbucketRepository", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketOwner": { - "name": "bitbucketOwner", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketBranch": { - "name": "bitbucketBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitUrl": { - "name": "customGitUrl", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitBranch": { - "name": "customGitBranch", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "customGitSSHKeyId": { - "name": "customGitSSHKeyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "composePath": { - "name": "composePath", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'./docker-compose.yml'" - }, - "suffix": { - "name": "suffix", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "randomize": { - "name": "randomize", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "isolatedDeployment": { - "name": "isolatedDeployment", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "composeStatus": { - "name": "composeStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "projectId": { - "name": "projectId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "githubId": { - "name": "githubId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitlabId": { - "name": "gitlabId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketId": { - "name": "bitbucketId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { - "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", - "tableFrom": "compose", - "tableTo": "ssh-key", - "columnsFrom": [ - "customGitSSHKeyId" - ], - "columnsTo": [ - "sshKeyId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_projectId_project_projectId_fk": { - "name": "compose_projectId_project_projectId_fk", - "tableFrom": "compose", - "tableTo": "project", - "columnsFrom": [ - "projectId" - ], - "columnsTo": [ - "projectId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "compose_githubId_github_githubId_fk": { - "name": "compose_githubId_github_githubId_fk", - "tableFrom": "compose", - "tableTo": "github", - "columnsFrom": [ - "githubId" - ], - "columnsTo": [ - "githubId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_gitlabId_gitlab_gitlabId_fk": { - "name": "compose_gitlabId_gitlab_gitlabId_fk", - "tableFrom": "compose", - "tableTo": "gitlab", - "columnsFrom": [ - "gitlabId" - ], - "columnsTo": [ - "gitlabId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_bitbucketId_bitbucket_bitbucketId_fk": { - "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", - "tableFrom": "compose", - "tableTo": "bitbucket", - "columnsFrom": [ - "bitbucketId" - ], - "columnsTo": [ - "bitbucketId" - ], - "onDelete": "set null", - "onUpdate": "no action" - }, - "compose_serverId_server_serverId_fk": { - "name": "compose_serverId_server_serverId_fk", - "tableFrom": "compose", - "tableTo": "server", - "columnsFrom": [ - "serverId" - ], - "columnsTo": [ - "serverId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.registry": { - "name": "registry", - "schema": "", - "columns": { - "registryId": { - "name": "registryId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "registryName": { - "name": "registryName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "imagePrefix": { - "name": "imagePrefix", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "registryUrl": { - "name": "registryUrl", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "selfHosted": { - "name": "selfHosted", - "type": "RegistryType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'cloud'" - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "registry_userId_user_temp_id_fk": { - "name": "registry_userId_user_temp_id_fk", - "tableFrom": "registry", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.discord": { - "name": "discord", - "schema": "", - "columns": { - "discordId": { - "name": "discordId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "webhookUrl": { - "name": "webhookUrl", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "decoration": { - "name": "decoration", - "type": "boolean", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.email": { - "name": "email", - "schema": "", - "columns": { - "emailId": { - "name": "emailId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "smtpServer": { - "name": "smtpServer", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "smtpPort": { - "name": "smtpPort", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "fromAddress": { - "name": "fromAddress", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "toAddress": { - "name": "toAddress", - "type": "text[]", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.gotify": { - "name": "gotify", - "schema": "", - "columns": { - "gotifyId": { - "name": "gotifyId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "serverUrl": { - "name": "serverUrl", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appToken": { - "name": "appToken", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "priority": { - "name": "priority", - "type": "integer", - "primaryKey": false, - "notNull": true, - "default": 5 - }, - "decoration": { - "name": "decoration", - "type": "boolean", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.notification": { - "name": "notification", - "schema": "", - "columns": { - "notificationId": { - "name": "notificationId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "appDeploy": { - "name": "appDeploy", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "appBuildError": { - "name": "appBuildError", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "databaseBackup": { - "name": "databaseBackup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "dokployRestart": { - "name": "dokployRestart", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "dockerCleanup": { - "name": "dockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "serverThreshold": { - "name": "serverThreshold", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "notificationType": { - "name": "notificationType", - "type": "notificationType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "slackId": { - "name": "slackId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "telegramId": { - "name": "telegramId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "discordId": { - "name": "discordId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "emailId": { - "name": "emailId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gotifyId": { - "name": "gotifyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "notification_slackId_slack_slackId_fk": { - "name": "notification_slackId_slack_slackId_fk", - "tableFrom": "notification", - "tableTo": "slack", - "columnsFrom": [ - "slackId" - ], - "columnsTo": [ - "slackId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_telegramId_telegram_telegramId_fk": { - "name": "notification_telegramId_telegram_telegramId_fk", - "tableFrom": "notification", - "tableTo": "telegram", - "columnsFrom": [ - "telegramId" - ], - "columnsTo": [ - "telegramId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_discordId_discord_discordId_fk": { - "name": "notification_discordId_discord_discordId_fk", - "tableFrom": "notification", - "tableTo": "discord", - "columnsFrom": [ - "discordId" - ], - "columnsTo": [ - "discordId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_emailId_email_emailId_fk": { - "name": "notification_emailId_email_emailId_fk", - "tableFrom": "notification", - "tableTo": "email", - "columnsFrom": [ - "emailId" - ], - "columnsTo": [ - "emailId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_gotifyId_gotify_gotifyId_fk": { - "name": "notification_gotifyId_gotify_gotifyId_fk", - "tableFrom": "notification", - "tableTo": "gotify", - "columnsFrom": [ - "gotifyId" - ], - "columnsTo": [ - "gotifyId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "notification_userId_user_temp_id_fk": { - "name": "notification_userId_user_temp_id_fk", - "tableFrom": "notification", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.slack": { - "name": "slack", - "schema": "", - "columns": { - "slackId": { - "name": "slackId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "webhookUrl": { - "name": "webhookUrl", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "channel": { - "name": "channel", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.telegram": { - "name": "telegram", - "schema": "", - "columns": { - "telegramId": { - "name": "telegramId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "botToken": { - "name": "botToken", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "chatId": { - "name": "chatId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.ssh-key": { - "name": "ssh-key", - "schema": "", - "columns": { - "sshKeyId": { - "name": "sshKeyId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "privateKey": { - "name": "privateKey", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "publicKey": { - "name": "publicKey", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "lastUsedAt": { - "name": "lastUsedAt", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "ssh-key_userId_user_temp_id_fk": { - "name": "ssh-key_userId_user_temp_id_fk", - "tableFrom": "ssh-key", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.git_provider": { - "name": "git_provider", - "schema": "", - "columns": { - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "providerType": { - "name": "providerType", - "type": "gitProviderType", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'github'" - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "git_provider_userId_user_temp_id_fk": { - "name": "git_provider_userId_user_temp_id_fk", - "tableFrom": "git_provider", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.bitbucket": { - "name": "bitbucket", - "schema": "", - "columns": { - "bitbucketId": { - "name": "bitbucketId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "bitbucketUsername": { - "name": "bitbucketUsername", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "appPassword": { - "name": "appPassword", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "bitbucketWorkspaceName": { - "name": "bitbucketWorkspaceName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { - "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", - "tableFrom": "bitbucket", - "tableTo": "git_provider", - "columnsFrom": [ - "gitProviderId" - ], - "columnsTo": [ - "gitProviderId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.github": { - "name": "github", - "schema": "", - "columns": { - "githubId": { - "name": "githubId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "githubAppName": { - "name": "githubAppName", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubAppId": { - "name": "githubAppId", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "githubClientId": { - "name": "githubClientId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubClientSecret": { - "name": "githubClientSecret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubInstallationId": { - "name": "githubInstallationId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubPrivateKey": { - "name": "githubPrivateKey", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "githubWebhookSecret": { - "name": "githubWebhookSecret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "github_gitProviderId_git_provider_gitProviderId_fk": { - "name": "github_gitProviderId_git_provider_gitProviderId_fk", - "tableFrom": "github", - "tableTo": "git_provider", - "columnsFrom": [ - "gitProviderId" - ], - "columnsTo": [ - "gitProviderId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.gitlab": { - "name": "gitlab", - "schema": "", - "columns": { - "gitlabId": { - "name": "gitlabId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "gitlabUrl": { - "name": "gitlabUrl", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'https://gitlab.com'" - }, - "application_id": { - "name": "application_id", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "redirect_uri": { - "name": "redirect_uri", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "secret": { - "name": "secret", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "group_name": { - "name": "group_name", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "expires_at": { - "name": "expires_at", - "type": "integer", - "primaryKey": false, - "notNull": false - }, - "gitProviderId": { - "name": "gitProviderId", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "gitlab_gitProviderId_git_provider_gitProviderId_fk": { - "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", - "tableFrom": "gitlab", - "tableTo": "git_provider", - "columnsFrom": [ - "gitProviderId" - ], - "columnsTo": [ - "gitProviderId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.server": { - "name": "server", - "schema": "", - "columns": { - "serverId": { - "name": "serverId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "description": { - "name": "description", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "ipAddress": { - "name": "ipAddress", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "port": { - "name": "port", - "type": "integer", - "primaryKey": false, - "notNull": true - }, - "username": { - "name": "username", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "'root'" - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "enableDockerCleanup": { - "name": "enableDockerCleanup", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "userId": { - "name": "userId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "serverStatus": { - "name": "serverStatus", - "type": "serverStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'active'" - }, - "command": { - "name": "command", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" - }, - "sshKeyId": { - "name": "sshKeyId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "metricsConfig": { - "name": "metricsConfig", - "type": "jsonb", - "primaryKey": false, - "notNull": true, - "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" - } - }, - "indexes": {}, - "foreignKeys": { - "server_userId_user_temp_id_fk": { - "name": "server_userId_user_temp_id_fk", - "tableFrom": "server", - "tableTo": "user_temp", - "columnsFrom": [ - "userId" - ], - "columnsTo": [ - "id" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "server_sshKeyId_ssh-key_sshKeyId_fk": { - "name": "server_sshKeyId_ssh-key_sshKeyId_fk", - "tableFrom": "server", - "tableTo": "ssh-key", - "columnsFrom": [ - "sshKeyId" - ], - "columnsTo": [ - "sshKeyId" - ], - "onDelete": "set null", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.preview_deployments": { - "name": "preview_deployments", - "schema": "", - "columns": { - "previewDeploymentId": { - "name": "previewDeploymentId", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "branch": { - "name": "branch", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestId": { - "name": "pullRequestId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestNumber": { - "name": "pullRequestNumber", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestURL": { - "name": "pullRequestURL", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestTitle": { - "name": "pullRequestTitle", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "pullRequestCommentId": { - "name": "pullRequestCommentId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "previewStatus": { - "name": "previewStatus", - "type": "applicationStatus", - "typeSchema": "public", - "primaryKey": false, - "notNull": true, - "default": "'idle'" - }, - "appName": { - "name": "appName", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "applicationId": { - "name": "applicationId", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "domainId": { - "name": "domainId", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "createdAt": { - "name": "createdAt", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expiresAt": { - "name": "expiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "preview_deployments_applicationId_application_applicationId_fk": { - "name": "preview_deployments_applicationId_application_applicationId_fk", - "tableFrom": "preview_deployments", - "tableTo": "application", - "columnsFrom": [ - "applicationId" - ], - "columnsTo": [ - "applicationId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - }, - "preview_deployments_domainId_domain_domainId_fk": { - "name": "preview_deployments_domainId_domain_domainId_fk", - "tableFrom": "preview_deployments", - "tableTo": "domain", - "columnsFrom": [ - "domainId" - ], - "columnsTo": [ - "domainId" - ], - "onDelete": "cascade", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "preview_deployments_appName_unique": { - "name": "preview_deployments_appName_unique", - "nullsNotDistinct": false, - "columns": [ - "appName" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.account": { - "name": "account", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "account_id": { - "name": "account_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "provider_id": { - "name": "provider_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "access_token": { - "name": "access_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "refresh_token": { - "name": "refresh_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "id_token": { - "name": "id_token", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "access_token_expires_at": { - "name": "access_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "refresh_token_expires_at": { - "name": "refresh_token_expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "scope": { - "name": "scope", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "password": { - "name": "password", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "is2FAEnabled": { - "name": "is2FAEnabled", - "type": "boolean", - "primaryKey": false, - "notNull": true, - "default": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "resetPasswordToken": { - "name": "resetPasswordToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "resetPasswordExpiresAt": { - "name": "resetPasswordExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationToken": { - "name": "confirmationToken", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "confirmationExpiresAt": { - "name": "confirmationExpiresAt", - "type": "text", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": { - "account_user_id_user_temp_id_fk": { - "name": "account_user_id_user_temp_id_fk", - "tableFrom": "account", - "tableTo": "user_temp", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.invitation": { - "name": "invitation", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "organization_id": { - "name": "organization_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "email": { - "name": "email", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "status": { - "name": "status", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "inviter_id": { - "name": "inviter_id", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "invitation_organization_id_organization_id_fk": { - "name": "invitation_organization_id_organization_id_fk", - "tableFrom": "invitation", - "tableTo": "organization", - "columnsFrom": [ - "organization_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "invitation_inviter_id_user_temp_id_fk": { - "name": "invitation_inviter_id_user_temp_id_fk", - "tableFrom": "invitation", - "tableTo": "user_temp", - "columnsFrom": [ - "inviter_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.member": { - "name": "member", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "organization_id": { - "name": "organization_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "user_id": { - "name": "user_id", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "role": { - "name": "role", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "member_organization_id_organization_id_fk": { - "name": "member_organization_id_organization_id_fk", - "tableFrom": "member", - "tableTo": "organization", - "columnsFrom": [ - "organization_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - }, - "member_user_id_user_temp_id_fk": { - "name": "member_user_id_user_temp_id_fk", - "tableFrom": "member", - "tableTo": "user_temp", - "columnsFrom": [ - "user_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.organization": { - "name": "organization", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "name": { - "name": "name", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "slug": { - "name": "slug", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "logo": { - "name": "logo", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "metadata": { - "name": "metadata", - "type": "text", - "primaryKey": false, - "notNull": false - }, - "owner_id": { - "name": "owner_id", - "type": "text", - "primaryKey": false, - "notNull": true - } - }, - "indexes": {}, - "foreignKeys": { - "organization_owner_id_user_temp_id_fk": { - "name": "organization_owner_id_user_temp_id_fk", - "tableFrom": "organization", - "tableTo": "user_temp", - "columnsFrom": [ - "owner_id" - ], - "columnsTo": [ - "id" - ], - "onDelete": "no action", - "onUpdate": "no action" - } - }, - "compositePrimaryKeys": {}, - "uniqueConstraints": { - "organization_slug_unique": { - "name": "organization_slug_unique", - "nullsNotDistinct": false, - "columns": [ - "slug" - ] - } - }, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - }, - "public.verification": { - "name": "verification", - "schema": "", - "columns": { - "id": { - "name": "id", - "type": "text", - "primaryKey": true, - "notNull": true - }, - "identifier": { - "name": "identifier", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "value": { - "name": "value", - "type": "text", - "primaryKey": false, - "notNull": true - }, - "expires_at": { - "name": "expires_at", - "type": "timestamp", - "primaryKey": false, - "notNull": true - }, - "created_at": { - "name": "created_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - }, - "updated_at": { - "name": "updated_at", - "type": "timestamp", - "primaryKey": false, - "notNull": false - } - }, - "indexes": {}, - "foreignKeys": {}, - "compositePrimaryKeys": {}, - "uniqueConstraints": {}, - "policies": {}, - "checkConstraints": {}, - "isRLSEnabled": false - } - }, - "enums": { - "public.buildType": { - "name": "buildType", - "schema": "public", - "values": [ - "dockerfile", - "heroku_buildpacks", - "paketo_buildpacks", - "nixpacks", - "static" - ] - }, - "public.sourceType": { - "name": "sourceType", - "schema": "public", - "values": [ - "docker", - "git", - "github", - "gitlab", - "bitbucket", - "drop" - ] - }, - "public.Roles": { - "name": "Roles", - "schema": "public", - "values": [ - "admin", - "user" - ] - }, - "public.domainType": { - "name": "domainType", - "schema": "public", - "values": [ - "compose", - "application", - "preview" - ] - }, - "public.databaseType": { - "name": "databaseType", - "schema": "public", - "values": [ - "postgres", - "mariadb", - "mysql", - "mongo" - ] - }, - "public.deploymentStatus": { - "name": "deploymentStatus", - "schema": "public", - "values": [ - "running", - "done", - "error" - ] - }, - "public.mountType": { - "name": "mountType", - "schema": "public", - "values": [ - "bind", - "volume", - "file" - ] - }, - "public.serviceType": { - "name": "serviceType", - "schema": "public", - "values": [ - "application", - "postgres", - "mysql", - "mariadb", - "mongo", - "redis", - "compose" - ] - }, - "public.protocolType": { - "name": "protocolType", - "schema": "public", - "values": [ - "tcp", - "udp" - ] - }, - "public.applicationStatus": { - "name": "applicationStatus", - "schema": "public", - "values": [ - "idle", - "running", - "done", - "error" - ] - }, - "public.certificateType": { - "name": "certificateType", - "schema": "public", - "values": [ - "letsencrypt", - "none" - ] - }, - "public.composeType": { - "name": "composeType", - "schema": "public", - "values": [ - "docker-compose", - "stack" - ] - }, - "public.sourceTypeCompose": { - "name": "sourceTypeCompose", - "schema": "public", - "values": [ - "git", - "github", - "gitlab", - "bitbucket", - "raw" - ] - }, - "public.RegistryType": { - "name": "RegistryType", - "schema": "public", - "values": [ - "selfHosted", - "cloud" - ] - }, - "public.notificationType": { - "name": "notificationType", - "schema": "public", - "values": [ - "slack", - "telegram", - "discord", - "email", - "gotify" - ] - }, - "public.gitProviderType": { - "name": "gitProviderType", - "schema": "public", - "values": [ - "github", - "gitlab", - "bitbucket" - ] - }, - "public.serverStatus": { - "name": "serverStatus", - "schema": "public", - "values": [ - "active", - "inactive" - ] - } - }, - "schemas": {}, - "sequences": {}, - "roles": {}, - "policies": {}, - "views": {}, - "_meta": { - "columns": {}, - "schemas": {}, - "tables": {} - } -} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index d10d6816e..39934064c 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -488,15 +488,8 @@ { "idx": 69, "version": "7", - "when": 1739432476590, - "tag": "0069_known_aqueduct", - "breakpoints": true - }, - { - "idx": 70, - "version": "7", - "when": 1739432513877, - "tag": "0070_overrated_the_stranger", + "when": 1739664410814, + "tag": "0069_broad_ken_ellis", "breakpoints": true } ] diff --git a/apps/dokploy/drizzle/old_migration b/apps/dokploy/drizzle/old_migration deleted file mode 100644 index d7e3acf0e..000000000 --- a/apps/dokploy/drizzle/old_migration +++ /dev/null @@ -1,278 +0,0 @@ -CREATE TABLE "account" ( - "id" text PRIMARY KEY NOT NULL, - "account_id" text NOT NULL, - "provider_id" text NOT NULL, - "user_id" text NOT NULL, - "access_token" text, - "refresh_token" text, - "id_token" text, - "access_token_expires_at" timestamp, - "refresh_token_expires_at" timestamp, - "scope" text, - "password" text, - "is2FAEnabled" boolean DEFAULT false NOT NULL, - "created_at" timestamp NOT NULL, - "updated_at" timestamp NOT NULL, - "resetPasswordToken" text, - "resetPasswordExpiresAt" text, - "confirmationToken" text, - "confirmationExpiresAt" text -); - ---> statement-breakpoint -CREATE TABLE "verification" ( - "id" text PRIMARY KEY NOT NULL, - "identifier" text NOT NULL, - "value" text NOT NULL, - "expires_at" timestamp NOT NULL, - "created_at" timestamp, - "updated_at" timestamp -); - --- Primero eliminar las restricciones NOT NULL y foreign keys -ALTER TABLE "user" ALTER COLUMN "adminId" DROP NOT NULL; -ALTER TABLE "user" ALTER COLUMN "authId" DROP NOT NULL; - -ALTER TABLE "user" DROP CONSTRAINT IF EXISTS "user_adminId_admin_adminId_fk"; -ALTER TABLE "user" DROP CONSTRAINT IF EXISTS "user_authId_auth_id_fk"; -ALTER TABLE "admin" DROP CONSTRAINT IF EXISTS "admin_authId_auth_id_fk"; -ALTER TABLE "project" DROP CONSTRAINT IF EXISTS "project_adminId_admin_adminId_fk"; -ALTER TABLE "destination" DROP CONSTRAINT IF EXISTS "destination_adminId_admin_adminId_fk"; -ALTER TABLE "certificate" DROP CONSTRAINT IF EXISTS "certificate_adminId_admin_adminId_fk"; -ALTER TABLE "session" DROP CONSTRAINT IF EXISTS "session_user_id_auth_id_fk"; -ALTER TABLE "registry" DROP CONSTRAINT IF EXISTS "registry_adminId_admin_adminId_fk"; -ALTER TABLE "notification" DROP CONSTRAINT IF EXISTS "notification_adminId_admin_adminId_fk"; -ALTER TABLE "ssh-key" DROP CONSTRAINT IF EXISTS "ssh-key_adminId_admin_adminId_fk"; -ALTER TABLE "git_provider" DROP CONSTRAINT IF EXISTS "git_provider_adminId_admin_adminId_fk"; -ALTER TABLE "server" DROP CONSTRAINT IF EXISTS "server_adminId_admin_adminId_fk"; - --- Luego renombrar las columnas -ALTER TABLE "user" RENAME COLUMN "userId" TO "id"; -ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "destination" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "certificate" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "registry" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "notification" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "ssh-key" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "git_provider" RENAME COLUMN "adminId" TO "userId"; -ALTER TABLE "server" RENAME COLUMN "adminId" TO "userId"; - --- Primero agregar todas las columnas sin restricciones -ALTER TABLE "user" ADD COLUMN "name" text; -ALTER TABLE "user" ADD COLUMN "email" text; -ALTER TABLE "user" ADD COLUMN "email_verified" boolean; -ALTER TABLE "user" ADD COLUMN "image" text; -ALTER TABLE "user" ADD COLUMN "role" text; -ALTER TABLE "user" ADD COLUMN "banned" boolean; -ALTER TABLE "user" ADD COLUMN "ban_reason" text; -ALTER TABLE "user" ADD COLUMN "ban_expires" timestamp; -ALTER TABLE "user" ADD COLUMN "updated_at" timestamp; -ALTER TABLE "user" ADD COLUMN "serverIp" text; -ALTER TABLE "user" ADD COLUMN "certificateType" "certificateType" DEFAULT 'none'; -ALTER TABLE "user" ADD COLUMN "host" text; -ALTER TABLE "user" ADD COLUMN "letsEncryptEmail" text; -ALTER TABLE "user" ADD COLUMN "sshPrivateKey" text; -ALTER TABLE "user" ADD COLUMN "enableDockerCleanup" boolean DEFAULT false; -ALTER TABLE "user" ADD COLUMN "enableLogRotation" boolean DEFAULT false; -ALTER TABLE "user" ADD COLUMN "enablePaidFeatures" boolean DEFAULT false; -ALTER TABLE "user" ADD COLUMN "metricsConfig" jsonb DEFAULT '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}'; -ALTER TABLE "user" ADD COLUMN "cleanupCacheApplications" boolean DEFAULT false; -ALTER TABLE "user" ADD COLUMN "cleanupCacheOnPreviews" boolean DEFAULT false; -ALTER TABLE "user" ADD COLUMN "cleanupCacheOnCompose" boolean DEFAULT false; - -ALTER TABLE "user" ALTER COLUMN "token" SET DEFAULT ''; -ALTER TABLE "user" ALTER COLUMN "expirationDate" SET DEFAULT CURRENT_TIMESTAMP + INTERVAL '1 year'; -ALTER TABLE "user" ALTER COLUMN "createdAt" SET DEFAULT to_char(CURRENT_TIMESTAMP, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"'); - ---> statement-breakpoint --- Luego actualizar los valores nulos -UPDATE "user" SET token = '' WHERE token IS NULL; -UPDATE "user" SET "expirationDate" = CURRENT_TIMESTAMP + INTERVAL '1 year' WHERE "expirationDate" IS NULL; -UPDATE "user" SET "createdAt" = to_char(CURRENT_TIMESTAMP, 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"') WHERE "createdAt" IS NULL; -UPDATE "user" SET "name" = '' WHERE "name" IS NULL; --- Generar emails únicos para registros vacíos -UPDATE "user" SET "email" = CONCAT('user_', id, '@dokploy.local') WHERE "email" = '' OR "email" IS NULL; -UPDATE "user" SET "email_verified" = COALESCE("email_verified", false) WHERE true; -UPDATE "user" SET "role" = COALESCE("role", 'user') WHERE true; -UPDATE "user" SET "banned" = COALESCE("banned", false) WHERE true; -UPDATE "user" SET "updated_at" = COALESCE("updated_at", CURRENT_TIMESTAMP) WHERE true; -UPDATE "user" SET "certificateType" = COALESCE("certificateType", 'none') WHERE true; -UPDATE "user" SET "enableDockerCleanup" = COALESCE("enableDockerCleanup", false) WHERE true; -UPDATE "user" SET "enableLogRotation" = COALESCE("enableLogRotation", false) WHERE true; -UPDATE "user" SET "enablePaidFeatures" = COALESCE("enablePaidFeatures", false) WHERE true; -UPDATE "user" SET "metricsConfig" = COALESCE("metricsConfig", '{"server":{"type":"Dokploy","refreshRate":60,"port":4500,"token":"","retentionDays":2,"cronJob":"","urlCallback":"","thresholds":{"cpu":0,"memory":0}},"containers":{"refreshRate":60,"services":{"include":[],"exclude":[]}}}') WHERE true; -UPDATE "user" SET "cleanupCacheApplications" = COALESCE("cleanupCacheApplications", false) WHERE true; -UPDATE "user" SET "cleanupCacheOnPreviews" = COALESCE("cleanupCacheOnPreviews", false) WHERE true; -UPDATE "user" SET "cleanupCacheOnCompose" = COALESCE("cleanupCacheOnCompose", false) WHERE true; ---> statement-breakpoint - --- Migrar datos de auth a user -INSERT INTO "user" ( - id, - name, - email, - email_verified, - image, - role, - updated_at -) -SELECT - id, - '' as name, - email, - true as email_verified, - image, - CASE - WHEN rol = 'admin' THEN 'admin' - ELSE 'user' - END as role, - CAST("createdAt" AS timestamp) as updated_at -FROM "auth"; - --- Migrar datos de auth a account -INSERT INTO "account" ( - id, - account_id, - provider_id, - user_id, - password, - "is2FAEnabled", - created_at, - updated_at -) -SELECT - id as id, - id as account_id, - 'credentials' as provider_id, - id as user_id, - password, - "is2FAEnabled", - CAST("createdAt" AS timestamp) as created_at, - CAST("createdAt" AS timestamp) as updated_at -FROM "auth"; - --- Migrar datos de admin a user -UPDATE "user" u -SET - "serverIp" = a."serverIp", - "certificateType" = a."certificateType", - "host" = a."host", - "letsEncryptEmail" = a."letsEncryptEmail", - "sshPrivateKey" = a."sshPrivateKey", - "enableDockerCleanup" = a."enableDockerCleanup", - "enableLogRotation" = a."enableLogRotation", - "enablePaidFeatures" = a."enablePaidFeatures", - "metricsConfig" = a."metricsConfig", - "cleanupCacheApplications" = a."cleanupCacheApplications", - "cleanupCacheOnPreviews" = a."cleanupCacheOnPreviews", - "cleanupCacheOnCompose" = a."cleanupCacheOnCompose" -FROM "admin" a -WHERE u.id = a."authId"; - --- Actualizar referencias en las tablas relacionadas -UPDATE "project" p -SET "userId" = a."authId" -FROM "admin" a -WHERE p."userId" = a."adminId"; - -UPDATE "destination" d -SET "userId" = a."authId" -FROM "admin" a -WHERE d."userId" = a."adminId"; - -UPDATE "certificate" c -SET "userId" = a."authId" -FROM "admin" a -WHERE c."userId" = a."adminId"; - -UPDATE "registry" r -SET "userId" = a."authId" -FROM "admin" a -WHERE r."userId" = a."adminId"; - -UPDATE "notification" n -SET "userId" = a."authId" -FROM "admin" a -WHERE n."userId" = a."adminId"; - -UPDATE "ssh-key" s -SET "userId" = a."authId" -FROM "admin" a -WHERE s."userId" = a."adminId"; - -UPDATE "git_provider" g -SET "userId" = a."authId" -FROM "admin" a -WHERE g."userId" = a."adminId"; - -UPDATE "server" s -SET "userId" = a."authId" -FROM "admin" a -WHERE s."userId" = a."adminId"; - --- Ahora agregar las restricciones NOT NULL después de migrar los datos -ALTER TABLE "user" ALTER COLUMN "name" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "email" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "email_verified" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "updated_at" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "certificateType" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "enableDockerCleanup" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "enableLogRotation" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "enablePaidFeatures" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "metricsConfig" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "cleanupCacheApplications" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "cleanupCacheOnPreviews" SET NOT NULL; -ALTER TABLE "user" ALTER COLUMN "cleanupCacheOnCompose" SET NOT NULL; - --- Modificar session -ALTER TABLE "session" ALTER COLUMN "expires_at" SET DATA TYPE timestamp; -ALTER TABLE "session" ADD COLUMN "token" text; -ALTER TABLE "session" ADD COLUMN "created_at" timestamp; -ALTER TABLE "session" ADD COLUMN "updated_at" timestamp; -ALTER TABLE "session" ADD COLUMN "ip_address" text; -ALTER TABLE "session" ADD COLUMN "user_agent" text; -ALTER TABLE "session" ADD COLUMN "impersonated_by" text; - --- Agregar nuevas restricciones después de migrar todos los datos -ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; -ALTER TABLE "project" ADD CONSTRAINT "project_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "destination" ADD CONSTRAINT "destination_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "certificate" ADD CONSTRAINT "certificate_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "session" ADD CONSTRAINT "session_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE no action ON UPDATE no action; -ALTER TABLE "registry" ADD CONSTRAINT "registry_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "notification" ADD CONSTRAINT "notification_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action; - --- Agregar restricciones únicas -ALTER TABLE "user" ADD CONSTRAINT "user_email_unique" UNIQUE("email"); -ALTER TABLE "session" ADD CONSTRAINT "session_token_unique" UNIQUE("token"); - --- Eliminar columnas antiguas -ALTER TABLE "user" DROP COLUMN IF EXISTS "adminId"; -ALTER TABLE "user" DROP COLUMN IF EXISTS "authId"; - --- Eliminar columnas de admin -ALTER TABLE "admin" DROP COLUMN IF EXISTS "adminId"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "serverIp"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "certificateType"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "host"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "letsEncryptEmail"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "sshPrivateKey"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "enableDockerCleanup"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "enableLogRotation"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "authId"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "createdAt"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "stripeCustomerId"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "stripeSubscriptionId"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "serversQuantity"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "enablePaidFeatures"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "metricsConfig"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "cleanupCacheApplications"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "cleanupCacheOnPreviews"; -ALTER TABLE "admin" DROP COLUMN IF EXISTS "cleanupCacheOnCompose"; - --- Eliminar tablas antiguas -DROP TABLE IF EXISTS "auth" CASCADE; -DROP TABLE IF EXISTS "admin" CASCADE; diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index fba21e234..f22967d59 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -111,7 +111,6 @@ export const users_temp = pgTable("user_temp", { email: text("email").notNull().unique(), emailVerified: boolean("email_verified").notNull(), image: text("image"), - role: text("role"), banned: boolean("banned"), banReason: text("ban_reason"), banExpires: timestamp("ban_expires"), From d233f2c764fc213d8c411391a4ac60f59116791a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 19:12:44 -0600 Subject: [PATCH 041/126] feat: adjust roles --- .../components/dashboard/projects/show.tsx | 6 +- apps/dokploy/pages/index.tsx | 1 - apps/dokploy/server/api/routers/auth.ts | 13 +- apps/dokploy/server/api/trpc.ts | 5 +- packages/server/src/db/schema/account.ts | 4 +- packages/server/src/db/schema/user.ts | 5 +- packages/server/src/lib/auth.ts | 167 ++++++---- packages/server/src/services/admin.ts | 299 +++++++++--------- 8 files changed, 273 insertions(+), 227 deletions(-) diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 98ef14a4b..206137f56 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -57,7 +57,7 @@ export const ShowProjects = () => { authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); const { mutateAsync } = api.project.remove.useMutation(); @@ -91,7 +91,7 @@ export const ShowProjects = () => { - {(auth?.role === "admin" || user?.canCreateProjects) && ( + {(auth?.role === "owner" || user?.canCreateProjects) && (
@@ -293,7 +293,7 @@ export const ShowProjects = () => {
e.stopPropagation()} > - {(auth?.role === "admin" || + {(auth?.role === "owner" || user?.canDeleteProjects) && ( diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 0fecc209c..b85b1c7e1 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -261,7 +261,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { } const { user } = await validateRequest(context.req); - console.log("Response", user); if (user) { return { diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 7f1382b49..ccb6b5ad7 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -7,6 +7,7 @@ import { apiVerify2FA, apiVerifyLogin2FA, auth, + member, } from "@/server/db/schema"; import { WEBSITE_URL } from "@/server/utils/stripe"; import { @@ -32,7 +33,7 @@ import { import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; import { isBefore } from "date-fns"; -import { eq } from "drizzle-orm"; +import { and, eq } from "drizzle-orm"; import { nanoid } from "nanoid"; import { z } from "zod"; import { db } from "../../db"; @@ -170,8 +171,14 @@ export const authRouter = createTRPCRouter({ }), get: protectedProcedure.query(async ({ ctx }) => { - const auth = await findAuthById(ctx.user.id); - return auth; + const memberResult = await db.query.member.findFirst({ + where: and( + eq(member.userId, ctx.user.id), + eq(member.organizationId, ctx.session?.activeOrganizationId || ""), + ), + }); + + return memberResult; }), logout: protectedProcedure.mutation(async ({ ctx }) => { diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 9f373ad3f..c88158b85 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -32,7 +32,7 @@ import { ZodError } from "zod"; interface CreateContextOptions { user: (User & { rol: "admin" | "user"; ownerId: string }) | null; - session: Session | null; + session: (Session & { activeOrganizationId: string }) | null; req: CreateNextContextOptions["req"]; res: CreateNextContextOptions["res"]; } @@ -75,12 +75,15 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { user = cookieResult.user; } + console.log("session", { session, user }); + return createInnerTRPCContext({ req, res, session: session, ...((user && { user: { + ...user, email: user.email, rol: user.role, id: user.id, diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 432753fde..0b5ef2707 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -77,7 +77,7 @@ export const member = pgTable("member", { userId: text("user_id") .notNull() .references(() => users_temp.id), - role: text("role").notNull(), + role: text("role").notNull().$type<"owner" | "member" | "admin">(), createdAt: timestamp("created_at").notNull(), }); @@ -98,7 +98,7 @@ export const invitation = pgTable("invitation", { .notNull() .references(() => organization.id), email: text("email").notNull(), - role: text("role"), + role: text("role").$type<"owner" | "member" | "admin">(), status: text("status").notNull(), expiresAt: timestamp("expires_at").notNull(), inviterId: text("inviter_id") diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index f22967d59..c05b734a7 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -10,7 +10,7 @@ import { import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { account } from "./account"; +import { account, organization } from "./account"; import { admins } from "./admin"; import { auth } from "./auth"; import { certificateType } from "./shared"; @@ -185,7 +185,7 @@ export const users_temp = pgTable("user_temp", { serversQuantity: integer("serversQuantity").notNull().default(0), }); -export const usersRelations = relations(users_temp, ({ one }) => ({ +export const usersRelations = relations(users_temp, ({ one, many }) => ({ // auth: one(auth, { // fields: [users.authId], // references: [auth.id], @@ -194,6 +194,7 @@ export const usersRelations = relations(users_temp, ({ one }) => ({ fields: [users_temp.id], references: [account.userId], }), + organizations: many(organization), // admin: one(admins, { // fields: [users.adminId], // references: [admins.adminId], diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 226ab0a8c..ce636c134 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -3,85 +3,116 @@ import * as bcrypt from "bcrypt"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { createAuthMiddleware, organization } from "better-auth/plugins"; -import { eq } from "drizzle-orm"; +import { desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; export const auth = betterAuth({ - database: drizzleAdapter(db, { - provider: "pg", - schema: schema, - }), + database: drizzleAdapter(db, { + provider: "pg", + schema: schema, + }), - emailAndPassword: { - enabled: true, + emailAndPassword: { + enabled: true, - password: { - async hash(password) { - return bcrypt.hashSync(password, 10); - }, - async verify({ hash, password }) { - return bcrypt.compareSync(password, hash); - }, - }, - }, - hooks: { - after: createAuthMiddleware(async (ctx) => { - if (ctx.path.startsWith("/sign-up")) { - const newSession = ctx.context.newSession; - await db - .update(schema.users_temp) - .set({ - role: "admin", - }) - .where(eq(schema.users_temp.id, newSession?.user?.id || "")); - } - }), - }, - user: { - modelName: "users_temp", - additionalFields: { - role: { - type: "string", - }, - ownerId: { - type: "string", - }, - }, - }, - plugins: [organization()], + password: { + async hash(password) { + return bcrypt.hashSync(password, 10); + }, + async verify({ hash, password }) { + return bcrypt.compareSync(password, hash); + }, + }, + }, + hooks: { + after: createAuthMiddleware(async (ctx) => { + if (ctx.path.startsWith("/sign-up")) { + const newSession = ctx.context.newSession; + const organization = await db + .insert(schema.organization) + .values({ + name: "My Organization", + ownerId: newSession?.user?.id || "", + createdAt: new Date(), + }) + .returning() + .then((res) => res[0]); + + await db.insert(schema.member).values({ + userId: newSession?.user?.id || "", + organizationId: organization?.id || "", + role: "owner", + createdAt: new Date(), + }); + } + }), + }, + databaseHooks: { + session: { + create: { + before: async (session) => { + const member = await db.query.member.findFirst({ + where: eq(schema.member.userId, session.userId), + orderBy: desc(schema.member.createdAt), + with: { + organization: true, + }, + }); + + return { + data: { + ...session, + activeOrganizationId: member?.organization.id, + }, + }; + }, + }, + }, + }, + user: { + modelName: "users_temp", + additionalFields: { + role: { + type: "string", + }, + ownerId: { + type: "string", + }, + }, + }, + plugins: [organization()], }); export const validateRequest = async (request: IncomingMessage) => { - const session = await auth.api.getSession({ - headers: new Headers({ - cookie: request.headers.cookie || "", - }), - }); + const session = await auth.api.getSession({ + headers: new Headers({ + cookie: request.headers.cookie || "", + }), + }); - if (!session?.session || !session.user) { - return { - session: null, - user: null, - }; - } + if (!session?.session || !session.user) { + return { + session: null, + user: null, + }; + } - if (session?.user) { - if (session?.user.role === "user") { - const owner = await db.query.member.findFirst({ - where: eq(schema.member.userId, session.user.id), - with: { - organization: true, - }, - }); + if (session?.user) { + const member = await db.query.member.findFirst({ + where: eq(schema.member.userId, session.user.id), + with: { + organization: true, + }, + }); - if (owner) { - session.user.ownerId = owner.organization.ownerId; - } - } else { - session.user.ownerId = session?.user?.id || ""; - } - } + session.user.role = member?.role || "member"; + if (member) { + session.user.ownerId = member.organization.ownerId; + } else { + session.user.ownerId = session.user.id; + } + } - return session; + return session; }; diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index e8a60498f..78a0375a8 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -1,10 +1,13 @@ import { randomBytes } from "node:crypto"; import { db } from "@dokploy/server/db"; import { - admins, - type apiCreateUserInvitation, - auth, - users_temp, + account, + admins, + type apiCreateUserInvitation, + auth, + member, + organization, + users_temp, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; @@ -13,188 +16,190 @@ import { IS_CLOUD } from "../constants"; export type Admin = typeof users_temp.$inferSelect; export const createInvitation = async ( - input: typeof apiCreateUserInvitation._type, - adminId: string + input: typeof apiCreateUserInvitation._type, + adminId: string, ) => { - await db.transaction(async (tx) => { - const result = await tx - .insert(auth) - .values({ - email: input.email.toLowerCase(), - rol: "user", - password: bcrypt.hashSync("01231203012312", 10), - }) - .returning() - .then((res) => res[0]); + await db.transaction(async (tx) => { + const result = await tx + .insert(auth) + .values({ + email: input.email.toLowerCase(), + rol: "user", + password: bcrypt.hashSync("01231203012312", 10), + }) + .returning() + .then((res) => res[0]); - if (!result) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - }); - } - const expiresIn24Hours = new Date(); - expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); - const token = randomBytes(32).toString("hex"); - await tx - .insert(users) - .values({ - adminId: adminId, - authId: result.id, - token, - expirationDate: expiresIn24Hours.toISOString(), - }) - .returning(); - }); + if (!result) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error creating the user", + }); + } + const expiresIn24Hours = new Date(); + expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); + const token = randomBytes(32).toString("hex"); + // await tx + // .insert(users) + // .values({ + // adminId: adminId, + // authId: result.id, + // token, + // expirationDate: expiresIn24Hours.toISOString(), + // }) + // .returning(); + }); }; export const findUserById = async (userId: string) => { - const user = await db.query.users_temp.findFirst({ - where: eq(users_temp.id, userId), - // with: { - // account: true, - // }, - }); - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - return user; + const user = await db.query.users_temp.findFirst({ + where: eq(users_temp.id, userId), + // with: { + // account: true, + // }, + }); + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "User not found", + }); + } + return user; }; export const updateUser = async (userId: string, userData: Partial) => { - const user = await db - .update(users_temp) - .set({ - ...userData, - }) - .where(eq(users_temp.id, userId)) - .returning() - .then((res) => res[0]); + const user = await db + .update(users_temp) + .set({ + ...userData, + }) + .where(eq(users_temp.id, userId)) + .returning() + .then((res) => res[0]); - return user; + return user; }; export const updateAdminById = async ( - adminId: string, - adminData: Partial + adminId: string, + adminData: Partial, ) => { - const admin = await db - .update(admins) - .set({ - ...adminData, - }) - .where(eq(admins.adminId, adminId)) - .returning() - .then((res) => res[0]); - - return admin; + // const admin = await db + // .update(admins) + // .set({ + // ...adminData, + // }) + // .where(eq(admins.adminId, adminId)) + // .returning() + // .then((res) => res[0]); + // return admin; }; export const findAdminById = async (userId: string) => { - const admin = await db.query.admins.findFirst({ - where: eq(admins.userId, userId), - }); - return admin; + const admin = await db.query.admins.findFirst({ + // where: eq(admins.userId, userId), + }); + return admin; }; export const isAdminPresent = async () => { - const admin = await db.query.users_temp.findFirst({ - where: eq(users_temp.role, "admin"), - }); - if (!admin) { - return false; - } - return true; + const admin = await db.query.member.findFirst({ + where: eq(member.role, "owner"), + }); + + console.log("admin", admin); + + if (!admin) { + return false; + } + return true; }; export const findAdminByAuthId = async (authId: string) => { - const admin = await db.query.admins.findFirst({ - where: eq(admins.authId, authId), - with: { - users: true, - }, - }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } - return admin; + const admin = await db.query.admins.findFirst({ + where: eq(admins.authId, authId), + with: { + users: true, + }, + }); + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } + return admin; }; export const findAdmin = async () => { - const admin = await db.query.admins.findFirst({}); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } - return admin; + const admin = await db.query.admins.findFirst({}); + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } + return admin; }; export const getUserByToken = async (token: string) => { - const user = await db.query.users.findFirst({ - where: eq(users.token, token), - with: { - auth: { - columns: { - password: false, - }, - }, - }, - }); + const user = await db.query.users.findFirst({ + where: eq(users.token, token), + with: { + auth: { + columns: { + password: false, + }, + }, + }, + }); - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Invitation not found", - }); - } - return { - ...user, - isExpired: user.isRegistered, - }; + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Invitation not found", + }); + } + return { + ...user, + isExpired: user.isRegistered, + }; }; export const removeUserById = async (userId: string) => { - await db - .delete(users_temp) - .where(eq(users_temp.id, userId)) - .returning() - .then((res) => res[0]); + await db + .delete(users_temp) + .where(eq(users_temp.id, userId)) + .returning() + .then((res) => res[0]); }; export const removeAdminByAuthId = async (authId: string) => { - const admin = await findAdminByAuthId(authId); - if (!admin) return null; + const admin = await findAdminByAuthId(authId); + if (!admin) return null; - // First delete all associated users - const users = admin.users; + // First delete all associated users + const users = admin.users; - for (const user of users) { - await removeUserById(user.id); - } - // Then delete the auth record which will cascade delete the admin - return await db - .delete(auth) - .where(eq(auth.id, authId)) - .returning() - .then((res) => res[0]); + for (const user of users) { + await removeUserById(user.id); + } + // Then delete the auth record which will cascade delete the admin + return await db + .delete(auth) + .where(eq(auth.id, authId)) + .returning() + .then((res) => res[0]); }; export const getDokployUrl = async () => { - if (IS_CLOUD) { - return "https://app.dokploy.com"; - } - const admin = await findAdmin(); + if (IS_CLOUD) { + return "https://app.dokploy.com"; + } + const admin = await findAdmin(); - if (admin.host) { - return `https://${admin.host}`; - } - return `http://${admin.serverIp}:${process.env.PORT}`; + if (admin.host) { + return `https://${admin.host}`; + } + return `http://${admin.serverIp}:${process.env.PORT}`; }; From 1c5cc5a0dbd539533917a8b9c14d50f887fba7aa Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 19:23:08 -0600 Subject: [PATCH 042/126] refactor: update roles --- .../settings/billing/show-welcome-dokploy.tsx | 6 +-- apps/dokploy/components/layouts/side.tsx | 36 +++++++-------- apps/dokploy/components/layouts/user-nav.tsx | 14 +++--- .../pages/api/providers/github/setup.ts | 2 +- apps/dokploy/pages/dashboard/docker.tsx | 2 +- .../pages/dashboard/project/[projectId].tsx | 4 +- .../services/application/[applicationId].tsx | 4 +- .../services/compose/[composeId].tsx | 4 +- .../services/mariadb/[mariadbId].tsx | 4 +- .../[projectId]/services/mongo/[mongoId].tsx | 4 +- .../[projectId]/services/mysql/[mysqlId].tsx | 4 +- .../services/postgres/[postgresId].tsx | 4 +- .../[projectId]/services/redis/[redisId].tsx | 4 +- .../pages/dashboard/settings/billing.tsx | 2 +- .../pages/dashboard/settings/certificates.tsx | 2 +- .../pages/dashboard/settings/cluster.tsx | 2 +- .../pages/dashboard/settings/destinations.tsx | 2 +- .../dashboard/settings/git-providers.tsx | 2 +- .../pages/dashboard/settings/index.tsx | 2 +- .../dashboard/settings/notifications.tsx | 2 +- .../pages/dashboard/settings/profile.tsx | 6 +-- .../pages/dashboard/settings/registry.tsx | 2 +- .../pages/dashboard/settings/server.tsx | 2 +- .../pages/dashboard/settings/servers.tsx | 2 +- .../pages/dashboard/settings/ssh-keys.tsx | 2 +- .../pages/dashboard/settings/users.tsx | 2 +- apps/dokploy/pages/dashboard/swarm.tsx | 2 +- apps/dokploy/pages/dashboard/traefik.tsx | 2 +- apps/dokploy/pages/swagger.tsx | 2 +- apps/dokploy/server/api/routers/admin.ts | 2 +- .../dokploy/server/api/routers/application.ts | 8 ++-- apps/dokploy/server/api/routers/auth.ts | 5 ++- apps/dokploy/server/api/routers/compose.ts | 12 ++--- apps/dokploy/server/api/routers/mariadb.ts | 8 ++-- apps/dokploy/server/api/routers/mongo.ts | 8 ++-- apps/dokploy/server/api/routers/mysql.ts | 8 ++-- apps/dokploy/server/api/routers/postgres.ts | 8 ++-- apps/dokploy/server/api/routers/project.ts | 10 ++--- apps/dokploy/server/api/routers/redis.ts | 8 ++-- apps/dokploy/server/api/routers/settings.ts | 6 +-- apps/dokploy/server/api/trpc.ts | 44 ++++++++++--------- packages/server/src/auth/auth.ts | 4 +- packages/server/src/auth/token.ts | 8 ++-- packages/server/src/lib/auth.ts | 2 + packages/server/src/utils/providers/gitlab.ts | 4 +- 45 files changed, 141 insertions(+), 132 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx b/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx index ca6a4daee..9464a565e 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx @@ -15,7 +15,7 @@ export const ShowWelcomeDokploy = () => { const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); - if (!isCloud || data?.rol !== "admin") { + if (!isCloud || data?.role !== "admin") { return null; } @@ -24,14 +24,14 @@ export const ShowWelcomeDokploy = () => { !isLoading && isCloud && !localStorage.getItem("hasSeenCloudWelcomeModal") && - data?.rol === "admin" + data?.role === "owner" ) { setOpen(true); } }, [isCloud, isLoading]); const handleClose = (isOpen: boolean) => { - if (data?.rol === "admin") { + if (data?.role === "owner") { setOpen(isOpen); if (!isOpen) { localStorage.setItem("hasSeenCloudWelcomeModal", "true"); // Establece el flag al cerrar el modal diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 88740054b..99938a5de 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -155,7 +155,7 @@ const MENU: Menu = { // Only enabled for admins and users with access to Traefik files in non-cloud environments isEnabled: ({ auth, user, isCloud }) => !!( - (auth?.role === "admin" || user?.canAccessToTraefikFiles) && + (auth?.role === "owner" || user?.canAccessToTraefikFiles) && !isCloud ), }, @@ -166,7 +166,7 @@ const MENU: Menu = { icon: BlocksIcon, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!((auth?.role === "admin" || user?.canAccessToDocker) && !isCloud), + !!((auth?.role === "owner" || user?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -175,7 +175,7 @@ const MENU: Menu = { icon: PieChart, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!((auth?.role === "admin" || user?.canAccessToDocker) && !isCloud), + !!((auth?.role === "owner" || user?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -184,7 +184,7 @@ const MENU: Menu = { icon: Forward, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!((auth?.role === "admin" || user?.canAccessToDocker) && !isCloud), + !!((auth?.role === "owner" || user?.canAccessToDocker) && !isCloud), }, // Legacy unused menu, adjusted to the new structure @@ -252,7 +252,7 @@ const MENU: Menu = { icon: Activity, // Only enabled for admins in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!(auth?.role === "admin" && !isCloud), + !!(auth?.role === "owner" && !isCloud), }, { isSingle: true, @@ -266,7 +266,7 @@ const MENU: Menu = { url: "/dashboard/settings/servers", icon: Server, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -274,7 +274,7 @@ const MENU: Menu = { icon: Users, url: "/dashboard/settings/users", // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -283,7 +283,7 @@ const MENU: Menu = { url: "/dashboard/settings/ssh-keys", // Only enabled for admins and users with access to SSH keys isEnabled: ({ auth, user }) => - !!(auth?.role === "admin" || user?.canAccessToSSHKeys), + !!(auth?.role === "owner" || user?.canAccessToSSHKeys), }, { isSingle: true, @@ -292,7 +292,7 @@ const MENU: Menu = { icon: GitBranch, // Only enabled for admins and users with access to Git providers isEnabled: ({ auth, user }) => - !!(auth?.role === "admin" || user?.canAccessToGitProviders), + !!(auth?.role === "owner" || user?.canAccessToGitProviders), }, { isSingle: true, @@ -300,7 +300,7 @@ const MENU: Menu = { url: "/dashboard/settings/registry", icon: Package, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -308,7 +308,7 @@ const MENU: Menu = { url: "/dashboard/settings/destinations", icon: Database, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), }, { @@ -317,7 +317,7 @@ const MENU: Menu = { url: "/dashboard/settings/certificates", icon: ShieldCheck, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -326,7 +326,7 @@ const MENU: Menu = { icon: Boxes, // Only enabled for admins in non-cloud environments isEnabled: ({ auth, user, isCloud }) => - !!(auth?.role === "admin" && !isCloud), + !!(auth?.role === "owner" && !isCloud), }, { isSingle: true, @@ -334,7 +334,7 @@ const MENU: Menu = { url: "/dashboard/settings/notifications", icon: Bell, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "admin"), + isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -343,7 +343,7 @@ const MENU: Menu = { icon: CreditCard, // Only enabled for admins in cloud environments isEnabled: ({ auth, user, isCloud }) => - !!(auth?.role === "admin" && isCloud), + !!(auth?.role === "owner" && isCloud), }, ], @@ -537,7 +537,7 @@ export default function Page({ children }: Props) { authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); @@ -557,7 +557,7 @@ export default function Page({ children }: Props) { // const showProjectsButton = // currentPath === "/dashboard/projects" && - // (auth?.rol === "admin" || user?.canCreateProjects); + // (auth?.rol === "owner" || user?.canCreateProjects); return ( ))} - {!isCloud && auth?.role === "admin" && ( + {!isCloud && auth?.role === "owner" && ( diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 18d459f07..421ea5f2b 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -37,7 +37,7 @@ export const UserNav = () => { authId: data?.id || "", }, { - enabled: !!data?.id && data?.rol === "user", + enabled: !!data?.id && data?.role === "member", }, ); const { locale, setLocale } = useLocale(); @@ -96,7 +96,7 @@ export const UserNav = () => { > Monitoring - {(data?.rol === "admin" || user?.canAccessToTraefikFiles) && ( + {(data?.role === "owner" || user?.canAccessToTraefikFiles) && ( { @@ -106,7 +106,7 @@ export const UserNav = () => { Traefik )} - {(data?.rol === "admin" || user?.canAccessToDocker) && ( + {(data?.role === "owner" || user?.canAccessToDocker) && ( { @@ -119,7 +119,7 @@ export const UserNav = () => { )} - {data?.rol === "admin" && ( + {data?.role === "owner" && ( { @@ -140,7 +140,7 @@ export const UserNav = () => { > Profile - {data?.rol === "admin" && ( + {data?.role === "owner" && ( { @@ -151,7 +151,7 @@ export const UserNav = () => { )} - {data?.rol === "admin" && ( + {data?.role === "owner" && ( { @@ -164,7 +164,7 @@ export const UserNav = () => { )} - {isCloud && data?.rol === "admin" && ( + {isCloud && data?.role === "owner" && ( { diff --git a/apps/dokploy/pages/api/providers/github/setup.ts b/apps/dokploy/pages/api/providers/github/setup.ts index 38a281b1b..bd123c687 100644 --- a/apps/dokploy/pages/api/providers/github/setup.ts +++ b/apps/dokploy/pages/api/providers/github/setup.ts @@ -42,7 +42,7 @@ export default async function handler( const auth = await findAuthById(value as string); let adminId = ""; - if (auth.role === "admin") { + if (auth.role === "owner") { const admin = await findAdminByAuthId(auth.id); adminId = admin.adminId; } else { diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index 5449b9bdc..051e4f0a9 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.role === "user") { + if (auth.role === "member") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index f8c39a3a3..87fa3df4f 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -206,7 +206,7 @@ const Project = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); const { data, isLoading, refetch } = api.project.one.useQuery({ projectId }); @@ -335,7 +335,7 @@ const Project = ( {data?.description} - {(auth?.role === "admin" || user?.canCreateServices) && ( + {(auth?.role === "owner" || user?.canCreateServices) && (
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index e2bf8455b..441720b94 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -93,7 +93,7 @@ const Service = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); @@ -186,7 +186,7 @@ const Service = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index ddc613e5f..41a346b4b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -87,7 +87,7 @@ const Service = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); @@ -181,7 +181,7 @@ const Service = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 813ea7292..7138be021 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -68,7 +68,7 @@ const Mariadb = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -154,7 +154,7 @@ const Mariadb = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index 0361b7e29..c0ddd96d5 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -68,7 +68,7 @@ const Mongo = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); @@ -156,7 +156,7 @@ const Mongo = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index eff9cfe79..d170bb76e 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -67,7 +67,7 @@ const MySql = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); @@ -156,7 +156,7 @@ const MySql = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 808c7aee4..67c6a4a5e 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -66,7 +66,7 @@ const Postgresql = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); @@ -154,7 +154,7 @@ const Postgresql = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 82180401b..bb9ebc4ae 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -67,7 +67,7 @@ const Redis = ( authId: auth?.id || "", }, { - enabled: !!auth?.id && auth?.role === "user", + enabled: !!auth?.id && auth?.role === "member", }, ); @@ -155,7 +155,7 @@ const Redis = (
- {(auth?.role === "admin" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || user?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/settings/billing.tsx b/apps/dokploy/pages/dashboard/settings/billing.tsx index dd70c22ae..b1a7cb21d 100644 --- a/apps/dokploy/pages/dashboard/settings/billing.tsx +++ b/apps/dokploy/pages/dashboard/settings/billing.tsx @@ -30,7 +30,7 @@ export async function getServerSideProps( } const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/certificates.tsx b/apps/dokploy/pages/dashboard/settings/certificates.tsx index d5bbf0332..14d020c41 100644 --- a/apps/dokploy/pages/dashboard/settings/certificates.tsx +++ b/apps/dokploy/pages/dashboard/settings/certificates.tsx @@ -25,7 +25,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index 9affb9ee2..dd12723c4 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -34,7 +34,7 @@ export async function getServerSideProps( }; } const { user, session } = await validateRequest(ctx.req, ctx.res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/destinations.tsx b/apps/dokploy/pages/dashboard/settings/destinations.tsx index 671087fe8..5d34ec5f4 100644 --- a/apps/dokploy/pages/dashboard/settings/destinations.tsx +++ b/apps/dokploy/pages/dashboard/settings/destinations.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index 6e8bf5874..c9135fee6 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -51,7 +51,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.role === "user") { + if (auth.role === "member") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/settings/index.tsx b/apps/dokploy/pages/dashboard/settings/index.tsx index 9b4c80485..577c2b7c7 100644 --- a/apps/dokploy/pages/dashboard/settings/index.tsx +++ b/apps/dokploy/pages/dashboard/settings/index.tsx @@ -190,7 +190,7 @@ export async function getServerSideProps( }, }; } - if (user.role === "user") { + if (user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/notifications.tsx b/apps/dokploy/pages/dashboard/settings/notifications.tsx index 054f92e5a..840eb9d4a 100644 --- a/apps/dokploy/pages/dashboard/settings/notifications.tsx +++ b/apps/dokploy/pages/dashboard/settings/notifications.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 00f10ec72..1d6d9896d 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -19,7 +19,7 @@ const Page = () => { authId: data?.id || "", }, { - enabled: !!data?.id && data?.role === "user", + enabled: !!data?.id && data?.role === "member", }, ); @@ -28,7 +28,7 @@ const Page = () => {
- {(user?.canAccessToAPI || data?.role === "admin") && } + {(user?.canAccessToAPI || data?.role === "owner") && } {isCloud && }
@@ -62,7 +62,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); await helpers.auth.get.prefetch(); - if (user?.role === "user") { + if (user?.role === "member") { await helpers.user.byAuthId.prefetch({ authId: user.authId, }); diff --git a/apps/dokploy/pages/dashboard/settings/registry.tsx b/apps/dokploy/pages/dashboard/settings/registry.tsx index 441bee395..61f62614a 100644 --- a/apps/dokploy/pages/dashboard/settings/registry.tsx +++ b/apps/dokploy/pages/dashboard/settings/registry.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index 98710d06e..498e97ce1 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -107,7 +107,7 @@ export async function getServerSideProps( }, }; } - if (user.role === "user") { + if (user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/servers.tsx b/apps/dokploy/pages/dashboard/settings/servers.tsx index 27f88be24..6f83fb669 100644 --- a/apps/dokploy/pages/dashboard/settings/servers.tsx +++ b/apps/dokploy/pages/dashboard/settings/servers.tsx @@ -36,7 +36,7 @@ export async function getServerSideProps( }, }; } - if (user.role === "user") { + if (user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index fcb11666f..d7876641f 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -51,7 +51,7 @@ export async function getServerSideProps( const auth = await helpers.auth.get.fetch(); await helpers.settings.isCloud.prefetch(); - if (auth.role === "user") { + if (auth.role === "member") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index ace439692..8f97e2b6c 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -26,7 +26,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req, res); - if (!user || user.role === "user") { + if (!user || user.role === "member") { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index 524b464af..f57728209 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.role === "user") { + if (auth.role === "member") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index b8ff13f3f..1f710c9eb 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); const auth = await helpers.auth.get.fetch(); - if (auth.role === "user") { + if (auth.role === "member") { const user = await helpers.user.byAuthId.fetch({ authId: auth.id, }); diff --git a/apps/dokploy/pages/swagger.tsx b/apps/dokploy/pages/swagger.tsx index 2113e4c85..765194f1c 100644 --- a/apps/dokploy/pages/swagger.tsx +++ b/apps/dokploy/pages/swagger.tsx @@ -58,7 +58,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { }, transformer: superjson, }); - if (user.role === "user") { + if (user.role === "member") { const result = await helpers.user.byAuthId.fetch({ authId: user.id, }); diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index e8467283a..55df35afe 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -39,7 +39,7 @@ export const adminRouter = createTRPCRouter({ update: adminProcedure .input(apiUpdateAdmin) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to update this admin", diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 0f7c1eb67..618fd9ce8 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -60,7 +60,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiCreateApplication) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -80,7 +80,7 @@ export const applicationRouter = createTRPCRouter({ } const newApplication = await createApplication(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newApplication.applicationId); } return newApplication; @@ -98,7 +98,7 @@ export const applicationRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOneApplication) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.applicationId, "access"); } const application = await findApplicationById(input.applicationId); @@ -140,7 +140,7 @@ export const applicationRouter = createTRPCRouter({ delete: protectedProcedure .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.applicationId, "delete"); } const application = await findApplicationById(input.applicationId); diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index ccb6b5ad7..cc149b3c0 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -176,6 +176,9 @@ export const authRouter = createTRPCRouter({ eq(member.userId, ctx.user.id), eq(member.organizationId, ctx.session?.activeOrganizationId || ""), ), + with: { + user: true, + }, }); return memberResult; @@ -251,7 +254,7 @@ export const authRouter = createTRPCRouter({ await lucia.invalidateSession(session.id); res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); - if (ctx.user.rol === "admin") { + if (ctx.user.rol === "owner") { await removeAdminByAuthId(ctx.user.authId); } else { await removeUserByAuthId(ctx.user.authId); diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index b3fbae93a..69586eb42 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -61,7 +61,7 @@ export const composeRouter = createTRPCRouter({ .input(apiCreateCompose) .mutation(async ({ ctx, input }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -80,7 +80,7 @@ export const composeRouter = createTRPCRouter({ } const newService = await createCompose(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newService.composeId); } @@ -93,7 +93,7 @@ export const composeRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindCompose) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.composeId, "access"); } @@ -122,7 +122,7 @@ export const composeRouter = createTRPCRouter({ delete: protectedProcedure .input(apiDeleteCompose) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.composeId, "delete"); } const composeResult = await findComposeById(input.composeId); @@ -376,7 +376,7 @@ export const composeRouter = createTRPCRouter({ deployTemplate: protectedProcedure .input(apiCreateComposeByTemplate) .mutation(async ({ ctx, input }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -419,7 +419,7 @@ export const composeRouter = createTRPCRouter({ isolatedDeployment: true, }); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, compose.composeId); } diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 9305395d3..e50b9b129 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -37,7 +37,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiCreateMariaDB) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -56,7 +56,7 @@ export const mariadbRouter = createTRPCRouter({ }); } const newMariadb = await createMariadb(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newMariadb.mariadbId); } @@ -79,7 +79,7 @@ export const mariadbRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOneMariaDB) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.mariadbId, "access"); } const mariadb = await findMariadbById(input.mariadbId); @@ -201,7 +201,7 @@ export const mariadbRouter = createTRPCRouter({ remove: protectedProcedure .input(apiFindOneMariaDB) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.mariadbId, "delete"); } diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index aed7b4c52..ddb3d3ccd 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -36,7 +36,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiCreateMongo) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -55,7 +55,7 @@ export const mongoRouter = createTRPCRouter({ }); } const newMongo = await createMongo(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newMongo.mongoId); } @@ -82,7 +82,7 @@ export const mongoRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOneMongo) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.mongoId, "access"); } @@ -242,7 +242,7 @@ export const mongoRouter = createTRPCRouter({ remove: protectedProcedure .input(apiFindOneMongo) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.mongoId, "delete"); } diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index e66c30941..c81fc8429 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -38,7 +38,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiCreateMySql) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -58,7 +58,7 @@ export const mysqlRouter = createTRPCRouter({ } const newMysql = await createMysql(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newMysql.mysqlId); } @@ -85,7 +85,7 @@ export const mysqlRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOneMySql) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.mysqlId, "access"); } const mysql = await findMySqlById(input.mysqlId); @@ -240,7 +240,7 @@ export const mysqlRouter = createTRPCRouter({ remove: protectedProcedure .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.mysqlId, "delete"); } const mongo = await findMySqlById(input.mysqlId); diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 0aab4dc65..8a4e8ba25 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -44,7 +44,7 @@ export const postgresRouter = createTRPCRouter({ .input(apiCreatePostgres) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -63,7 +63,7 @@ export const postgresRouter = createTRPCRouter({ }); } const newPostgres = await createPostgres(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newPostgres.postgresId); } @@ -90,7 +90,7 @@ export const postgresRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOnePostgres) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.postgresId, "access"); } @@ -221,7 +221,7 @@ export const postgresRouter = createTRPCRouter({ remove: protectedProcedure .input(apiFindOnePostgres) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.postgresId, "delete"); } const postgres = await findPostgresById(input.postgresId); diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index d4a30580a..bce12565f 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -36,7 +36,7 @@ export const projectRouter = createTRPCRouter({ .input(apiCreateProject) .mutation(async ({ ctx, input }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkProjectAccess(ctx.user.id, "create"); } @@ -50,7 +50,7 @@ export const projectRouter = createTRPCRouter({ } const project = await createProject(input, ctx.user.ownerId); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewProject(ctx.user.id, project.projectId); } @@ -67,7 +67,7 @@ export const projectRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOneProject) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { const { accessedServices } = await findUserByAuthId(ctx.user.id); await checkProjectAccess(ctx.user.id, "access", input.projectId); @@ -125,7 +125,7 @@ export const projectRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { // console.log(ctx.user); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { const { accessedProjects, accessedServices } = await findUserById( ctx.user.id, ); @@ -203,7 +203,7 @@ export const projectRouter = createTRPCRouter({ .input(apiRemoveProject) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkProjectAccess(ctx.user.id, "delete"); } const currentProject = await findProjectById(input.projectId); diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index 46586e5e2..acded3723 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -36,7 +36,7 @@ export const redisRouter = createTRPCRouter({ .input(apiCreateRedis) .mutation(async ({ input, ctx }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.projectId, "create"); } @@ -55,7 +55,7 @@ export const redisRouter = createTRPCRouter({ }); } const newRedis = await createRedis(input); - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await addNewService(ctx.user.id, newRedis.redisId); } @@ -75,7 +75,7 @@ export const redisRouter = createTRPCRouter({ one: protectedProcedure .input(apiFindOneRedis) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.redisId, "access"); } @@ -232,7 +232,7 @@ export const redisRouter = createTRPCRouter({ remove: protectedProcedure .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { await checkServiceAccess(ctx.user.id, input.redisId, "delete"); } diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index 6dda93835..cd1f8bd3b 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -382,7 +382,7 @@ export const settingsRouter = createTRPCRouter({ .input(apiServerSchema) .query(async ({ ctx, input }) => { try { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { const canAccess = await canAccessToTraefikFiles(ctx.user.authId); if (!canAccess) { @@ -400,7 +400,7 @@ export const settingsRouter = createTRPCRouter({ updateTraefikFile: protectedProcedure .input(apiModifyTraefikConfig) .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { const canAccess = await canAccessToTraefikFiles(ctx.user.authId); if (!canAccess) { @@ -418,7 +418,7 @@ export const settingsRouter = createTRPCRouter({ readTraefikFile: protectedProcedure .input(apiReadTraefikConfig) .query(async ({ input, ctx }) => { - if (ctx.user.rol === "user") { + if (ctx.user.rol === "member") { const canAccess = await canAccessToTraefikFiles(ctx.user.authId); if (!canAccess) { diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index c88158b85..4e777883a 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -32,7 +32,7 @@ import { ZodError } from "zod"; interface CreateContextOptions { user: (User & { rol: "admin" | "user"; ownerId: string }) | null; - session: (Session & { activeOrganizationId: string }) | null; + session: (Session & { activeOrganizationId?: string }) | null; req: CreateNextContextOptions["req"]; res: CreateNextContextOptions["res"]; } @@ -67,31 +67,35 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { const { req, res } = opts; // Get from the request - let { session, user } = await validateRequest(req); + const { session, user } = await validateRequest(req); - if (!session) { - const cookieResult = await validateRequest(req); - session = cookieResult.session; - user = cookieResult.user; - } + // if (!session) { + // const cookieResult = await validateRequest(req); + // session = cookieResult.session; + // user = cookieResult.user; + // } - console.log("session", { session, user }); + console.log("session", session); + console.log("user", user); return createInnerTRPCContext({ req, res, - session: session, - ...((user && { - user: { - ...user, - email: user.email, - rol: user.role, - id: user.id, - ownerId: user.ownerId, - }, - }) || { - user: null, - }), + session: session + ? { + ...session, + activeOrganizationId: session.activeOrganizationId ?? undefined, + } + : null, + user: user + ? { + ...user, + email: user.email, + rol: user.role as "admin" | "user", + id: user.id, + ownerId: user.ownerId, + } + : null, }); }; diff --git a/packages/server/src/auth/auth.ts b/packages/server/src/auth/auth.ts index 09372fa5b..ab18955f2 100644 --- a/packages/server/src/auth/auth.ts +++ b/packages/server/src/auth/auth.ts @@ -71,10 +71,10 @@ export async function validateRequest( } if (result.user) { try { - if (result.user?.rol === "admin") { + if (result.user?.rol === "owner") { const admin = await findAdminByAuthId(result.user.id); result.user.adminId = admin.adminId; - } else if (result.user?.rol === "user") { + } else if (result.user?.rol === "member") { const userResult = await findUserByAuthId(result.user.id); result.user.adminId = userResult.adminId; } diff --git a/packages/server/src/auth/token.ts b/packages/server/src/auth/token.ts index f29d4dbdd..9dcf99736 100644 --- a/packages/server/src/auth/token.ts +++ b/packages/server/src/auth/token.ts @@ -35,10 +35,10 @@ export const validateBearerToken = async ( const result = await luciaToken.validateSession(sessionId); if (result.user) { - if (result.user?.rol === "admin") { + if (result.user?.rol === "owner") { const admin = await findAdminByAuthId(result.user.id); result.user.adminId = admin.adminId; - } else if (result.user?.rol === "user") { + } else if (result.user?.rol === "member") { const userResult = await findUserByAuthId(result.user.id); result.user.adminId = userResult.adminId; } @@ -73,10 +73,10 @@ export const validateBearerTokenAPI = async ( const result = await luciaToken.validateSession(sessionId); if (result.user) { - if (result.user?.rol === "admin") { + if (result.user?.rol === "owner") { const admin = await findAdminByAuthId(result.user.id); result.user.adminId = admin.adminId; - } else if (result.user?.rol === "user") { + } else if (result.user?.rol === "member") { const userResult = await findUserByAuthId(result.user.id); result.user.adminId = userResult.adminId; } diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index ce636c134..522a921e8 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -75,9 +75,11 @@ export const auth = betterAuth({ additionalFields: { role: { type: "string", + required: true, }, ownerId: { type: "string", + required: true, }, }, }, diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index 096f9e284..a2c4b8ccb 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -268,7 +268,7 @@ export const getGitlabRepositories = async (gitlabId?: string) => { if (groupName) { return full_path.toLowerCase().includes(groupName) && kind === "group"; } - return kind === "user"; + return kind === "member"; }); const mappedRepositories = filteredRepos.map((repo: any) => { return { @@ -442,7 +442,7 @@ export const testGitlabConnection = async ( if (groupName) { return full_path.toLowerCase().includes(groupName) && kind === "group"; } - return kind === "user"; + return kind === "member"; }); return filteredRepos.length; From 8b71f963cc9fd5d111f4144c27674d15d53e6bd6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 19:35:22 -0600 Subject: [PATCH 043/126] refactor: update logic --- .../dashboard/settings/profile/generate-token.tsx | 2 +- .../dashboard/settings/profile/profile-form.tsx | 12 ++++++------ apps/dokploy/components/layouts/user-nav.tsx | 9 ++++++--- apps/dokploy/pages/dashboard/settings/profile.tsx | 4 ++-- apps/dokploy/server/api/trpc.ts | 8 ++++---- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx index 66486c330..ad7840b20 100644 --- a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx @@ -51,7 +51,7 @@ export const GenerateToken = () => {
diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 9ae140c67..4da97d18b 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -73,9 +73,9 @@ export const ProfileForm = () => { const form = useForm({ defaultValues: { - email: data?.email || "", + email: data?.user?.email || "", password: "", - image: data?.image || "", + image: data?.user?.image || "", currentPassword: "", }, resolver: zodResolver(profileSchema), @@ -84,14 +84,14 @@ export const ProfileForm = () => { useEffect(() => { if (data) { form.reset({ - email: data?.email || "", + email: data?.user?.email || "", password: "", - image: data?.image || "", + image: data?.user?.image || "", currentPassword: "", }); - if (data.email) { - generateSHA256Hash(data.email).then((hash) => { + if (data.user.email) { + generateSHA256Hash(data.user.email).then((hash) => { setGravatarHash(hash); }); } diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 421ea5f2b..d4de2019c 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -51,12 +51,15 @@ export const UserNav = () => { className="data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground" > - + CN
Account - {data?.email} + {data?.user?.email}
@@ -71,7 +74,7 @@ export const UserNav = () => { My Account - {data?.email} + {data?.user?.email} diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 1d6d9896d..656c30996 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -19,7 +19,7 @@ const Page = () => { authId: data?.id || "", }, { - enabled: !!data?.id && data?.role === "member", + enabled: !!data?.id && data?.role === "user", }, ); @@ -62,7 +62,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); await helpers.auth.get.prefetch(); - if (user?.role === "member") { + if (user?.role === "user") { await helpers.user.byAuthId.prefetch({ authId: user.authId, }); diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 4e777883a..98b5ab8e4 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -31,7 +31,7 @@ import { ZodError } from "zod"; */ interface CreateContextOptions { - user: (User & { rol: "admin" | "user"; ownerId: string }) | null; + user: (User & { rol: "member" | "admin" | "owner"; ownerId: string }) | null; session: (Session & { activeOrganizationId?: string }) | null; req: CreateNextContextOptions["req"]; res: CreateNextContextOptions["res"]; @@ -91,7 +91,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { ? { ...user, email: user.email, - rol: user.role as "admin" | "user", + rol: user.role as "owner" | "member" | "admin", id: user.id, ownerId: user.ownerId, } @@ -188,7 +188,7 @@ export const uploadProcedure = async (opts: any) => { }; export const cliProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session || !ctx.user || ctx.user.rol !== "admin") { + if (!ctx.session || !ctx.user || ctx.user.rol !== "owner") { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ @@ -202,7 +202,7 @@ export const cliProcedure = t.procedure.use(({ ctx, next }) => { }); export const adminProcedure = t.procedure.use(({ ctx, next }) => { - if (!ctx.session || !ctx.user || ctx.user.rol !== "admin") { + if (!ctx.session || !ctx.user || ctx.user.rol !== "owner") { throw new TRPCError({ code: "UNAUTHORIZED" }); } return next({ From 87b12ff6e91cc8df584cecac97e0b8d6a620ca97 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:06:33 -0600 Subject: [PATCH 044/126] refactor: update --- .../drizzle/0070_dusty_wind_dancer.sql | 2 + .../drizzle/0071_migrate-data-projects.sql | 27 + apps/dokploy/drizzle/meta/0070_snapshot.json | 5299 +++++++++++++++++ apps/dokploy/drizzle/meta/0071_snapshot.json | 5299 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 14 + apps/dokploy/migrate.ts | 24 + packages/server/auth-schema.ts | 149 +- packages/server/src/db/schema/project.ts | 8 + packages/server/src/db/schema/user.ts | 2 + 9 files changed, 10740 insertions(+), 84 deletions(-) create mode 100644 apps/dokploy/drizzle/0070_dusty_wind_dancer.sql create mode 100644 apps/dokploy/drizzle/0071_migrate-data-projects.sql create mode 100644 apps/dokploy/drizzle/meta/0070_snapshot.json create mode 100644 apps/dokploy/drizzle/meta/0071_snapshot.json diff --git a/apps/dokploy/drizzle/0070_dusty_wind_dancer.sql b/apps/dokploy/drizzle/0070_dusty_wind_dancer.sql new file mode 100644 index 000000000..c8308b4a8 --- /dev/null +++ b/apps/dokploy/drizzle/0070_dusty_wind_dancer.sql @@ -0,0 +1,2 @@ +ALTER TABLE "project" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0071_migrate-data-projects.sql b/apps/dokploy/drizzle/0071_migrate-data-projects.sql new file mode 100644 index 000000000..038dd0f62 --- /dev/null +++ b/apps/dokploy/drizzle/0071_migrate-data-projects.sql @@ -0,0 +1,27 @@ +-- Custom SQL migration file, put your code below! -- +-- Primero, actualizamos los proyectos con la organización del usuario +UPDATE "project" p +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = p."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE p."organizationId" IS NULL; + +-- Verificamos que todos los proyectos tengan una organización +DO $$ +BEGIN + IF EXISTS ( + SELECT 1 + FROM "project" + WHERE "organizationId" IS NULL + ) THEN + RAISE EXCEPTION 'Hay proyectos sin organización asignada'; + END IF; +END $$; + +-- Hacemos organization_id NOT NULL después de la migración +ALTER TABLE "project" + ALTER COLUMN "organizationId" SET NOT NULL; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0070_snapshot.json b/apps/dokploy/drizzle/meta/0070_snapshot.json new file mode 100644 index 000000000..e4c51a577 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0070_snapshot.json @@ -0,0 +1,5299 @@ +{ + "id": "cd06dbbf-61cb-4aba-b096-49a1850ff32b", + "prevId": "e0842a94-530d-4a3c-a6b1-16cf37618b8b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_temp_id_fk": { + "name": "project_userId_user_temp_id_fk", + "tableFrom": "project", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_temp_id_fk": { + "name": "destination_userId_user_temp_id_fk", + "tableFrom": "destination", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_temp_id_fk": { + "name": "certificate_userId_user_temp_id_fk", + "tableFrom": "certificate", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_temp_id_fk": { + "name": "registry_userId_user_temp_id_fk", + "tableFrom": "registry", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_userId_user_temp_id_fk": { + "name": "notification_userId_user_temp_id_fk", + "tableFrom": "notification", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_temp_id_fk": { + "name": "ssh-key_userId_user_temp_id_fk", + "tableFrom": "ssh-key", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_temp_id_fk": { + "name": "server_userId_user_temp_id_fk", + "tableFrom": "server", + "tableTo": "user_temp", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0071_snapshot.json b/apps/dokploy/drizzle/meta/0071_snapshot.json new file mode 100644 index 000000000..782aa6451 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0071_snapshot.json @@ -0,0 +1,5299 @@ +{ + "id": "30a4b462-2f38-42ae-8291-f6a2ce6d0bb4", + "prevId": "cd06dbbf-61cb-4aba-b096-49a1850ff32b", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "columnsFrom": [ + "registryId" + ], + "tableTo": "registry", + "columnsTo": [ + "registryId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "columnsFrom": [ + "githubId" + ], + "tableTo": "github", + "columnsTo": [ + "githubId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "columnsFrom": [ + "gitlabId" + ], + "tableTo": "gitlab", + "columnsTo": [ + "gitlabId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "columnsFrom": [ + "bitbucketId" + ], + "tableTo": "bitbucket", + "columnsTo": [ + "bitbucketId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "columnsFrom": [ + "adminId" + ], + "tableTo": "admin", + "columnsTo": [ + "adminId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "columnsFrom": [ + "authId" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "columnsFrom": [ + "authId" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "columns": [ + "email" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_userId_user_temp_id_fk": { + "name": "project_userId_user_temp_id_fk", + "tableFrom": "project", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "columnsFrom": [ + "previewDeploymentId" + ], + "tableTo": "preview_deployments", + "columnsTo": [ + "previewDeploymentId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "destinationId" + ], + "tableTo": "destination", + "columnsTo": [ + "destinationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "postgresId" + ], + "tableTo": "postgres", + "columnsTo": [ + "postgresId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mariadbId" + ], + "tableTo": "mariadb", + "columnsTo": [ + "mariadbId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mysqlId" + ], + "tableTo": "mysql", + "columnsTo": [ + "mysqlId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "columnsFrom": [ + "mongoId" + ], + "tableTo": "mongo", + "columnsTo": [ + "mongoId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_userId_user_temp_id_fk": { + "name": "destination_userId_user_temp_id_fk", + "tableFrom": "destination", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "columnsFrom": [ + "previewDeploymentId" + ], + "tableTo": "preview_deployments", + "columnsTo": [ + "previewDeploymentId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "postgresId" + ], + "tableTo": "postgres", + "columnsTo": [ + "postgresId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mariadbId" + ], + "tableTo": "mariadb", + "columnsTo": [ + "mariadbId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mongoId" + ], + "tableTo": "mongo", + "columnsTo": [ + "mongoId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "mysqlId" + ], + "tableTo": "mysql", + "columnsTo": [ + "mysqlId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "redisId" + ], + "tableTo": "redis", + "columnsTo": [ + "redisId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "columnsFrom": [ + "composeId" + ], + "tableTo": "compose", + "columnsTo": [ + "composeId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_userId_user_temp_id_fk": { + "name": "certificate_userId_user_temp_id_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "columns": [ + "certificatePath" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "columns": [ + "token" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "columnsFrom": [ + "user_id" + ], + "tableTo": "auth", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "columns": [ + "username", + "applicationId" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "projectId" + ], + "tableTo": "project", + "columnsTo": [ + "projectId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "githubId" + ], + "tableTo": "github", + "columnsTo": [ + "githubId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "gitlabId" + ], + "tableTo": "gitlab", + "columnsTo": [ + "gitlabId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "bitbucketId" + ], + "tableTo": "bitbucket", + "columnsTo": [ + "bitbucketId" + ], + "onUpdate": "no action", + "onDelete": "set null" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "columnsFrom": [ + "serverId" + ], + "tableTo": "server", + "columnsTo": [ + "serverId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_userId_user_temp_id_fk": { + "name": "registry_userId_user_temp_id_fk", + "tableFrom": "registry", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "slackId" + ], + "tableTo": "slack", + "columnsTo": [ + "slackId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "telegramId" + ], + "tableTo": "telegram", + "columnsTo": [ + "telegramId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "discordId" + ], + "tableTo": "discord", + "columnsTo": [ + "discordId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "emailId" + ], + "tableTo": "email", + "columnsTo": [ + "emailId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "columnsFrom": [ + "gotifyId" + ], + "tableTo": "gotify", + "columnsTo": [ + "gotifyId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "notification_userId_user_temp_id_fk": { + "name": "notification_userId_user_temp_id_fk", + "tableFrom": "notification", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_userId_user_temp_id_fk": { + "name": "ssh-key_userId_user_temp_id_fk", + "tableFrom": "ssh-key", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_userId_user_temp_id_fk": { + "name": "git_provider_userId_user_temp_id_fk", + "tableFrom": "git_provider", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "columnsFrom": [ + "gitProviderId" + ], + "tableTo": "git_provider", + "columnsTo": [ + "gitProviderId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_userId_user_temp_id_fk": { + "name": "server_userId_user_temp_id_fk", + "tableFrom": "server", + "columnsFrom": [ + "userId" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "columnsFrom": [ + "sshKeyId" + ], + "tableTo": "ssh-key", + "columnsTo": [ + "sshKeyId" + ], + "onUpdate": "no action", + "onDelete": "set null" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "columnsFrom": [ + "applicationId" + ], + "tableTo": "application", + "columnsTo": [ + "applicationId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "columnsFrom": [ + "domainId" + ], + "tableTo": "domain", + "columnsTo": [ + "domainId" + ], + "onUpdate": "no action", + "onDelete": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "columns": [ + "appName" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "columnsFrom": [ + "organization_id" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "columnsFrom": [ + "inviter_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "columnsFrom": [ + "organization_id" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "columnsFrom": [ + "user_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "columnsFrom": [ + "owner_id" + ], + "tableTo": "user_temp", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "columns": [ + "slug" + ], + "nullsNotDistinct": false + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "views": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 39934064c..c11391251 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -491,6 +491,20 @@ "when": 1739664410814, "tag": "0069_broad_ken_ellis", "breakpoints": true + }, + { + "idx": 70, + "version": "7", + "when": 1739671371444, + "tag": "0070_dusty_wind_dancer", + "breakpoints": true + }, + { + "idx": 71, + "version": "7", + "when": 1739671387634, + "tag": "0071_migrate-data-projects", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index d56006f18..38f48784c 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -124,3 +124,27 @@ await db .catch((error) => { console.error(error); }); + +await db + .transaction(async (db) => { + const projects = await db.query.projects.findMany({ + with: { + user: { + with: { + organizations: true, + }, + }, + }, + }); + for (const project of projects) { + const user = await db.update(schema.projects).set({ + organizationId: project.user.organizations[0]?.id || "", + }); + } + }) + .then(() => { + console.log("Migration finished"); + }) + .catch((error) => { + console.error(error); + }); diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index bfb03d2e4..20b0a2b61 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,96 +1,77 @@ -import { - boolean, - integer, - pgTable, - text, - timestamp, -} from "drizzle-orm/pg-core"; - -export const user = pgTable("user", { - id: text("id").primaryKey(), - name: text("name").notNull(), - email: text("email").notNull().unique(), - emailVerified: boolean("email_verified").notNull(), - image: text("image"), - createdAt: timestamp("created_at").notNull(), - updatedAt: timestamp("updated_at").notNull(), -}); +import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core"; + +export const users_temp = pgTable("users_temp", { + id: text("id").primaryKey(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + emailVerified: boolean('email_verified').notNull(), + image: text('image'), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull(), + role: text('role').notNull(), + ownerId: text('owner_id').notNull() + }); export const session = pgTable("session", { - id: text("id").primaryKey(), - expiresAt: timestamp("expires_at").notNull(), - token: text("token").notNull().unique(), - createdAt: timestamp("created_at").notNull(), - updatedAt: timestamp("updated_at").notNull(), - ipAddress: text("ip_address"), - userAgent: text("user_agent"), - userId: text("user_id") - .notNull() - .references(() => user.id), - activeOrganizationId: text("active_organization_id"), -}); + id: text("id").primaryKey(), + expiresAt: timestamp('expires_at').notNull(), + token: text('token').notNull().unique(), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull(), + ipAddress: text('ip_address'), + userAgent: text('user_agent'), + userId: text('user_id').notNull().references(()=> users_temp.id, { onDelete: 'cascade' }), + activeOrganizationId: text('active_organization_id') + }); export const account = pgTable("account", { - id: text("id").primaryKey(), - accountId: text("account_id").notNull(), - providerId: text("provider_id").notNull(), - userId: text("user_id") - .notNull() - .references(() => user.id), - accessToken: text("access_token"), - refreshToken: text("refresh_token"), - idToken: text("id_token"), - accessTokenExpiresAt: timestamp("access_token_expires_at"), - refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), - scope: text("scope"), - password: text("password"), - createdAt: timestamp("created_at").notNull(), - updatedAt: timestamp("updated_at").notNull(), -}); + id: text("id").primaryKey(), + accountId: text('account_id').notNull(), + providerId: text('provider_id').notNull(), + userId: text('user_id').notNull().references(()=> users_temp.id, { onDelete: 'cascade' }), + accessToken: text('access_token'), + refreshToken: text('refresh_token'), + idToken: text('id_token'), + accessTokenExpiresAt: timestamp('access_token_expires_at'), + refreshTokenExpiresAt: timestamp('refresh_token_expires_at'), + scope: text('scope'), + password: text('password'), + createdAt: timestamp('created_at').notNull(), + updatedAt: timestamp('updated_at').notNull() + }); export const verification = pgTable("verification", { - id: text("id").primaryKey(), - identifier: text("identifier").notNull(), - value: text("value").notNull(), - expiresAt: timestamp("expires_at").notNull(), - createdAt: timestamp("created_at"), - updatedAt: timestamp("updated_at"), -}); + id: text("id").primaryKey(), + identifier: text('identifier').notNull(), + value: text('value').notNull(), + expiresAt: timestamp('expires_at').notNull(), + createdAt: timestamp('created_at'), + updatedAt: timestamp('updated_at') + }); export const organization = pgTable("organization", { - id: text("id").primaryKey(), - name: text("name").notNull(), - slug: text("slug").unique(), - logo: text("logo"), - createdAt: timestamp("created_at").notNull(), - metadata: text("metadata"), - ownerId: text("owner_id") - .notNull() - .references(() => user.userId), -}); + id: text("id").primaryKey(), + name: text('name').notNull(), + slug: text('slug').unique(), + logo: text('logo'), + createdAt: timestamp('created_at').notNull(), + metadata: text('metadata') + }); export const member = pgTable("member", { - id: text("id").primaryKey(), - organizationId: text("organization_id") - .notNull() - .references(() => organization.id), - userId: text("user_id") - .notNull() - .references(() => user.userId), - role: text("role").notNull(), - createdAt: timestamp("created_at").notNull(), -}); + id: text("id").primaryKey(), + organizationId: text('organization_id').notNull().references(()=> organization.id, { onDelete: 'cascade' }), + userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }), + role: text('role').notNull(), + createdAt: timestamp('created_at').notNull() + }); export const invitation = pgTable("invitation", { - id: text("id").primaryKey(), - organizationId: text("organization_id") - .notNull() - .references(() => organization.id), - email: text("email").notNull(), - role: text("role"), - status: text("status").notNull(), - expiresAt: timestamp("expires_at").notNull(), - inviterId: text("inviter_id") - .notNull() - .references(() => user.userId), -}); + id: text("id").primaryKey(), + organizationId: text('organization_id').notNull().references(()=> organization.id, { onDelete: 'cascade' }), + email: text('email').notNull(), + role: text('role'), + status: text('status').notNull(), + expiresAt: timestamp('expires_at').notNull(), + inviterId: text('inviter_id').notNull().references(()=> user.id, { onDelete: 'cascade' }) + }); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index 60f7842b1..ee8859327 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -14,6 +14,7 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { users, users_temp } from "./user"; +import { organization } from "./account"; export const projects = pgTable("project", { projectId: text("projectId") @@ -31,6 +32,9 @@ export const projects = pgTable("project", { userId: text("userId") .notNull() .references(() => users_temp.id, { onDelete: "cascade" }), + organizationId: text("organizationId") + // .notNull() + .references(() => organization.id, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); @@ -42,6 +46,10 @@ export const projectRelations = relations(projects, ({ many, one }) => ({ mongo: many(mongo), redis: many(redis), compose: many(compose), + user: one(users_temp, { + fields: [projects.userId], + references: [users_temp.id], + }), // user: one(user, { // fields: [projects.userId], // references: [user.id], diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index c05b734a7..359cfbd45 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -14,6 +14,7 @@ import { account, organization } from "./account"; import { admins } from "./admin"; import { auth } from "./auth"; import { certificateType } from "./shared"; +import { projects } from "./project"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -199,6 +200,7 @@ export const usersRelations = relations(users_temp, ({ one, many }) => ({ // fields: [users.adminId], // references: [admins.adminId], // }), + projects: many(projects), })); const createSchema = createInsertSchema(users_temp, { From 53ce5e57faf313bc754007fdc52fd5b20251080d Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:25:58 -0600 Subject: [PATCH 045/126] refactor: update organization --- .../drizzle/0070_dusty_wind_dancer.sql | 2 - .../drizzle/0070_nervous_vivisector.sql | 16 + .../drizzle/0071_migrate-data-projects.sql | 135 +- apps/dokploy/drizzle/0072_lazy_pixie.sql | 32 + apps/dokploy/drizzle/meta/0070_snapshot.json | 135 +- apps/dokploy/drizzle/meta/0071_snapshot.json | 137 +- apps/dokploy/drizzle/meta/0072_snapshot.json | 5280 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 13 +- packages/server/src/db/schema/certificate.ts | 14 +- packages/server/src/db/schema/destination.ts | 12 +- packages/server/src/db/schema/git-provider.ts | 14 +- packages/server/src/db/schema/notification.ts | 14 +- packages/server/src/db/schema/project.ts | 19 +- packages/server/src/db/schema/registry.ts | 10 +- packages/server/src/db/schema/server.ts | 9 +- packages/server/src/db/schema/ssh-key.ts | 16 +- 16 files changed, 5776 insertions(+), 82 deletions(-) delete mode 100644 apps/dokploy/drizzle/0070_dusty_wind_dancer.sql create mode 100644 apps/dokploy/drizzle/0070_nervous_vivisector.sql create mode 100644 apps/dokploy/drizzle/0072_lazy_pixie.sql create mode 100644 apps/dokploy/drizzle/meta/0072_snapshot.json diff --git a/apps/dokploy/drizzle/0070_dusty_wind_dancer.sql b/apps/dokploy/drizzle/0070_dusty_wind_dancer.sql deleted file mode 100644 index c8308b4a8..000000000 --- a/apps/dokploy/drizzle/0070_dusty_wind_dancer.sql +++ /dev/null @@ -1,2 +0,0 @@ -ALTER TABLE "project" ADD COLUMN "organizationId" text;--> statement-breakpoint -ALTER TABLE "project" ADD CONSTRAINT "project_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0070_nervous_vivisector.sql b/apps/dokploy/drizzle/0070_nervous_vivisector.sql new file mode 100644 index 000000000..238a17693 --- /dev/null +++ b/apps/dokploy/drizzle/0070_nervous_vivisector.sql @@ -0,0 +1,16 @@ +ALTER TABLE "project" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "destination" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "certificate" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "registry" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "notification" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "ssh-key" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "git_provider" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "server" ADD COLUMN "organizationId" text;--> statement-breakpoint +ALTER TABLE "project" ADD CONSTRAINT "project_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "destination" ADD CONSTRAINT "destination_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "certificate" ADD CONSTRAINT "certificate_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "registry" ADD CONSTRAINT "registry_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "notification" ADD CONSTRAINT "notification_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "ssh-key" ADD CONSTRAINT "ssh-key_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "server" ADD CONSTRAINT "server_organizationId_organization_id_fk" FOREIGN KEY ("organizationId") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/0071_migrate-data-projects.sql b/apps/dokploy/drizzle/0071_migrate-data-projects.sql index 038dd0f62..27ca0bea0 100644 --- a/apps/dokploy/drizzle/0071_migrate-data-projects.sql +++ b/apps/dokploy/drizzle/0071_migrate-data-projects.sql @@ -1,5 +1,6 @@ --- Custom SQL migration file, put your code below! -- --- Primero, actualizamos los proyectos con la organización del usuario +-- Custom SQL migration file + +-- Actualizar projects UPDATE "project" p SET "organizationId" = ( SELECT m."organization_id" @@ -10,18 +11,132 @@ SET "organizationId" = ( ) WHERE p."organizationId" IS NULL; --- Verificamos que todos los proyectos tengan una organización +-- Actualizar servers +UPDATE "server" s +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = s."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE s."organizationId" IS NULL; + +-- Actualizar ssh-keys +UPDATE "ssh-key" k +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = k."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE k."organizationId" IS NULL; + +-- Actualizar destinations +UPDATE "destination" d +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = d."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE d."organizationId" IS NULL; + +-- Actualizar registry +UPDATE "registry" r +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = r."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE r."organizationId" IS NULL; + +-- Actualizar notifications +UPDATE "notification" n +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = n."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE n."organizationId" IS NULL; + +-- Actualizar certificates +UPDATE "certificate" c +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = c."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE c."organizationId" IS NULL; + +-- Actualizar git_provider +UPDATE "git_provider" g +SET "organizationId" = ( + SELECT m."organization_id" + FROM "member" m + WHERE m."user_id" = g."userId" + AND m."role" = 'owner' + LIMIT 1 +) +WHERE g."organizationId" IS NULL; + +-- Verificar que todos los recursos tengan una organización DO $$ BEGIN IF EXISTS ( - SELECT 1 - FROM "project" - WHERE "organizationId" IS NULL + SELECT 1 FROM "project" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "server" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "ssh-key" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "destination" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "registry" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "notification" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "certificate" WHERE "organizationId" IS NULL + UNION ALL + SELECT 1 FROM "git_provider" WHERE "organizationId" IS NULL ) THEN - RAISE EXCEPTION 'Hay proyectos sin organización asignada'; + RAISE EXCEPTION 'Hay recursos sin organización asignada'; END IF; END $$; --- Hacemos organization_id NOT NULL después de la migración -ALTER TABLE "project" - ALTER COLUMN "organizationId" SET NOT NULL; \ No newline at end of file +-- Hacer organization_id NOT NULL en todas las tablas +ALTER TABLE "project" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "server" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "ssh-key" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "destination" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "registry" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "notification" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "certificate" ALTER COLUMN "organizationId" SET NOT NULL; +ALTER TABLE "git_provider" ALTER COLUMN "organizationId" SET NOT NULL; + +-- Crear índices para mejorar el rendimiento de búsquedas por organización +CREATE INDEX IF NOT EXISTS "idx_project_organization" ON "project" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_server_organization" ON "server" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_sshkey_organization" ON "ssh-key" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_destination_organization" ON "destination" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_registry_organization" ON "registry" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_notification_organization" ON "notification" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_certificate_organization" ON "certificate" ("organizationId"); +CREATE INDEX IF NOT EXISTS "idx_git_provider_organization" ON "git_provider" ("organizationId"); + + + + + + + + + diff --git a/apps/dokploy/drizzle/0072_lazy_pixie.sql b/apps/dokploy/drizzle/0072_lazy_pixie.sql new file mode 100644 index 000000000..f86f444d4 --- /dev/null +++ b/apps/dokploy/drizzle/0072_lazy_pixie.sql @@ -0,0 +1,32 @@ +ALTER TABLE "project" DROP CONSTRAINT "project_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "destination" DROP CONSTRAINT "destination_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "certificate" DROP CONSTRAINT "certificate_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "registry" DROP CONSTRAINT "registry_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "notification" DROP CONSTRAINT "notification_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "ssh-key" DROP CONSTRAINT "ssh-key_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "git_provider" DROP CONSTRAINT "git_provider_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "server" DROP CONSTRAINT "server_userId_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "project" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "destination" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "certificate" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "registry" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "notification" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "ssh-key" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "git_provider" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "server" ALTER COLUMN "organizationId" SET NOT NULL;--> statement-breakpoint +ALTER TABLE "project" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "destination" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "certificate" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "registry" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "notification" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "ssh-key" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "git_provider" DROP COLUMN "userId";--> statement-breakpoint +ALTER TABLE "server" DROP COLUMN "userId"; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0070_snapshot.json b/apps/dokploy/drizzle/meta/0070_snapshot.json index e4c51a577..681bded7e 100644 --- a/apps/dokploy/drizzle/meta/0070_snapshot.json +++ b/apps/dokploy/drizzle/meta/0070_snapshot.json @@ -1,5 +1,5 @@ { - "id": "cd06dbbf-61cb-4aba-b096-49a1850ff32b", + "id": "8e8626de-fb34-4c11-b9d0-b7a958993fb7", "prevId": "e0842a94-530d-4a3c-a6b1-16cf37618b8b", "version": "7", "dialect": "postgresql", @@ -2352,6 +2352,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -2368,6 +2374,19 @@ ], "onDelete": "cascade", "onUpdate": "no action" + }, + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -2761,6 +2780,12 @@ "primaryKey": false, "notNull": true }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, "serverId": { "name": "serverId", "type": "text", @@ -2783,6 +2808,19 @@ "onDelete": "cascade", "onUpdate": "no action" }, + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, "certificate_serverId_server_serverId_fk": { "name": "certificate_serverId_server_serverId_fk", "tableFrom": "certificate", @@ -3677,6 +3715,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -3693,6 +3737,19 @@ ], "onDelete": "cascade", "onUpdate": "no action" + }, + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -3937,6 +3994,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -4018,6 +4081,19 @@ ], "onDelete": "cascade", "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4140,6 +4216,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -4156,6 +4238,19 @@ ], "onDelete": "cascade", "onUpdate": "no action" + }, + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4199,6 +4294,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -4215,6 +4316,19 @@ ], "onDelete": "cascade", "onUpdate": "no action" + }, + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" } }, "compositePrimaryKeys": {}, @@ -4515,6 +4629,12 @@ "primaryKey": false, "notNull": true }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, "serverStatus": { "name": "serverStatus", "type": "serverStatus", @@ -4559,6 +4679,19 @@ "onDelete": "cascade", "onUpdate": "no action" }, + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, "server_sshKeyId_ssh-key_sshKeyId_fk": { "name": "server_sshKeyId_ssh-key_sshKeyId_fk", "tableFrom": "server", diff --git a/apps/dokploy/drizzle/meta/0071_snapshot.json b/apps/dokploy/drizzle/meta/0071_snapshot.json index 782aa6451..4214343a0 100644 --- a/apps/dokploy/drizzle/meta/0071_snapshot.json +++ b/apps/dokploy/drizzle/meta/0071_snapshot.json @@ -1,6 +1,6 @@ { - "id": "30a4b462-2f38-42ae-8291-f6a2ce6d0bb4", - "prevId": "cd06dbbf-61cb-4aba-b096-49a1850ff32b", + "id": "11cb27c0-0f5a-4ec9-98be-5b6f7c5e7799", + "prevId": "8e8626de-fb34-4c11-b9d0-b7a958993fb7", "version": "7", "dialect": "postgresql", "tables": { @@ -2352,6 +2352,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -2368,6 +2374,19 @@ ], "onUpdate": "no action", "onDelete": "cascade" + }, + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, @@ -2761,6 +2780,12 @@ "primaryKey": false, "notNull": true }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, "serverId": { "name": "serverId", "type": "text", @@ -2783,6 +2808,19 @@ "onUpdate": "no action", "onDelete": "cascade" }, + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, "certificate_serverId_server_serverId_fk": { "name": "certificate_serverId_server_serverId_fk", "tableFrom": "certificate", @@ -3677,6 +3715,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -3693,6 +3737,19 @@ ], "onUpdate": "no action", "onDelete": "cascade" + }, + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, @@ -3937,6 +3994,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -4018,6 +4081,19 @@ ], "onUpdate": "no action", "onDelete": "cascade" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, @@ -4140,6 +4216,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -4156,6 +4238,19 @@ ], "onUpdate": "no action", "onDelete": "cascade" + }, + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, @@ -4199,6 +4294,12 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false } }, "indexes": {}, @@ -4215,6 +4316,19 @@ ], "onUpdate": "no action", "onDelete": "cascade" + }, + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" } }, "compositePrimaryKeys": {}, @@ -4515,6 +4629,12 @@ "primaryKey": false, "notNull": true }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, "serverStatus": { "name": "serverStatus", "type": "serverStatus", @@ -4559,6 +4679,19 @@ "onUpdate": "no action", "onDelete": "cascade" }, + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "columnsFrom": [ + "organizationId" + ], + "tableTo": "organization", + "columnsTo": [ + "id" + ], + "onUpdate": "no action", + "onDelete": "cascade" + }, "server_sshKeyId_ssh-key_sshKeyId_fk": { "name": "server_sshKeyId_ssh-key_sshKeyId_fk", "tableFrom": "server", diff --git a/apps/dokploy/drizzle/meta/0072_snapshot.json b/apps/dokploy/drizzle/meta/0072_snapshot.json new file mode 100644 index 000000000..2f38d98d9 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0072_snapshot.json @@ -0,0 +1,5280 @@ +{ + "id": "4eb71c0e-5bdb-427b-b198-39b1059dcd16", + "prevId": "11cb27c0-0f5a-4ec9-98be-5b6f7c5e7799", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "userId": { + "name": "userId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "timestamp(3)", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "user_adminId_admin_adminId_fk": { + "name": "user_adminId_admin_adminId_fk", + "tableFrom": "user", + "tableTo": "admin", + "columnsFrom": [ + "adminId" + ], + "columnsTo": [ + "adminId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "user_authId_auth_id_fk": { + "name": "user_authId_auth_id_fk", + "tableFrom": "user", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.admin": { + "name": "admin", + "schema": "", + "columns": { + "adminId": { + "name": "adminId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "authId": { + "name": "authId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "admin_authId_auth_id_fk": { + "name": "admin_authId_auth_id_fk", + "tableFrom": "admin", + "tableTo": "auth", + "columnsFrom": [ + "authId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.auth": { + "name": "auth", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rol": { + "name": "rol", + "type": "Roles", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "auth_email_unique": { + "name": "auth_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_user_id_auth_id_fk": { + "name": "session_user_id_auth_id_fk", + "tableFrom": "session", + "tableTo": "auth", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.Roles": { + "name": "Roles", + "schema": "public", + "values": [ + "admin", + "user" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index c11391251..aae9728d3 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -495,16 +495,23 @@ { "idx": 70, "version": "7", - "when": 1739671371444, - "tag": "0070_dusty_wind_dancer", + "when": 1739671869809, + "tag": "0070_nervous_vivisector", "breakpoints": true }, { "idx": 71, "version": "7", - "when": 1739671387634, + "when": 1739671878698, "tag": "0071_migrate-data-projects", "breakpoints": true + }, + { + "idx": 72, + "version": "7", + "when": 1739672367223, + "tag": "0072_lazy_pixie", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index d31e5929f..c78ee9986 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -8,6 +8,7 @@ import { server } from "./server"; import { users_temp } from "./user"; // import { user } from "./user"; import { generateAppName } from "./utils"; +import { organization } from "./account"; export const certificates = pgTable("certificate", { certificateId: text("certificateId") @@ -22,12 +23,9 @@ export const certificates = pgTable("certificate", { .$defaultFn(() => generateAppName("certificate")) .unique(), autoRenew: boolean("autoRenew"), - // userId: text("userId").references(() => user.userId, { - // onDelete: "cascade", - // }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), serverId: text("serverId").references(() => server.serverId, { onDelete: "cascade", }), @@ -40,9 +38,9 @@ export const certificatesRelations = relations( fields: [certificates.serverId], references: [server.serverId], }), - user: one(users_temp, { - fields: [certificates.userId], - references: [users_temp.id], + organization: one(organization, { + fields: [certificates.organizationId], + references: [organization.id], }), }), ); diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index 6b9ea5d93..b438d20bb 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -6,6 +6,7 @@ import { z } from "zod"; import { admins } from "./admin"; import { backups } from "./backups"; import { users_temp } from "./user"; +import { organization } from "./account"; // import { user } from "./user"; export const destinations = pgTable("destination", { @@ -21,18 +22,19 @@ export const destinations = pgTable("destination", { region: text("region").notNull(), // maybe it can be null endpoint: text("endpoint").notNull(), - // userId: text("userId") - // .notNull() - // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), }); export const destinationsRelations = relations( destinations, ({ many, one }) => ({ backups: many(backups), + organization: one(organization, { + fields: [destinations.organizationId], + references: [organization.id], + }), // user: one(user, { // fields: [destinations.userId], // references: [user.id], diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index be2c50001..40c4558af 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -8,6 +8,7 @@ import { bitbucket } from "./bitbucket"; import { github } from "./github"; import { gitlab } from "./gitlab"; import { users_temp } from "./user"; +import { organization } from "./account"; // import { user } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ @@ -26,12 +27,9 @@ export const gitProvider = pgTable("git_provider", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - // userId: text("userId").references(() => user.userId, { - // onDelete: "cascade", - // }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), }); export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ @@ -47,9 +45,9 @@ export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ fields: [gitProvider.gitProviderId], references: [bitbucket.gitProviderId], }), - user: one(users_temp, { - fields: [gitProvider.userId], - references: [users_temp.id], + organization: one(organization, { + fields: [gitProvider.organizationId], + references: [organization.id], }), })); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 78596b0d7..5d2bb243d 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -4,6 +4,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { users_temp } from "./user"; +import { organization } from "./account"; // import { user } from "./user"; export const notificationType = pgEnum("notificationType", [ @@ -45,12 +46,9 @@ export const notifications = pgTable("notification", { gotifyId: text("gotifyId").references(() => gotify.gotifyId, { onDelete: "cascade", }), - // userId: text("userId").references(() => user.userId, { - // onDelete: "cascade", - // }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), }); export const slack = pgTable("slack", { @@ -125,9 +123,9 @@ export const notificationsRelations = relations(notifications, ({ one }) => ({ fields: [notifications.gotifyId], references: [gotify.gotifyId], }), - user: one(users_temp, { - fields: [notifications.userId], - references: [users_temp.id], + organization: one(organization, { + fields: [notifications.organizationId], + references: [organization.id], }), })); diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index ee8859327..193fce47c 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -26,14 +26,9 @@ export const projects = pgTable("project", { createdAt: text("createdAt") .notNull() .$defaultFn(() => new Date().toISOString()), - // userId: text("userId") - // .notNull() - // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") - .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + organizationId: text("organizationId") - // .notNull() + .notNull() .references(() => organization.id, { onDelete: "cascade" }), env: text("env").notNull().default(""), }); @@ -46,14 +41,10 @@ export const projectRelations = relations(projects, ({ many, one }) => ({ mongo: many(mongo), redis: many(redis), compose: many(compose), - user: one(users_temp, { - fields: [projects.userId], - references: [users_temp.id], + organization: one(organization, { + fields: [projects.organizationId], + references: [organization.id], }), - // user: one(user, { - // fields: [projects.userId], - // references: [user.id], - // }), })); const createSchema = createInsertSchema(projects, { diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 62c2b2d7d..932b198f2 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -6,6 +6,7 @@ import { z } from "zod"; import { admins } from "./admin"; import { applications } from "./application"; import { users_temp } from "./user"; +import { organization } from "./account"; // import { user } from "./user"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same @@ -29,12 +30,9 @@ export const registry = pgTable("registry", { .notNull() .$defaultFn(() => new Date().toISOString()), registryType: registryType("selfHosted").notNull().default("cloud"), - // userId: text("userId") - // .notNull() - // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), }); export const registryRelations = relations(registry, ({ one, many }) => ({ @@ -50,7 +48,7 @@ const createSchema = createInsertSchema(registry, { username: z.string().min(1), password: z.string().min(1), registryUrl: z.string(), - userId: z.string().min(1), + organizationId: z.string().min(1), registryId: z.string().min(1), registryType: z.enum(["cloud"]), imagePrefix: z.string().nullable().optional(), diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index 26023e96c..971c69164 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -25,6 +25,7 @@ import { sshKeys } from "./ssh-key"; import { users_temp } from "./user"; // import { user } from "./user"; import { generateAppName } from "./utils"; +import { organization } from "./account"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); @@ -43,13 +44,9 @@ export const server = pgTable("server", { .$defaultFn(() => generateAppName("server")), enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), createdAt: text("createdAt").notNull(), - // .$defaultFn(() => new Date().toISOString()), - // userId: text("userId") - // .notNull() - // .references(() => user.userId, { onDelete: "cascade" }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), serverStatus: serverStatus("serverStatus").notNull().default("active"), command: text("command").notNull().default(""), sshKeyId: text("sshKeyId").references(() => sshKeys.sshKeyId, { diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index 4daa438ce..0b0124953 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -8,6 +8,7 @@ import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; import { users_temp } from "./user"; +import { organization } from "./account"; // import { user } from "./user"; export const sshKeys = pgTable("ssh-key", { @@ -23,21 +24,18 @@ export const sshKeys = pgTable("ssh-key", { .notNull() .$defaultFn(() => new Date().toISOString()), lastUsedAt: text("lastUsedAt"), - // userId: text("userId").references(() => user.userId, { - // onDelete: "cascade", - // }), - userId: text("userId") + organizationId: text("organizationId") .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), + .references(() => organization.id, { onDelete: "cascade" }), }); export const sshKeysRelations = relations(sshKeys, ({ many, one }) => ({ applications: many(applications), compose: many(compose), servers: many(server), - user: one(users_temp, { - fields: [sshKeys.userId], - references: [users_temp.id], + organization: one(organization, { + fields: [sshKeys.organizationId], + references: [organization.id], }), })); @@ -53,7 +51,7 @@ export const apiCreateSshKey = createSchema description: true, privateKey: true, publicKey: true, - userId: true, + organizationId: true, }) .merge(sshKeyCreate.pick({ privateKey: true })); From 6d0e195a4d3af51d9a0b280ae4a20a0493cdb35e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:26:05 -0600 Subject: [PATCH 046/126] refactor: update --- packages/server/auth-schema.ts | 146 ++++++++++-------- packages/server/src/db/schema/certificate.ts | 2 +- packages/server/src/db/schema/destination.ts | 2 +- packages/server/src/db/schema/git-provider.ts | 2 +- packages/server/src/db/schema/notification.ts | 2 +- packages/server/src/db/schema/project.ts | 2 +- packages/server/src/db/schema/registry.ts | 2 +- packages/server/src/db/schema/server.ts | 2 +- packages/server/src/db/schema/ssh-key.ts | 2 +- packages/server/src/db/schema/user.ts | 2 +- 10 files changed, 91 insertions(+), 73 deletions(-) diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 20b0a2b61..38839afbf 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,77 +1,95 @@ -import { pgTable, text, integer, timestamp, boolean } from "drizzle-orm/pg-core"; - +import { + boolean, + integer, + pgTable, + text, + timestamp, +} from "drizzle-orm/pg-core"; + export const users_temp = pgTable("users_temp", { - id: text("id").primaryKey(), - name: text('name').notNull(), - email: text('email').notNull().unique(), - emailVerified: boolean('email_verified').notNull(), - image: text('image'), - createdAt: timestamp('created_at').notNull(), - updatedAt: timestamp('updated_at').notNull(), - role: text('role').notNull(), - ownerId: text('owner_id').notNull() - }); + id: text("id").primaryKey(), + name: text("name").notNull(), + email: text("email").notNull().unique(), + emailVerified: boolean("email_verified").notNull(), + image: text("image"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + role: text("role").notNull(), + ownerId: text("owner_id").notNull(), +}); export const session = pgTable("session", { - id: text("id").primaryKey(), - expiresAt: timestamp('expires_at').notNull(), - token: text('token').notNull().unique(), - createdAt: timestamp('created_at').notNull(), - updatedAt: timestamp('updated_at').notNull(), - ipAddress: text('ip_address'), - userAgent: text('user_agent'), - userId: text('user_id').notNull().references(()=> users_temp.id, { onDelete: 'cascade' }), - activeOrganizationId: text('active_organization_id') - }); + id: text("id").primaryKey(), + expiresAt: timestamp("expires_at").notNull(), + token: text("token").notNull().unique(), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + ipAddress: text("ip_address"), + userAgent: text("user_agent"), + userId: text("user_id") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), + activeOrganizationId: text("active_organization_id"), +}); export const account = pgTable("account", { - id: text("id").primaryKey(), - accountId: text('account_id').notNull(), - providerId: text('provider_id').notNull(), - userId: text('user_id').notNull().references(()=> users_temp.id, { onDelete: 'cascade' }), - accessToken: text('access_token'), - refreshToken: text('refresh_token'), - idToken: text('id_token'), - accessTokenExpiresAt: timestamp('access_token_expires_at'), - refreshTokenExpiresAt: timestamp('refresh_token_expires_at'), - scope: text('scope'), - password: text('password'), - createdAt: timestamp('created_at').notNull(), - updatedAt: timestamp('updated_at').notNull() - }); + id: text("id").primaryKey(), + accountId: text("account_id").notNull(), + providerId: text("provider_id").notNull(), + userId: text("user_id") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), + accessToken: text("access_token"), + refreshToken: text("refresh_token"), + idToken: text("id_token"), + accessTokenExpiresAt: timestamp("access_token_expires_at"), + refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), + scope: text("scope"), + password: text("password"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), +}); export const verification = pgTable("verification", { - id: text("id").primaryKey(), - identifier: text('identifier').notNull(), - value: text('value').notNull(), - expiresAt: timestamp('expires_at').notNull(), - createdAt: timestamp('created_at'), - updatedAt: timestamp('updated_at') - }); + id: text("id").primaryKey(), + identifier: text("identifier").notNull(), + value: text("value").notNull(), + expiresAt: timestamp("expires_at").notNull(), + createdAt: timestamp("created_at"), + updatedAt: timestamp("updated_at"), +}); export const organization = pgTable("organization", { - id: text("id").primaryKey(), - name: text('name').notNull(), - slug: text('slug').unique(), - logo: text('logo'), - createdAt: timestamp('created_at').notNull(), - metadata: text('metadata') - }); + id: text("id").primaryKey(), + name: text("name").notNull(), + slug: text("slug").unique(), + logo: text("logo"), + createdAt: timestamp("created_at").notNull(), + metadata: text("metadata"), +}); export const member = pgTable("member", { - id: text("id").primaryKey(), - organizationId: text('organization_id').notNull().references(()=> organization.id, { onDelete: 'cascade' }), - userId: text('user_id').notNull().references(()=> user.id, { onDelete: 'cascade' }), - role: text('role').notNull(), - createdAt: timestamp('created_at').notNull() - }); + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id, { onDelete: "cascade" }), + userId: text("user_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), + role: text("role").notNull(), + createdAt: timestamp("created_at").notNull(), +}); export const invitation = pgTable("invitation", { - id: text("id").primaryKey(), - organizationId: text('organization_id').notNull().references(()=> organization.id, { onDelete: 'cascade' }), - email: text('email').notNull(), - role: text('role'), - status: text('status').notNull(), - expiresAt: timestamp('expires_at').notNull(), - inviterId: text('inviter_id').notNull().references(()=> user.id, { onDelete: 'cascade' }) - }); + id: text("id").primaryKey(), + organizationId: text("organization_id") + .notNull() + .references(() => organization.id, { onDelete: "cascade" }), + email: text("email").notNull(), + role: text("role"), + status: text("status").notNull(), + expiresAt: timestamp("expires_at").notNull(), + inviterId: text("inviter_id") + .notNull() + .references(() => user.id, { onDelete: "cascade" }), +}); diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index c78ee9986..dd121b504 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -3,12 +3,12 @@ import { boolean, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { organization } from "./account"; import { admins } from "./admin"; import { server } from "./server"; import { users_temp } from "./user"; // import { user } from "./user"; import { generateAppName } from "./utils"; -import { organization } from "./account"; export const certificates = pgTable("certificate", { certificateId: text("certificateId") diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index b438d20bb..4d8dbc13d 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -3,10 +3,10 @@ import { pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { organization } from "./account"; import { admins } from "./admin"; import { backups } from "./backups"; import { users_temp } from "./user"; -import { organization } from "./account"; // import { user } from "./user"; export const destinations = pgTable("destination", { diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index 40c4558af..e42557141 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -3,12 +3,12 @@ import { pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { organization } from "./account"; import { admins } from "./admin"; import { bitbucket } from "./bitbucket"; import { github } from "./github"; import { gitlab } from "./gitlab"; import { users_temp } from "./user"; -import { organization } from "./account"; // import { user } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 5d2bb243d..6e29ddf67 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -3,8 +3,8 @@ import { boolean, integer, pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { users_temp } from "./user"; import { organization } from "./account"; +import { users_temp } from "./user"; // import { user } from "./user"; export const notificationType = pgEnum("notificationType", [ diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index 193fce47c..cf4ea8a86 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -4,6 +4,7 @@ import { pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { organization } from "./account"; import { admins } from "./admin"; // import { admins } from "./admin"; import { applications } from "./application"; @@ -14,7 +15,6 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { users, users_temp } from "./user"; -import { organization } from "./account"; export const projects = pgTable("project", { projectId: text("projectId") diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 932b198f2..aa362a050 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -3,10 +3,10 @@ import { pgEnum, pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { organization } from "./account"; import { admins } from "./admin"; import { applications } from "./application"; import { users_temp } from "./user"; -import { organization } from "./account"; // import { user } from "./user"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index 971c69164..f1dcd1001 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -11,6 +11,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; +import { organization } from "./account"; import { admins } from "./admin"; import { applications } from "./application"; import { certificates } from "./certificate"; @@ -25,7 +26,6 @@ import { sshKeys } from "./ssh-key"; import { users_temp } from "./user"; // import { user } from "./user"; import { generateAppName } from "./utils"; -import { organization } from "./account"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index 0b0124953..b705be36e 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -3,12 +3,12 @@ import { pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { sshKeyCreate, sshKeyType } from "../validations"; +import { organization } from "./account"; import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; import { users_temp } from "./user"; -import { organization } from "./account"; // import { user } from "./user"; export const sshKeys = pgTable("ssh-key", { diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 359cfbd45..e2755b8f9 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -13,8 +13,8 @@ import { z } from "zod"; import { account, organization } from "./account"; import { admins } from "./admin"; import { auth } from "./auth"; -import { certificateType } from "./shared"; import { projects } from "./project"; +import { certificateType } from "./shared"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. From e3e35ce7926a2e7fcb3148be97921b7cedc2376e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:43:23 -0600 Subject: [PATCH 047/126] refactor: update to use organization resources --- .../dokploy/server/api/routers/application.ts | 80 ++++++++++++++----- apps/dokploy/server/api/routers/bitbucket.ts | 19 +++-- .../dokploy/server/api/routers/certificate.ts | 17 +++- apps/dokploy/server/api/routers/deployment.ts | 8 +- .../dokploy/server/api/routers/destination.ts | 10 +-- apps/dokploy/server/api/routers/domain.ts | 41 +++++++--- .../server/api/routers/git-provider.ts | 12 +-- apps/dokploy/server/api/routers/github.ts | 30 +++++-- apps/dokploy/server/api/routers/gitlab.ts | 30 +++++-- apps/dokploy/server/api/routers/mariadb.ts | 22 ++--- apps/dokploy/server/api/routers/mongo.ts | 24 +++--- apps/dokploy/server/api/routers/mysql.ts | 24 +++--- .../server/api/routers/notification.ts | 24 ++++-- apps/dokploy/server/api/routers/postgres.ts | 44 +++++++--- .../server/api/routers/preview-deployment.ts | 14 +++- apps/dokploy/server/api/routers/project.ts | 16 ++-- apps/dokploy/server/api/routers/redirects.ts | 16 +++- apps/dokploy/server/api/routers/redis.ts | 22 ++--- apps/dokploy/server/api/routers/registry.ts | 6 +- apps/dokploy/server/api/routers/security.ts | 16 +++- apps/dokploy/server/api/routers/server.ts | 25 +++--- apps/dokploy/server/api/routers/ssh-key.ts | 21 +++-- apps/dokploy/server/api/trpc.ts | 4 +- packages/server/src/lib/auth.ts | 1 + 24 files changed, 348 insertions(+), 178 deletions(-) diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 618fd9ce8..490da340a 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -72,7 +72,7 @@ export const applicationRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -102,7 +102,9 @@ export const applicationRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.id, input.applicationId, "access"); } const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -115,7 +117,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiReloadApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this application", @@ -145,7 +149,9 @@ export const applicationRouter = createTRPCRouter({ } const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this application", @@ -186,7 +192,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const service = await findApplicationById(input.applicationId); - if (service.project.userId !== ctx.user.ownerId) { + if (service.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this application", @@ -206,7 +212,7 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const service = await findApplicationById(input.applicationId); - if (service.project.userId !== ctx.user.ownerId) { + if (service.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this application", @@ -227,7 +233,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to redeploy this application", @@ -260,7 +268,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariables) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -276,7 +286,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveBuildType) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this build type", @@ -297,7 +309,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveGithubProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this github provider", @@ -319,7 +333,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveGitlabProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this gitlab provider", @@ -343,7 +359,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveBitbucketProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this bitbucket provider", @@ -365,7 +383,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveDockerProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this docker provider", @@ -386,7 +406,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiSaveGitProvider) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this git provider", @@ -407,7 +429,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to mark this application as running", @@ -419,7 +443,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiUpdateApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this application", @@ -443,7 +469,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to refresh this application", @@ -458,7 +486,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this application", @@ -492,7 +522,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to clean this application", @@ -505,7 +537,9 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to read this application", @@ -540,7 +574,7 @@ export const applicationRouter = createTRPCRouter({ const app = await findApplicationById(input.applicationId as string); - if (app.project.userId !== ctx.user.ownerId) { + if (app.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this application", @@ -582,7 +616,9 @@ export const applicationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this application", diff --git a/apps/dokploy/server/api/routers/bitbucket.ts b/apps/dokploy/server/api/routers/bitbucket.ts index 75513a805..987555e5f 100644 --- a/apps/dokploy/server/api/routers/bitbucket.ts +++ b/apps/dokploy/server/api/routers/bitbucket.ts @@ -38,7 +38,8 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.userId !== ctx.user.ownerId + bitbucketProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -61,7 +62,9 @@ export const bitbucketRouter = createTRPCRouter({ if (IS_CLOUD) { // TODO: mAyBe a rEfaCtoR 🤫 result = result.filter( - (provider) => provider.gitProvider.userId === ctx.user.ownerId, + (provider) => + provider.gitProvider.organizationId === + ctx.session.activeOrganizationId, ); } return result; @@ -73,7 +76,8 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.userId !== ctx.user.ownerId + bitbucketProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -91,7 +95,8 @@ export const bitbucketRouter = createTRPCRouter({ ); if ( IS_CLOUD && - bitbucketProvider.gitProvider.userId !== ctx.user.ownerId + bitbucketProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -108,7 +113,8 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.userId !== ctx.user.ownerId + bitbucketProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -132,7 +138,8 @@ export const bitbucketRouter = createTRPCRouter({ const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( IS_CLOUD && - bitbucketProvider.gitProvider.userId !== ctx.user.ownerId + bitbucketProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ diff --git a/apps/dokploy/server/api/routers/certificate.ts b/apps/dokploy/server/api/routers/certificate.ts index b1c6f5c2d..87e6c6c8d 100644 --- a/apps/dokploy/server/api/routers/certificate.ts +++ b/apps/dokploy/server/api/routers/certificate.ts @@ -32,7 +32,10 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .query(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if (IS_CLOUD && certificates.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + certificates.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this certificate", @@ -44,7 +47,10 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .mutation(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if (IS_CLOUD && certificates.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + certificates.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this certificate", @@ -56,7 +62,12 @@ export const certificateRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { return await db.query.certificates.findMany({ // TODO: Remove this line when the cloud version is ready - ...(IS_CLOUD && { where: eq(certificates.userId, ctx.user.ownerId) }), + ...(IS_CLOUD && { + where: eq( + certificates.organizationId, + ctx.session.activeOrganizationId, + ), + }), }); }), }); diff --git a/apps/dokploy/server/api/routers/deployment.ts b/apps/dokploy/server/api/routers/deployment.ts index b9daaf0e7..8d95c1218 100644 --- a/apps/dokploy/server/api/routers/deployment.ts +++ b/apps/dokploy/server/api/routers/deployment.ts @@ -19,7 +19,9 @@ export const deploymentRouter = createTRPCRouter({ .input(apiFindAllByApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -32,7 +34,7 @@ export const deploymentRouter = createTRPCRouter({ .input(apiFindAllByCompose) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -44,7 +46,7 @@ export const deploymentRouter = createTRPCRouter({ .input(apiFindAllByServer) .query(async ({ input, ctx }) => { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this server", diff --git a/apps/dokploy/server/api/routers/destination.ts b/apps/dokploy/server/api/routers/destination.ts index 49edadf5e..770cb699f 100644 --- a/apps/dokploy/server/api/routers/destination.ts +++ b/apps/dokploy/server/api/routers/destination.ts @@ -84,7 +84,7 @@ export const destinationRouter = createTRPCRouter({ .input(apiFindOneDestination) .query(async ({ input, ctx }) => { const destination = await findDestinationById(input.destinationId); - if (destination.userId !== ctx.user.ownerId) { + if (destination.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this destination", @@ -94,7 +94,7 @@ export const destinationRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { return await db.query.destinations.findMany({ - where: eq(destinations.userId, ctx.user.ownerId), + where: eq(destinations.organizationId, ctx.session.activeOrganizationId), }); }), remove: adminProcedure @@ -103,7 +103,7 @@ export const destinationRouter = createTRPCRouter({ try { const destination = await findDestinationById(input.destinationId); - if (destination.userId !== ctx.user.ownerId) { + if (destination.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this destination", @@ -122,7 +122,7 @@ export const destinationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const destination = await findDestinationById(input.destinationId); - if (destination.userId !== ctx.user.ownerId) { + if (destination.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to update this destination", @@ -130,7 +130,7 @@ export const destinationRouter = createTRPCRouter({ } return await updateDestinationById(input.destinationId, { ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw error; diff --git a/apps/dokploy/server/api/routers/domain.ts b/apps/dokploy/server/api/routers/domain.ts index 526a156bb..aac2a016f 100644 --- a/apps/dokploy/server/api/routers/domain.ts +++ b/apps/dokploy/server/api/routers/domain.ts @@ -30,7 +30,9 @@ export const domainRouter = createTRPCRouter({ try { if (input.domainType === "compose" && input.composeId) { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if ( + compose.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -38,7 +40,10 @@ export const domainRouter = createTRPCRouter({ } } else if (input.domainType === "application" && input.applicationId) { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== + ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -58,7 +63,9 @@ export const domainRouter = createTRPCRouter({ .input(apiFindOneApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -70,7 +77,7 @@ export const domainRouter = createTRPCRouter({ .input(apiFindCompose) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -95,7 +102,9 @@ export const domainRouter = createTRPCRouter({ if (currentDomain.applicationId) { const newApp = await findApplicationById(currentDomain.applicationId); - if (newApp.project.userId !== ctx.user.ownerId) { + if ( + newApp.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -103,7 +112,9 @@ export const domainRouter = createTRPCRouter({ } } else if (currentDomain.composeId) { const newCompose = await findComposeById(currentDomain.composeId); - if (newCompose.project.userId !== ctx.user.ownerId) { + if ( + newCompose.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -114,7 +125,8 @@ export const domainRouter = createTRPCRouter({ currentDomain.previewDeploymentId, ); if ( - newPreviewDeployment.application.project.userId !== ctx.user.ownerId + newPreviewDeployment.application.project.organizationId !== + ctx.session.activeOrganizationId ) { throw new TRPCError({ code: "UNAUTHORIZED", @@ -143,7 +155,9 @@ export const domainRouter = createTRPCRouter({ const domain = await findDomainById(input.domainId); if (domain.applicationId) { const application = await findApplicationById(domain.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -151,7 +165,7 @@ export const domainRouter = createTRPCRouter({ } } else if (domain.composeId) { const compose = await findComposeById(domain.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -166,7 +180,10 @@ export const domainRouter = createTRPCRouter({ const domain = await findDomainById(input.domainId); if (domain.applicationId) { const application = await findApplicationById(domain.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== + ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -174,7 +191,9 @@ export const domainRouter = createTRPCRouter({ } } else if (domain.composeId) { const compose = await findComposeById(domain.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if ( + compose.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", diff --git a/apps/dokploy/server/api/routers/git-provider.ts b/apps/dokploy/server/api/routers/git-provider.ts index 83e71dd81..39194ed31 100644 --- a/apps/dokploy/server/api/routers/git-provider.ts +++ b/apps/dokploy/server/api/routers/git-provider.ts @@ -1,11 +1,7 @@ import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc"; import { db } from "@/server/db"; import { apiRemoveGitProvider, gitProvider } from "@/server/db/schema"; -import { - IS_CLOUD, - findGitProviderById, - removeGitProvider, -} from "@dokploy/server"; +import { findGitProviderById, removeGitProvider } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; @@ -18,8 +14,7 @@ export const gitProviderRouter = createTRPCRouter({ github: true, }, orderBy: desc(gitProvider.createdAt), - ...(IS_CLOUD && { where: eq(gitProvider.userId, ctx.user.ownerId) }), - //TODO: Remove this line when the cloud version is ready + where: eq(gitProvider.organizationId, ctx.session.activeOrganizationId), }); }), remove: protectedProcedure @@ -28,8 +23,7 @@ export const gitProviderRouter = createTRPCRouter({ try { const gitProvider = await findGitProviderById(input.gitProviderId); - if (IS_CLOUD && gitProvider.userId !== ctx.user.ownerId) { - // TODO: Remove isCloud in the next versions of dokploy + if (gitProvider.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this Git provider", diff --git a/apps/dokploy/server/api/routers/github.ts b/apps/dokploy/server/api/routers/github.ts index 62c21805d..2b3f15029 100644 --- a/apps/dokploy/server/api/routers/github.ts +++ b/apps/dokploy/server/api/routers/github.ts @@ -20,7 +20,10 @@ export const githubRouter = createTRPCRouter({ .input(apiFindOneGithub) .query(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); - if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + githubProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -33,7 +36,10 @@ export const githubRouter = createTRPCRouter({ .input(apiFindOneGithub) .query(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); - if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + githubProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -46,7 +52,10 @@ export const githubRouter = createTRPCRouter({ .input(apiFindGithubBranches) .query(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId || ""); - if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + githubProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -65,7 +74,9 @@ export const githubRouter = createTRPCRouter({ if (IS_CLOUD) { // TODO: mAyBe a rEfaCtoR 🤫 result = result.filter( - (provider) => provider.gitProvider.userId === ctx.user.ownerId, + (provider) => + provider.gitProvider.organizationId === + ctx.session.activeOrganizationId, ); } @@ -90,7 +101,8 @@ export const githubRouter = createTRPCRouter({ const githubProvider = await findGithubById(input.githubId); if ( IS_CLOUD && - githubProvider.gitProvider.userId !== ctx.user.ownerId + githubProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -111,7 +123,11 @@ export const githubRouter = createTRPCRouter({ .input(apiUpdateGithub) .mutation(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); - if (IS_CLOUD && githubProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + githubProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -120,7 +136,7 @@ export const githubRouter = createTRPCRouter({ } await updateGitProvider(input.gitProviderId, { name: input.name, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); }), }); diff --git a/apps/dokploy/server/api/routers/gitlab.ts b/apps/dokploy/server/api/routers/gitlab.ts index d3ba5a44a..338b8c64d 100644 --- a/apps/dokploy/server/api/routers/gitlab.ts +++ b/apps/dokploy/server/api/routers/gitlab.ts @@ -39,7 +39,10 @@ export const gitlabRouter = createTRPCRouter({ .input(apiFindOneGitlab) .query(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId); - if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + gitlabProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -58,7 +61,9 @@ export const gitlabRouter = createTRPCRouter({ if (IS_CLOUD) { // TODO: mAyBe a rEfaCtoR 🤫 result = result.filter( - (provider) => provider.gitProvider.userId === ctx.user.ownerId, + (provider) => + provider.gitProvider.organizationId === + ctx.session.activeOrganizationId, ); } const filtered = result @@ -78,7 +83,10 @@ export const gitlabRouter = createTRPCRouter({ .input(apiFindOneGitlab) .query(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId); - if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + gitlabProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -92,7 +100,10 @@ export const gitlabRouter = createTRPCRouter({ .input(apiFindGitlabBranches) .query(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId || ""); - if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + gitlabProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -107,8 +118,8 @@ export const gitlabRouter = createTRPCRouter({ try { const gitlabProvider = await findGitlabById(input.gitlabId || ""); if ( - IS_CLOUD && - gitlabProvider.gitProvider.userId !== ctx.user.ownerId + gitlabProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ @@ -130,7 +141,10 @@ export const gitlabRouter = createTRPCRouter({ .input(apiUpdateGitlab) .mutation(async ({ input, ctx }) => { const gitlabProvider = await findGitlabById(input.gitlabId); - if (IS_CLOUD && gitlabProvider.gitProvider.userId !== ctx.user.ownerId) { + if ( + gitlabProvider.gitProvider.organizationId !== + ctx.session.activeOrganizationId + ) { //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", @@ -140,7 +154,7 @@ export const gitlabRouter = createTRPCRouter({ if (input.name) { await updateGitProvider(input.gitProviderId, { name: input.name, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); await updateGitlab(input.gitlabId, { diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index e50b9b129..4276560c6 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -49,7 +49,7 @@ export const mariadbRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -83,7 +83,7 @@ export const mariadbRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.id, input.mariadbId, "access"); } const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.userId !== ctx.user.ownerId) { + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this Mariadb", @@ -96,7 +96,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiFindOneMariaDB) .mutation(async ({ input, ctx }) => { const service = await findMariadbById(input.mariadbId); - if (service.project.userId !== ctx.user.ownerId) { + if (service.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this Mariadb", @@ -133,7 +133,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiSaveExternalPortMariaDB) .mutation(async ({ input, ctx }) => { const mongo = await findMariadbById(input.mariadbId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -149,7 +149,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiDeployMariaDB) .mutation(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.userId !== ctx.user.ownerId) { + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Mariadb", @@ -170,7 +170,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiDeployMariaDB) .subscription(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.userId !== ctx.user.ownerId) { + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Mariadb", @@ -187,7 +187,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiChangeMariaDBStatus) .mutation(async ({ input, ctx }) => { const mongo = await findMariadbById(input.mariadbId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this Mariadb status", @@ -206,7 +206,7 @@ export const mariadbRouter = createTRPCRouter({ } const mongo = await findMariadbById(input.mariadbId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this Mariadb", @@ -232,7 +232,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesMariaDB) .mutation(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.userId !== ctx.user.ownerId) { + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -255,7 +255,7 @@ export const mariadbRouter = createTRPCRouter({ .input(apiResetMariadb) .mutation(async ({ input, ctx }) => { const mariadb = await findMariadbById(input.mariadbId); - if (mariadb.project.userId !== ctx.user.ownerId) { + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this Mariadb", @@ -285,7 +285,7 @@ export const mariadbRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { mariadbId, ...rest } = input; const mariadb = await findMariadbById(mariadbId); - if (mariadb.project.userId !== ctx.user.ownerId) { + if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this Mariadb", diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index ddb3d3ccd..d1d12bd0b 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -48,7 +48,7 @@ export const mongoRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -87,7 +87,7 @@ export const mongoRouter = createTRPCRouter({ } const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this mongo", @@ -101,7 +101,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const service = await findMongoById(input.mongoId); - if (service.project.userId !== ctx.user.ownerId) { + if (service.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this mongo", @@ -124,7 +124,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this mongo", @@ -146,7 +146,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiSaveExternalPortMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -162,7 +162,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiDeployMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this mongo", @@ -182,7 +182,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiDeployMongo) .subscription(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this mongo", @@ -199,7 +199,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiChangeMongoStatus) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this mongo status", @@ -214,7 +214,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiResetMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this mongo", @@ -248,7 +248,7 @@ export const mongoRouter = createTRPCRouter({ const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this mongo", @@ -274,7 +274,7 @@ export const mongoRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesMongo) .mutation(async ({ input, ctx }) => { const mongo = await findMongoById(input.mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -298,7 +298,7 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { mongoId, ...rest } = input; const mongo = await findMongoById(mongoId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this mongo", diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index c81fc8429..dc107bdba 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -50,7 +50,7 @@ export const mysqlRouter = createTRPCRouter({ } 1; const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -89,7 +89,7 @@ export const mysqlRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.id, input.mysqlId, "access"); } const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.userId !== ctx.user.ownerId) { + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this MySQL", @@ -102,7 +102,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { const service = await findMySqlById(input.mysqlId); - if (service.project.userId !== ctx.user.ownerId) { + if (service.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this MySQL", @@ -124,7 +124,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this MySQL", @@ -145,7 +145,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiSaveExternalPortMySql) .mutation(async ({ input, ctx }) => { const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -161,7 +161,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiDeployMySql) .mutation(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.userId !== ctx.user.ownerId) { + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this MySQL", @@ -181,7 +181,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiDeployMySql) .subscription(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.userId !== ctx.user.ownerId) { + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this MySQL", @@ -198,7 +198,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiChangeMySqlStatus) .mutation(async ({ input, ctx }) => { const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this MySQL status", @@ -213,7 +213,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiResetMysql) .mutation(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.userId !== ctx.user.ownerId) { + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this MySQL", @@ -244,7 +244,7 @@ export const mysqlRouter = createTRPCRouter({ await checkServiceAccess(ctx.user.id, input.mysqlId, "delete"); } const mongo = await findMySqlById(input.mysqlId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this MySQL", @@ -270,7 +270,7 @@ export const mysqlRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesMySql) .mutation(async ({ input, ctx }) => { const mysql = await findMySqlById(input.mysqlId); - if (mysql.project.userId !== ctx.user.ownerId) { + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -294,7 +294,7 @@ export const mysqlRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { mysqlId, ...rest } = input; const mysql = await findMySqlById(mysqlId); - if (mysql.project.userId !== ctx.user.ownerId) { + if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this MySQL", diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 6c2b469d9..451700065 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -71,7 +71,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if (notification.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -122,7 +122,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if (notification.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -174,7 +174,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if (notification.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -234,7 +234,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if (notification.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -276,7 +276,7 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if (notification.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -295,7 +295,7 @@ export const notificationRouter = createTRPCRouter({ .input(apiFindOneNotification) .query(async ({ input, ctx }) => { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if (notification.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -314,7 +314,12 @@ export const notificationRouter = createTRPCRouter({ gotify: true, }, orderBy: desc(notifications.createdAt), - ...(IS_CLOUD && { where: eq(notifications.userId, ctx.user.ownerId) }), + ...(IS_CLOUD && { + where: eq( + notifications.organizationId, + ctx.session.activeOrganizationId, + ), + }), // TODO: Remove this line when the cloud version is ready }); }), @@ -400,7 +405,10 @@ export const notificationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const notification = await findNotificationById(input.notificationId); - if (IS_CLOUD && notification.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + notification.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this notification", diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index 8a4e8ba25..b74bc0f68 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -56,7 +56,7 @@ export const postgresRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -95,7 +95,9 @@ export const postgresRouter = createTRPCRouter({ } const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this Postgres", @@ -109,7 +111,7 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const service = await findPostgresById(input.postgresId); - if (service.project.userId !== ctx.user.ownerId) { + if (service.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this Postgres", @@ -131,7 +133,9 @@ export const postgresRouter = createTRPCRouter({ .input(apiFindOnePostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this Postgres", @@ -153,7 +157,9 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -169,7 +175,9 @@ export const postgresRouter = createTRPCRouter({ .input(apiDeployPostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Postgres", @@ -190,7 +198,9 @@ export const postgresRouter = createTRPCRouter({ .input(apiDeployPostgres) .subscription(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Postgres", @@ -207,7 +217,9 @@ export const postgresRouter = createTRPCRouter({ .input(apiChangePostgresStatus) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this Postgres status", @@ -226,7 +238,9 @@ export const postgresRouter = createTRPCRouter({ } const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this Postgres", @@ -249,7 +263,9 @@ export const postgresRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesPostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", @@ -272,7 +288,9 @@ export const postgresRouter = createTRPCRouter({ .input(apiResetPostgres) .mutation(async ({ input, ctx }) => { const postgres = await findPostgresById(input.postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this Postgres", @@ -302,7 +320,9 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { postgresId, ...rest } = input; const postgres = await findPostgresById(postgresId); - if (postgres.project.userId !== ctx.user.ownerId) { + if ( + postgres.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this Postgres", diff --git a/apps/dokploy/server/api/routers/preview-deployment.ts b/apps/dokploy/server/api/routers/preview-deployment.ts index 482bb3505..f833e9f95 100644 --- a/apps/dokploy/server/api/routers/preview-deployment.ts +++ b/apps/dokploy/server/api/routers/preview-deployment.ts @@ -14,7 +14,9 @@ export const previewDeploymentRouter = createTRPCRouter({ .input(apiFindAllByApplication) .query(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -28,7 +30,10 @@ export const previewDeploymentRouter = createTRPCRouter({ const previewDeployment = await findPreviewDeploymentById( input.previewDeploymentId, ); - if (previewDeployment.application.project.userId !== ctx.user.ownerId) { + if ( + previewDeployment.application.project.organizationId !== + ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this preview deployment", @@ -43,7 +48,10 @@ export const previewDeploymentRouter = createTRPCRouter({ const previewDeployment = await findPreviewDeploymentById( input.previewDeploymentId, ); - if (previewDeployment.application.project.userId !== ctx.user.ownerId) { + if ( + previewDeployment.application.project.organizationId !== + ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this preview deployment", diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index bce12565f..88bb21d76 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -75,7 +75,7 @@ export const projectRouter = createTRPCRouter({ const project = await db.query.projects.findFirst({ where: and( eq(projects.projectId, input.projectId), - eq(projects.userId, ctx.user.ownerId), + eq(projects.organizationId, ctx.session.activeOrganizationId), ), with: { compose: { @@ -115,7 +115,7 @@ export const projectRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -140,7 +140,7 @@ export const projectRouter = createTRPCRouter({ accessedProjects.map((projectId) => sql`${projectId}`), sql`, `, )})`, - eq(projects.userId, ctx.user.id), + eq(projects.organizationId, ctx.session.activeOrganizationId), ), with: { applications: { @@ -194,7 +194,7 @@ export const projectRouter = createTRPCRouter({ }, }, }, - where: eq(projects.userId, ctx.user.id), + where: eq(projects.organizationId, ctx.session.activeOrganizationId), orderBy: desc(projects.createdAt), }); }), @@ -207,7 +207,9 @@ export const projectRouter = createTRPCRouter({ await checkProjectAccess(ctx.user.id, "delete"); } const currentProject = await findProjectById(input.projectId); - if (currentProject.userId !== ctx.user.ownerId) { + if ( + currentProject.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this project", @@ -225,7 +227,9 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const currentProject = await findProjectById(input.projectId); - if (currentProject.userId !== ctx.user.ownerId) { + if ( + currentProject.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this project", diff --git a/apps/dokploy/server/api/routers/redirects.ts b/apps/dokploy/server/api/routers/redirects.ts index 1a8ba4cfa..2d520cc42 100644 --- a/apps/dokploy/server/api/routers/redirects.ts +++ b/apps/dokploy/server/api/routers/redirects.ts @@ -18,7 +18,9 @@ export const redirectsRouter = createTRPCRouter({ .input(apiCreateRedirect) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -31,7 +33,9 @@ export const redirectsRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const redirect = await findRedirectById(input.redirectId); const application = await findApplicationById(redirect.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -44,7 +48,9 @@ export const redirectsRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const redirect = await findRedirectById(input.redirectId); const application = await findApplicationById(redirect.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -57,7 +63,9 @@ export const redirectsRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const redirect = await findRedirectById(input.redirectId); const application = await findApplicationById(redirect.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index acded3723..db76ee6ce 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -48,7 +48,7 @@ export const redisRouter = createTRPCRouter({ } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -80,7 +80,7 @@ export const redisRouter = createTRPCRouter({ } const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this Redis", @@ -93,7 +93,7 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to start this Redis", @@ -115,7 +115,7 @@ export const redisRouter = createTRPCRouter({ .input(apiResetRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to reload this Redis", @@ -145,7 +145,7 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this Redis", @@ -166,7 +166,7 @@ export const redisRouter = createTRPCRouter({ .input(apiSaveExternalPortRedis) .mutation(async ({ input, ctx }) => { const mongo = await findRedisById(input.redisId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this external port", @@ -182,7 +182,7 @@ export const redisRouter = createTRPCRouter({ .input(apiDeployRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Redis", @@ -202,7 +202,7 @@ export const redisRouter = createTRPCRouter({ .input(apiDeployRedis) .subscription(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this Redis", @@ -218,7 +218,7 @@ export const redisRouter = createTRPCRouter({ .input(apiChangeRedisStatus) .mutation(async ({ input, ctx }) => { const mongo = await findRedisById(input.redisId); - if (mongo.project.userId !== ctx.user.ownerId) { + if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to change this Redis status", @@ -238,7 +238,7 @@ export const redisRouter = createTRPCRouter({ const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this Redis", @@ -261,7 +261,7 @@ export const redisRouter = createTRPCRouter({ .input(apiSaveEnvironmentVariablesRedis) .mutation(async ({ input, ctx }) => { const redis = await findRedisById(input.redisId); - if (redis.project.userId !== ctx.user.ownerId) { + if (redis.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to save this environment", diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 76df5cd65..b57171795 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -28,7 +28,7 @@ export const registryRouter = createTRPCRouter({ .input(apiRemoveRegistry) .mutation(async ({ ctx, input }) => { const registry = await findRegistryById(input.registryId); - if (registry.userId !== ctx.user.ownerId) { + if (registry.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this registry", @@ -41,7 +41,7 @@ export const registryRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const { registryId, ...rest } = input; const registry = await findRegistryById(registryId); - if (registry.userId !== ctx.user.ownerId) { + if (registry.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to update this registry", @@ -67,7 +67,7 @@ export const registryRouter = createTRPCRouter({ .input(apiFindOneRegistry) .query(async ({ input, ctx }) => { const registry = await findRegistryById(input.registryId); - if (registry.userId !== ctx.user.ownerId) { + if (registry.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this registry", diff --git a/apps/dokploy/server/api/routers/security.ts b/apps/dokploy/server/api/routers/security.ts index ca4892d26..b8e70bbb0 100644 --- a/apps/dokploy/server/api/routers/security.ts +++ b/apps/dokploy/server/api/routers/security.ts @@ -18,7 +18,9 @@ export const securityRouter = createTRPCRouter({ .input(apiCreateSecurity) .mutation(async ({ input, ctx }) => { const application = await findApplicationById(input.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -31,7 +33,9 @@ export const securityRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const security = await findSecurityById(input.securityId); const application = await findApplicationById(security.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -44,7 +48,9 @@ export const securityRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const security = await findSecurityById(input.securityId); const application = await findApplicationById(security.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", @@ -57,7 +63,9 @@ export const securityRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const security = await findSecurityById(input.securityId); const application = await findApplicationById(security.applicationId); - if (application.project.userId !== ctx.user.ownerId) { + if ( + application.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this application", diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 8e63d7e66..3662345d4 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -65,7 +65,7 @@ export const serverRouter = createTRPCRouter({ .input(apiFindOneServer) .query(async ({ input, ctx }) => { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this server", @@ -93,7 +93,7 @@ export const serverRouter = createTRPCRouter({ .leftJoin(mongo, eq(mongo.serverId, server.serverId)) .leftJoin(mysql, eq(mysql.serverId, server.serverId)) .leftJoin(postgres, eq(postgres.serverId, server.serverId)) - .where(eq(server.userId, ctx.user.ownerId)) + .where(eq(server.organizationId, ctx.session.activeOrganizationId)) .orderBy(desc(server.createdAt)) .groupBy(server.serverId); @@ -105,10 +105,13 @@ export const serverRouter = createTRPCRouter({ where: IS_CLOUD ? and( isNotNull(server.sshKeyId), - eq(server.userId, ctx.user.ownerId), + eq(server.organizationId, ctx.session.activeOrganizationId), eq(server.serverStatus, "active"), ) - : and(isNotNull(server.sshKeyId), eq(server.userId, ctx.user.ownerId)), + : and( + isNotNull(server.sshKeyId), + eq(server.organizationId, ctx.session.activeOrganizationId), + ), }); return result; }), @@ -117,7 +120,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", @@ -142,7 +145,7 @@ export const serverRouter = createTRPCRouter({ .subscription(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", @@ -162,7 +165,7 @@ export const serverRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to validate this server", @@ -204,7 +207,7 @@ export const serverRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to validate this server", @@ -254,7 +257,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to setup this server", @@ -296,7 +299,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this server", @@ -330,7 +333,7 @@ export const serverRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const server = await findServerById(input.serverId); - if (server.userId !== ctx.user.ownerId) { + if (server.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this server", diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index 4beb0bae8..725c67fe2 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -27,7 +27,7 @@ export const sshRouter = createTRPCRouter({ console.log(ctx.user.ownerId); await createSshKey({ ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw new TRPCError({ @@ -42,7 +42,10 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const sshKey = await findSSHKeyById(input.sshKeyId); - if (IS_CLOUD && sshKey.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + sshKey.organizationId !== ctx.session.activeOrganizationId + ) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -60,7 +63,10 @@ export const sshRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const sshKey = await findSSHKeyById(input.sshKeyId); - if (IS_CLOUD && sshKey.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + sshKey.organizationId !== ctx.session.activeOrganizationId + ) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -71,7 +77,9 @@ export const sshRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { return await db.query.sshKeys.findMany({ - ...(IS_CLOUD && { where: eq(sshKeys.userId, ctx.user.ownerId) }), + ...(IS_CLOUD && { + where: eq(sshKeys.organizationId, ctx.session.activeOrganizationId), + }), orderBy: desc(sshKeys.createdAt), }); // TODO: Remove this line when the cloud version is ready @@ -86,7 +94,10 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const sshKey = await findSSHKeyById(input.sshKeyId); - if (IS_CLOUD && sshKey.userId !== ctx.user.ownerId) { + if ( + IS_CLOUD && + sshKey.organizationId !== ctx.session.activeOrganizationId + ) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 98b5ab8e4..7f8f0f756 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -32,7 +32,7 @@ import { ZodError } from "zod"; interface CreateContextOptions { user: (User & { rol: "member" | "admin" | "owner"; ownerId: string }) | null; - session: (Session & { activeOrganizationId?: string }) | null; + session: (Session & { activeOrganizationId: string }) | null; req: CreateNextContextOptions["req"]; res: CreateNextContextOptions["res"]; } @@ -84,7 +84,7 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { session: session ? { ...session, - activeOrganizationId: session.activeOrganizationId ?? undefined, + activeOrganizationId: session.activeOrganizationId || "", } : null, user: user diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 522a921e8..3b29e0240 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -83,6 +83,7 @@ export const auth = betterAuth({ }, }, }, + plugins: [organization()], }); From 78c72b6337e8b10d65566c3baebf5b1fabd63256 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 20:49:10 -0600 Subject: [PATCH 048/126] refactor: update --- apps/dokploy/server/api/routers/certificate.ts | 2 +- apps/dokploy/server/api/routers/registry.ts | 10 +++++++--- packages/server/src/services/certificate.ts | 4 ++-- packages/server/src/services/registry.ts | 10 ++++++---- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/apps/dokploy/server/api/routers/certificate.ts b/apps/dokploy/server/api/routers/certificate.ts index 87e6c6c8d..cdca1e97c 100644 --- a/apps/dokploy/server/api/routers/certificate.ts +++ b/apps/dokploy/server/api/routers/certificate.ts @@ -25,7 +25,7 @@ export const certificateRouter = createTRPCRouter({ message: "Please set a server to create a certificate", }); } - return await createCertificate(input, ctx.user.ownerId); + return await createCertificate(input, ctx.session.activeOrganizationId); }), one: adminProcedure diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index b57171795..1ac37716b 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -4,25 +4,26 @@ import { apiRemoveRegistry, apiTestRegistry, apiUpdateRegistry, + registry, } from "@/server/db/schema"; import { IS_CLOUD, createRegistry, execAsync, execAsyncRemote, - findAllRegistryByUserId, findRegistryById, removeRegistry, updateRegistry, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; +import { eq } from "drizzle-orm"; export const registryRouter = createTRPCRouter({ create: adminProcedure .input(apiCreateRegistry) .mutation(async ({ ctx, input }) => { - return await createRegistry(input, ctx.user.ownerId); + return await createRegistry(input, ctx.session.activeOrganizationId); }), remove: adminProcedure .input(apiRemoveRegistry) @@ -61,7 +62,10 @@ export const registryRouter = createTRPCRouter({ return true; }), all: protectedProcedure.query(async ({ ctx }) => { - return await findAllRegistryByUserId(ctx.user.ownerId); + const registryResponse = await db.query.registry.findMany({ + where: eq(registry.organizationId, ctx.session.activeOrganizationId), + }); + return registryResponse; }), one: adminProcedure .input(apiFindOneRegistry) diff --git a/packages/server/src/services/certificate.ts b/packages/server/src/services/certificate.ts index dd83e61df..f59f1c2aa 100644 --- a/packages/server/src/services/certificate.ts +++ b/packages/server/src/services/certificate.ts @@ -33,13 +33,13 @@ export const findCertificateById = async (certificateId: string) => { export const createCertificate = async ( certificateData: z.infer, - userId: string, + organizationId: string, ) => { const certificate = await db .insert(certificates) .values({ ...certificateData, - userId: userId, + organizationId: organizationId, }) .returning(); diff --git a/packages/server/src/services/registry.ts b/packages/server/src/services/registry.ts index b92eabdb0..853f4cf7b 100644 --- a/packages/server/src/services/registry.ts +++ b/packages/server/src/services/registry.ts @@ -12,14 +12,14 @@ export type Registry = typeof registry.$inferSelect; export const createRegistry = async ( input: typeof apiCreateRegistry._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newRegistry = await tx .insert(registry) .values({ ...input, - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); @@ -135,9 +135,11 @@ export const findRegistryById = async (registryId: string) => { return registryResponse; }; -export const findAllRegistryByUserId = async (userId: string) => { +export const findAllRegistryByOrganizationId = async ( + organizationId: string, +) => { const registryResponse = await db.query.registry.findMany({ - where: eq(registry.userId, userId), + where: eq(registry.organizationId, organizationId), }); return registryResponse; }; From 2ebb02dc208874c95ff4be3d27fb57332001ea1f Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sat, 15 Feb 2025 22:46:37 -0500 Subject: [PATCH 049/126] feat(template): add docker registry template --- apps/dokploy/public/templates/registry.png | Bin 0 -> 28962 bytes .../templates/registry/docker-compose.yml | 19 ++++++++++ apps/dokploy/templates/registry/index.ts | 35 ++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 ++++++++ 4 files changed, 69 insertions(+) create mode 100644 apps/dokploy/public/templates/registry.png create mode 100644 apps/dokploy/templates/registry/docker-compose.yml create mode 100644 apps/dokploy/templates/registry/index.ts diff --git a/apps/dokploy/public/templates/registry.png b/apps/dokploy/public/templates/registry.png new file mode 100644 index 0000000000000000000000000000000000000000..39418022e3363b38db53f6ed913ef9ec66099877 GIT binary patch literal 28962 zcmd43bySpJ^f&qt64D?bL#NWp42^VmcZl>*lG1`Sf^-bsFqG1b0gf~%0|F8%4I&K^ z%6*3K@4fe~b>DUWyz9zxDbJj9p1t?kXP+IPJ@LBQDkQfdw*dekQBzga2LNms_&1pV z4}207quUDpgZD&3MG?5h{1o?Ar31jzuWE{Nh5_@x3J3!%jpoA59G1_N{O9BxC{jPd zxRvhQNhy6h$foMjJjn%3Z|k^)C8tNObO$%JzJQK<&^U@s!I7)P`Ar-2tdcVW+hIBT zOH$kUTWIIUFOF%W3%d)!%V&Y(2G>O*2=R>tpQYfeJquTUkdFTk{_LlV^#9SU^O^Y< z|LQ}W>5>?47W5O!=i|Xa{y96h$wwdMyo{`Xr5=_nVI3{|v3`_;qnp|o7660`4lpA+ z)!4rMoeo&a$r*&4#aB!5uGw+-8fCQGiLZE_o4rl*Uhk;uq!K0u8EKfzE$S_bQ{hX6 zGEotl=~wg+SJb6Y7a-K1KG|JAaPRHi(Y*x6Jyv<*Z%k=GNqmOu_(sTvWBY47=?YN` z0r(Md($Z3^(&LzystbY+h-9{A$010;c8K;Ax{$>AJ~+TMQqRh2XT)}qIYI2Mxtiwn z+O*D8TJ{vJBuFGr%J|R~(1y1kibeBL-TI7=pE^hqk!4mRp%|Yi>u`%FS>|P$b zLh?oiD4X~DWr^>ac6c@X* z_M>gIhh)nOe3kZy=F-(G%@&PRl4SLnkn&ESf6B*F;0LiOgs1qBPdR#9)P;pq!cH@bqzU?N|?T{nod7p)#W zy#(3iscL_=tB9Iy!kAaKkeCjxu$&q4VpD0 z!iWVY4f-opu0J@7|CZoJifyxf0=$pfhxh*KMn-;kzWN>)2yaR(u%2ZFk4vWL5%2Ua z+=ufKq z-Dc8XKdwnN0)Xxl`>tn;Ga+6%1cR>~UTpgNu(h#mS{H8KPpqD}n}>%iEDuv2cDkPZ zG<3PEzc|E`W6b-k3BKZFziQexJ8D1-klQ{fXg)RJy)VSw^J~h!e3gZE1Ywsax$&#i z&uT5HIV1yd=Af}vRewI0w`Hkv;9q9k_V(ZICoJbyl0hmGfc&9-#Sho*i(1E6^Xw6l zyi>)7+2XdImfY$~QsC0d(Ggh}W(hq&{&vTn>a_o;Jbw^;IA^nO`KlS!9?*pal)hnO z#}8C;EF_lgNp(0BXExdhK>O#s7D|T-oGM(YFWVSTzL@{|GkJ7j!2Iu#w~=e=_KMUN z0I*hYe(inTBP?@w@-cj3sO0Bt$7mw|BO8~&T$wXDduszHzYkx|e$IU;IqdC{s_T#D zH!FKp#RRY@R!4PFKMqm6)4scsD5*V9K}1z_uMSGG zEBSQaZA_n&6kah#WNUDMu%|xO6#xFu?)-?9BJ25(Q^0h^UZu5ep;2uEPs@wjoW+XF zpRvLxQwI?n)znG*8z-rM!~%Wt&Gj0soV=;c!`8>78UBTq3PXLqRv&A%VskDB);OK; zrvC#CZhC*MmJ0F?1shensUS&LU3ee6{7Lu#UhZVBAj6O7Q7oWC-h!*Skhn!u@qx<^ zwpJyMwI20$Hx8!prNh0~N34gtw-EsXCzq4Wf`1K-DeLNWUz1u08gjGNP|#=Y4JS00 zwT%|)elwE`6hH^^ogWEqKMlng;k&F@EacTjw|98_(NLl1e4gXUBW<6lw-@9zx(_}E zd>Y-I9w8HnLAR*96P+NnqIa~Ydk|;qrJ;d`SmV) z){eC0_A2K^bZly-kFs7xJ%M?-2N3sS^q5iB{>{wWRR^QTu0gwS{VChu4MNdVG9jki z8I!p1lQe4rV363MdadvQ$A&MF-+hZn#cVzPJ&mZ}BfdKISfEPojS>q{C31!xUEF8- zSAVB9i1Qn(&S$3(-q*u&KaV>9kpP7yTq<-S|K0<2&qo>jfJ`PyUo=mIUtZk56&tar z=SYHyV`bneBeh&cgvn)-B3I1bltrx>67e<_y{K@^T6F0-{s)oJt&ofhn8dBU+z^_D z+hnDk&oS{h$c7&!M^_K-4qi+|7Nd5~X<(hBANUF(?B)%Q1r zwplhf7HR4Dk@0TK)DlF~h?g?C?rhKJSQ7%&4vi%G?Q6d1xGJ3hj9TbjJXY@-BDzAP+-#u1mAXe{=`j4INKNBX6;^gk(=$45TA9tN+T z`!9*_@Jo{!#>}67w)``mJz=&l<;>*dcx?K9G|y&3Gf5!&4l^)GW;}{`76Qq)=U%?j zdz_WJHl|z8GPDskn~?d#aj}PPf*QVA0#VcIJ{M1F@_p-BUqho#;qQZLQJm?}S=>*j zuCS>87Iyu{+sMysqkkR$7kS9swLVVxdeP3Mu*YmRSIv?e zyc`|$tlv*8$3pDaxs1C7pGiJ_QY3{iu=m@9oWB+H7@}V`Y=4c?$?H~XEqcqJZl<$% zO0^{}Qpj(8f6d%a*ql)RUHSU%L7<&z5C@Rq{2O-KNZB7Kad+tPMYsBEOIOMFC%2wl zA~L=*b`E6-hfPpV15%RSed-O@GT*OLdz2u5SKLQ8QZDr+U8TgyQ8lJGZJmK*AH5_{l{c0VOqwb8I`@6 ziDmKQ*MmM5N^e9A4vW2q(dft4#zlWsfqs-1Bs$z12xN&0q zyU`Qoo-x+$4|{1dp{$Lg`R?@!17~F3(p!P4!!j-FL@JZ)krT69(TuB|7YWBFd&qd> z`nI@^#I$Dqjn?&-{aoPXa2N60O7y9qbkkU+$Gn-7mv4IRN4cdgJV3?RiQ{L(vjg^} zgu`;iod?fMoM@h$f7lXwm|uh4rj}SxuoX?-L5=Kt97*O%qxPrBat$x;K9zTK+u>8X zb9cX`aC3Zu^m5u{LvBLY?Jkuj<8al6@ztbwCK4EP4bCgHn5^ojwERHxSa{QEVHKF*NipdQ?3XQJZ$Uy8XZd zj#c$%e6Ae5oxAo@+R->sZa`}6C1?9#73|sga-2F|cuDC`!=c&FyJ{DiiH+>wbV@^S zRoazDXoG%0mwRk?F?R57)1*5xmBC=c;2Xdy_oUx)`^)2lE_Giu5*%6K>JU$+x-aF` zF5z#8A>z&b1!806nAI^ynlAUmpO@*ZggJl$V&Eq}=i69fsya=dEkwCJF6>=7%RzXho19W*s5S6S$@ zZxBAKOvAzkjn2$g#E%s(GoO2KT7ZGM*;4bf{Hc35P}X218RpOBx}Y+K9d0o=GnFn- zaQV`p8;}eAHbi&U@w9u34^9JE(zhR?5Wnlb-0>{)$9e(2RkDEA;itjkH{E97I}Q6( z?@pKZirV;wZa09X$QbVPga4iexZ;kWU0~Vg=z|?2I1NiU$Km?3d>I+LHhplCh`W^) z{YcJHWxw6*?GqoQRd_L>*36%>DS4>0Cxaq5nRbwWtk!Z+<{67H5qZb^cbooy9_&lO zsevB(URm+CyY_|3W7xQH80Vdq6#cg__ZFaIGkS=6&R|{A8D)NpV20&sFqIV$;V=E*{GJT7NFMNsM#fGVjUdBrMkM*zxv}UR)fuse}d+@ycSOQYkAO}e)hxddzqF& zut>2#R){=jYz8%A@1r+rr6D1)b@7g8QSZH0QlPJ|XDw;P60$|uXa*LU(Z*X*0oz6I7iM)W(E+xO(C6U zT#e;R<+WCJ#SZtka~uC-P#xviYEi)^qJuhG7khztA^2Z7$S>8`VqR~hi>x@pF&>kl zm+c=VMHf8d$XCoBKgQwq`EW&3{6h8fJWvdccRMp-=YbWusSK%Kl&>$;^=^{r zQZyw8nop9QlX5!Bvw4OU%-{jLTN;CxMR6Khul*K&NQ_H;`0k0th@vfTUS&UcyqctJ z&H%iCO4BN{z=|?VjDi?~Zq>lBcuDjY275q>Pm(INRroR0dV~{GEDK4?m&VGFOn7X;M_mbS43HT6ho~pZL zPL7y1{@#oV_4e_D2B|7Kk3nqGVxDu$-~0J3PJ$4yWuFmuz1E+g)#)@WNjzl{=O*F#i!{Yo4!>>MW7xgPN`M5G*_~=hJ9>ZbS%m z@dBPE|le#y=ZfC|sf!s*8Ke`{A3{ z7$UxIZdiyN5E~mz-SSg%{GIWI*P&!wzj=FD%Qg51;5Va8I`YaG-_fD2$I3&sHJj@q zzwrUjH=UcL6K$-61D9u*iqc0Cs|=et$yW|ZMe5=y#&`gkeWj?XKJ+eC(e_fMr_raY zuGIuc1>3&*Pz*msO@7qzdhzSg7W&rEq23QE#iKb)6Ev@>Bg7TPQEpm0*x_VC zR6NKnTD9ilaptV?b^9b6(=@8hmD`j?f3lA^S-`1zMd>jil5d=BS6S2ke%Hv%5XRi4 zG`9dPd#)?}W(dzS{pBBaD}p?`vJY0!ceJaV@X0fWS;B`ni?SUicr8H7iH!r~j%coy zJ5EzGh&Ko1Q0EAXvC1}~EO_wfA4xrqySqS{qJ8QC`m;V@j>h^CM+*ahtLj+FD@;@2 zx)Q1e-6NAn2T}(lTQhMWQlVT}9ht|T;_lBKEgY9(TJ8e4rjBfa!n}Hcm8-*lPJ~c` zUo7AW%a!f;fS+kwMzzK==UeSg=>SUpTL5bsUAZGQ$H}{Kq48_&6(a7510hDher<(I zOHx<6AgLr8U?~@hMAs>6Gz8nY@~XcT=l0YrkA`v0=t=4Ke-9MY_;k*+VF$2agQJe` zi3G0XRdmWW<9ygYB(5*x<_So#KKqws5jRNyP^8n9S2Evt-clw#T7t-jR4s`yEug~* z+rIC~!(L?su#|$UWUyul7V`QG#O96~y-wWTHZL;%Oze{5lIxq&5ODeHkqZS7*D=Z> zytZCTdh}QvCZ+ST0JoD`^QH5Qc#D`McwIR|6DKCwfN*dS&O`qoTYT}X{R}*PXwu9i z0dAvXr=*j=Qm$6$jTqQ0JBE_oQHwR_18`vOSAu1d4~S6-r}Z{9*bDCN^l=JU-L zzBHknJvv>8&pJ$Bh71H`9aA9fx{`p<^5|JWn8gF3#cqMtJsh6!0(1U@)1WplW??cQ zXhid{A$*8fe~K*L?ckgB+cz4P`G2benK8NG{h(STCL;-F0#fM8QAGAjfjKY&y*FR; z17B^VhGVe}#{JpNBX-pc4X$tPn2rUdZT)01$Xxq$_f4Mb%%A93p`)hTA&T`1cj#tP zw2!r5LqFeKtctt4yYJ}of`VQ7JQ6(9G1cAK@CP5hkX<3>lkn{8+^A2rSKO~{O@-|f z9W9Y0q`bSGHwUi>X1sJ0iEpvfQ1%fjQpzX{*7VG_w8(SBex?k?npO)>q6ZXcdmVAK+KHoK_e=i`k#x7W*WL*Dp|qzJW>Q3@O7l zzvOV9iwQvOGFx25UUuoViG;TFUu8Oi~4wG^(xQu!UECFY4o6J;RZ<%^X<&^K$Q>rR>ggye~fv z>FQASRD#C~`_o|7{%IFINzIE>y>4>C5*Jk~n$mbzWS#$lKy2(8VIjKwIw_!o9V(o( zrOU*cl9^Gvs2V(p$uB7@+dx{LR`Zfbv^wFyX-x|V+h)-soM+1L6vY1c^YsaVNcYu=qMmk*l4PO+<9&mG<5XGz;_x1;i(xNbV$TY;*H<#gY~iQTT9*+;swh zwl8O+c!}5HIA+xsoqKGw#?E2yHaFQRq3fSW@u%}t>g92bouuRT%d2{4S%e2ii+yas zdc~1R8&Rx~{Dmr;iP+o^$v=nDV(}H+Iv*H2ehTy$8kh+LK#00%pY&6#=0wfk`YUDfI% z?(B19z0Tr$b&$H^z&)qcr?<{Cb^(J8~ygYq5wCGtWJGT}K-)BMIYB(W&F09L5UrVbd3hjohsXlTm8Ub<+qk z1i>Z$h~Zn}4@Z$8AGNjg8vY1WdtPh{o9%qwa|W4`5Do?&mBE*oHReIGVG9z*=;zjcHjxuur`qe)GFYY|E>_B$So#HN(dR*)|_kd{gcBT z9KF_g8wm=)LHD5@gJsFNv~W$uq*zwnNHI9}9g&v8w)sGXrX;Lf{>&I6f=vAnFu% z7_a}f1_ghuR2Ei(Y$~ZhxB zZs1vR9t&NE7b=6i%H7FbQP!GM3;QZRP;ott$o-stMsVpvV+e{pMrxv0O@8=3-^aDY zWeG~>`zBYMt?`q%dd{n7SEOFtCTXIIYi2~bATw=NUl&8t`xRmSl6|C4y&nk4aKNP z+9Vpr^*p&F0|Be>DE%|K1(L7nFyOszS{0>iRbHYjtZXIcGivR#7;6BkfNoUGGy-d+ zG`EtwTlu7O$_U(49%E;3)UNiJ*9o1S0o>4zfrPL?6oS-?;<_)-t`~6~-m#gv#sG3? zNe<~R5%;)d(k5IbEE5dHC(~S1!U#Oabb5~5PZ>9G>$zX0zi;kpFhq?+$bj)CZPL>+ zT5`T?2RHP=xLOkTVG|fa$fJ}Om*gymm)tH$bPAxl#fWqIll+0Q^*7kpUq$PR(_d&l-Gr=T@TmPn5p(@dcVdnc2;c^ zrR-Fqm+DE8EkxxUx(zRscm$qGFIne9RHXK}CzyGCty^m@I7FxQ>bBPSA1d%rZt}qT zKUF?UFX{$)Xy?28@S<=xaQiA7Vvmw`M~nvXoTa57S@1a}mO7qTr(NdtlUSDR2(r1dc-#pD4s~d@WyvgdHY9m^6I1UPy&q;BVI14&?V-Ox2hSxN8hq zh`w*#ybBbN6+*Zv6qAJ&5cYpF)F|uOeG)07$iZOu)!}q8pKzW$siXba7fALM`}O0%a~?6Djz-)`S9dusnPEDd_vV| zFlN@B+o%ke8)x*FgQk8E2mHw0RP1M6DGPAo0FFadmYEz6@1 zntq}AG>r~b>vP-TX?saSP4x0q@+Dg;ZcBvL1(j4%M&#xtsXlx`1X@VGv=K3_?U;xB z23SSF+mMQ`_I}O> zb+Rybx?d|4xWeH_V3Lh8(2H}Sj^es4Kb1V&Q+vLxNu+ZpDlC)B69(sk@x1-v+URzm zXkJCUJ(+5V9)pN1sG8%K#HUT-D!N%4rKSd!vfQqP36VWQep|Tj(3v69R%~ns8VW6* zN{xQJyw5Y=x=Hz?CKdHSvqA6oH@}LoR34Mtw3iMt>KAV^F-`$PPhyY0%&e&k(nq}| zwhHxNAQk&ro|Tn@rwB+`F+JsDCb!q zBZov)K4}bG{+l>I6jaC-Cjk#up}8fLJ6;B5Yy6AO8mhY|g$jy1gIw-+u)qkk z*3gN$E%hT$j()0X8Hj%8#x`}WuS7ngS5+@Auk>;0tFPtlX(yi^P7E2!KnWcssyDy0 zdojrZ3rf(^3O!^iWC<5upI&zE{FW;Gn`k%F!-{uNqGXW?87dQnS8|~42GKVeI{P;m z(G?s~9tSwAf9(+;ParD$x{xc}pPkE*D*VYg#Bp8u_ar-vASsyPYsS)e!z_BHYOo6L z!S~XLz}!(6BH=tcnXJw0PcfBgc3@q~JlAeKz~Yp7;`>8cXL88IM@L(SldO=!X}a)| z(e`yhi2B%-?rIo(d~9B$Z}bw0=J-x%q4QpJx&9g!ELW~-NrYDujf537r*M$b2#P!>*Lk5GJqtkLUaIJ;^z+{e1#Elw znig$HyrZpa5Tkc7=q_JBkYB=Av#O*>A0%t|u%45N{nDjumFfq03Hwv(xm@8s4(2d~ z05vzsr6k)m8_4YvI;o%~l2hbm-Nm-8AKtqdbW7f+otD2DgVglcCh>>wedhDuj3X3j zlt7Jb%Sf(>V|JI&KbJ^p8qGOqyP_+x1uqE006qBU(JaZnnr$@-|M^ME^ z>*q1uRm#!(-d5V=OY>NPkAKMs$RP zkv0c2?*@)bdE{mdsO30vm2KRe?Z@dAe~$}7tU?nsNWT-zj8##)NuKl%`tEfdVlAH_ zpJdgdN?zV9ZtV<7YbX5@d+4SV!Typw@CafP&E>gJ?C{Yv;`oXXoi55XDNF&0!h4~u z6&l36#m-EI6r8TAoEE8gt5`4BS{?Af`)G?>rtHks>|t8#1!WQOUeWfdh!s?pOwZz* z#66L!_E&Im2Q&`-6j8AZN6z<}gg)Bfc8`#F({mTuc6(^0Aes32r#(D=(}K zwpGq2z*U))lRoGoK}6JW$VI74H{ZHguYIN6`l+;`q6sEV;j07?NcfQqg(@j$i3b?F zD!D6MM6Bd-Jx`sFE=d`rdAmEsGgj166Nd?p_n`hX+Lc7uvUgL6SJJH{qO2+D#oexM z!RhcaAkim;jGt}&p*~UT!&xRJ_#yC+Iv`NEzcgYD@B|cLTyC~$g{$dwz#G7 zgZSc;x`eb{!{zz!R&z`+sgHFB@$0Q6#aoA-ub=)_*z^X-ec{UY$WC4SD&IC4-a0Wz zO>m)0>1_Mh4~2QUoUXlGd315eWkmvfkaZ8;wk9#~$rw=P94wD6eW|#&Xtw^%foah@ zNUK(m83ak5Y`$DpHNgolF8kSE%{vX*VBn1(+ka?qTH0C~Z+RNHP!^q#-Fop8v=QSk z1(`+=PE%t5%O)CD{+moyX!?IezVfw7F9T*Y1E8xWtwbNRTRyv|=fo9YKxCWW9}p#gx2(*^I#%vlCe_^@^hJkG?WX`P*kf%Cl4-$n|w zzrN?P`*yy9071(ntelB7VzkbjMzvELjDJ;kV)UF7YZu@CvMVfGrk$WqYB0B4>d5?k z3y`a5U=8&hw09(FMAA#T;G0Wm${X}NNNZes()qjJYJB+^zl#>YrS6!(jcLJz~^ zNPZXbw4MwtOK`!o4E&S4tw?KimKdU>1<%u%UOLlB;Z_i#UiO+8BOP{+gB3?OOGTB$98g?wlep=GanHvGw%({n4!CVPWY@R?yzb@$EVQpK%JRTW!B7G91S+us5iN=mFX;(72 zk2x~l=)*&lny={13yvvrms(vR`q%Q7!!#b%crL7LV6f{)KV)0dDRw7w3I=B1hCZTK zzKiE}Z+YKr=_VUYI0MvPWH+4&tt zO6NasS=~K(q2RM|m1G}g_5HE&ZYNmnM(mr}faMm(y6U-zzJLE|BtcYVi^P!)s5N7i zG3eIB0E%UgVS1;S_9pwSq0V6;owCat*{^)VB=9u{Dz3Ii36s*FISvrb`=`(SaQ3|h zA?*qlW^f~=zD+7pqgmfB<{N4DB|3n00klZDBdfrg5WFgE%$b%CV^kO|3(QzW(~?Qv zVx77_3f)w!z+jX%V{W(&(9%*k?2glzYby5mRnB;K_eGlo2&$4R=@gfz-q1An7Jqtt zf06t~g$3!vg?bz6WOL#)I@7MG*{$BK+jold`D{OlwO>DRUbD&w6+Zt$CaE8|tIWSh zaWiMa*l9c6iIpEzA?3ur*f$WlZyv)C8Nc7ZG!L#z;Zd(8bR(=50&dPVs&<;rEMimH za(KZFu4z4VpO%kn)zQ7;%WZKEm;t**>Natr;X7~qFRb}W?TQ#nfO21bRv6il!OzSD zy0WkI1RmEy16OWWdGo7h{hm9A+;O<(!Cji$6 zty8-AG0u~5v8ds9+?lCTZK2W=}^)Ny^t!8o<=-irF9M4(H%utRBOF#oFbA` ztr%@LL%v*lJQV?^sW7KjpniN5Oqzg(W`b_<#SLP^e#vrEF$gEKBo9nS1b}#rih=Y~ z9??%&wl~VmU{m0tX%h}o|6hFmng6km$9TtL#56*QhlROyj?A0Ju?1(tMcE+zh90qx zKgv8TZ9GKAlq`pjw@`-k>=xhG=k1JOHkTWYqeRtXSScYo&s)rf_S!MgpVke-{o9E< zYm2#FXC6a?Gz{}Xh!gulK5~*-9OcU3ubDV~WV2H4e_4d%F$Ob<{lv0p$xuioIeYn> z6uUWyzC7$hHO-r8L_UR&tmD6LnA!#0hXAIvHz$=U>NqD+&EYbMRJ-9P|7Xt8-nUW8 zyl1I&#!fHd{K#j23-5|za)8AmysrZ!MT?dgi$wj)xG-K~iJY4yclLwQTW)?_t{m>e zqhA6VbQQFWF~DD?Iq9GY)XbDj=SkEJS@hL;kG5s&y4I-PUp&`{XJj{jn&LKJHJgE; z_GVahgMZOxKepPi*7akzAH!{pyP0Ew7G7vh;uP6asba3W_aOIHY`l)2=Zfs_ff1zI zJ!AMGO0+^qrlE-YJ*Sgxl|5IQG zZfAZQq|A%9H80TnI!ib7A|lgxb8I^rl`kGPzrbIumJMDY%*MRJ@XT1qf7-199vP?0 zH*sL>$Ah|0Q>_%(;ZDwzsu|X1aNugf2AbkjXqDscxKMF`tXVmfWBJ5H-N# z;Aq>kSY3hV=Q7wA_aW0-C%CG$_bJdCi`jdUCkE?|1`0J1sl0$sab1HUUAVs&1~>>o zCt~V&M!y)PtA20)4{^>Xb+F2_mH|gZ^2yarT>g0V3w>r>=P(+NnG=&^lhfK7F|+kI zyeqZ2e4yg!A;0_I{kQ>5?^z^kOf@7eqia2`#3Wu3pZ-~lVSUf4(^DbY?mOo4W($sg z{`!sA-j8*v8-}{{zh+2J+BF7(MZ{jZ>FL(KU+PlkN)nN0n5(q}FY-nHX;zfw%Jh%r z+mH)ULx%sv0IjqAB9}GG_pJSOrGWjwmpsLpKX%l-&^=Z0gn6Q$aIJAEEy!MENtMcd zZ-Dg1^zI_*3ZzUal`1O)jKyr;4sEEkRmk+mRh%rGmhsrHX9l!Az28dv*gn}yCetgp z`fKNk*-;dq4{-0TLB_5;Y3XGI_fpz1XFC_+R8_Z4aL#+;8A-)*|vtk-ktjsFA;c<@L4^nsk z@wCQZHcKnz?o7s252uKcBU-yig&z*W1CB;A@luo45KOR zG=Upy^FnnjXEocE{tKC*>FbG^7KfR#(_@7(CAfCYQwOeTSZiz^+*U=2fa&MrUr{@= z6X>Sk{&*igD}X=k)OhOAn%6J+vw1 z{;idCv(r?D9QURkHc_9<0-;Bsar4cuN(@zRPmd80(y%i-ibY#0#ycf>ss*~HXdkla zW6lYI)?_MF=#%>2zdmEl(4ppP#xY|@A?UMABAz3j6a-nvBzDCscKjGU(j&o{UOThi&r7{ zRLMF9p%!6Y^O!O#^kP1SyIZA|L2|vJKEGxpm3?YQxl=&LP1|#f>XvN@rXy#RTaZN0 zafxN0*H8%q@oFm$v*GeNn_8(FTYO=T4TQr}aNgk$Ssrdr+M|9s|ITkFYQ;Mx?&{so z{Fi1q%(R}a9#qW=0M${Z4VO<_e+uCu%=B(&Io(~I!*;rVYh`Byztv3`8Cb^;^;Cd^ zeSa|=P_RKz=y{I}E+WuoJtq}?x8sHHo3iq|MQOQpUN#mnrlbJy;6c3Q5Bp5qKmws7 zUmuSdYj9M8`9-=yKUH}@zbR+Q8VMTgL(J$`PwDa9=db+9YBVk>!K@Aa(~$^T%m1O`-p`QLp7%Y^gE;e5XPTQGX&M|K{d^P)>wS zVz-hw_|1zB)`I%XE@l@2DEU3DbC>3EV{pOqaVJI za2-AXX$v1u9Lbd&5;15j`ZY^8_hLR70`d#gyMTcH)OR&U#u}zqxt3r5F}31=kLNLP zTMq1_VIHAoMf>%yH+wq|E(7ewgD?vl-1TXrDVqE5OB?~VcX6N3++^xl&+`aqKY53c zFlR2Y4f~mW%VF5EU#@de6;^B&aD}9s2uT>lsJk~Oj7p^sQfpTl18f5aCqpD9G^t&&T}_p)6nz^9^&D-~Ugd|1X~Ycg+3o=lpLbW74Ak z&-eVlzwrMy=YKc(|DDJ(HDtHgBK-vYb*i_DlnFx-s4%bECzgEb!DxvN?9l$tw#H3f z)r%HFFL;?z-YA2^jj=|z#!gOG^hXd@?ZANp+~@~+E(@Vn=i#f2o{Swz@uPgu=?h6~Yw$MtC43Yq@A0?c21$iHwfX%dqmvh!6 z>i?s5q*-&`gg>&tPFaB$IOLfiuF%dku$j)qowfJ-SK!9+2g9|7?>S#F=xiQ)Ty_~S zznZd!slmdp%@o(wTsvpZu<&o3ohVefrK^TUCKjgcRHCbUSUKy*m)`{86tiOdSatiPqw)olT_gC7E)2H) z9eSw;BB}XzQM8;t_skAo_-0ZIu8Z%${Y+OK;|IT$#n*{DTFxxQBXHm8#B;CdaFf6^ zUcgdr)W58KGQ$c>Byr+OFvqcI)09_2j+U{NZMvdu!@_&6ePgEVczKWWu{~`b$d)Zl)1?rEHY?yL^&2`+RwE~7;{#({4YQe@+ld$rf4W zI7=Zv15d5kvcWfFA%D&36Kn+;#K4}Opwo6DMf06^vXty!Z`*bL6s+%m*45A@#lPlA zmk628GjR<&b<7OYxe;qAFAT@s;-gPAq;p}Cqtquo5jrK@x4N&l?9RQ4_niupwwSg^ z@Oz`(9(yV-ekH2L-`)oHgNbVG&b>=@Z)rPriV@b7xL*S%ntIFZy zQ)iVmr|h#r=Olzrz~q}U#$%n*2a4I@78Zn2tqz&f>z3L}0Xh}7y1ZJr2azc+6ZKpd zb-(;bSYhMVsV2zX_J3he3tq+6Wew@IMwm`&zBK5^kp*9zj2B%?4bt@dxFvEGz1fk? z*N2aA1I7nA4^b7Eh=*D)CkrcF_w`TJ59)MhztavYlkN6GPQ*yJ~?~e zr~;29w=X#Ov4K>}_o)`y7{|_=Y5&vAwAaMZJd*z}rqO>ALM}$h7~NaHGrDA5@|%d@ z#OM5|I-)@v^d&OEpwFTPDpqkMV79{l8s*%1b&!k8ER{$T{_X+mRBvo-aL0&-g_`%Y zUW4wQ<}MCk_$DYFtY8Wjvr~d5;n7@fT4X=Z;C*4T`7+Y4>Q#SVf8%}lli~|#REpLIXeo1U!UqQTGUlwgGnxGYvBMLVp&e>ZnaOI4t0Fn zO+#Phn&Y|a>G@QY_3a%WmJdgK`Qj0h3c&;R9LD%^2Ige~G83v3kBLGnO+cIqx1L{g)6onwd6b&kUymWOdNP>TD)G zny6pT++LkeRclQeIM+-hK2jhp4YgrZEu+B#UdT-y7mn3Gpkp=`KYgpg|H9vN!-*$} z?&wX_7L)ca7J&Q5!Pt9wC?KpL<71}!7zW^2EjO8fi6Rw#z0uCB>G!|l4WS5gems@>k)0E}n=+4O9acsX{g0|DLI9U)`Eraa3WT2oMN zU?UijxC{*v|6Z2x^XdjX{Sg3I(vu)d2d#&lS01i(sJdEQ~Eg8V}TXKpEXfluI(7M3Af_`oXY+C@NZLcD@2_zfTX*z;hR z!g(jFIYCX-#I~b}e{6iVrLD^_n8#(n;18leSh6ODzi9m8OuV^-N7^nHg<^Uk z#RtZjeDwqoa`KODDBj?^og5~pj^8V5R`~ zu1^8%)_2F7@C1c*1zm(-QOa$}RoaE7Pdt3%P&?v9v&gkSpGVLAqZxW8hc{~#kF;6b z&3VuUC2ulF@BpXqMIT^+Qu`JoB%FPr*q3Bti1+L3$Mlys{@nU!1W5{wzVTiwz0AQc zj06Rbp2UiUu(=y^e!S@yErIxq!~*lItJsQ;_|&&6TKmF>)nf#VSv5LtV(EANZxet9 zd%YSz8Z|$k7Yx=ID)8{#H7b(tfB5ecV7y!|s+Z!Y_-VjqzKPnYV@8XWe=E%lVxQJ$wA6>Tf9zaKGbqYO?}i zrmosQqzXE4!aYLUvOKfw?Zgme=MD+=RBOzNT=ln7XSmkO=9eDT=J{@>qD?-GV>ei- zKh^u7iIJaE$6dE>xY)HJKW?b5ZQ=xU+C+A zw<4`d@NoFbyG{-v8O*BWODyK11gqE@JX0O!G1x3S;df4N_3yGUFgvMBg zw2o^QGliWJn5u2UxxS{XKdZt6s!LslFq+527iy>4rVE3JN&Pu1tHONWAs%Gt ze@#wl5yx$(AIEO0Ef|KM{PV zPA1eU8v>DLNIIWV1a^N)y0N~JUFgFE`YG@8rJmTzdFnG4tIX7{jr~-N{==;fSg)h* zz0q2z-l9I*E2hma!IV(1`FskuZINMl?<+|WQ((}K;>X~y<^}`pm*zg4eHDII~ierZ8EL?mVoVd~eR>tEMI_cy45ge4q-HS?k zk>|8-xMt_zFZNxVVm!^@y-4K+`#r8@KGMqV(5j9fV=9Amr;L+yejB;=i)}CNpvG64 zAql2khVDB&o%hM2LacS2@Ezk68qX297p@z8scderfofSJ1Y#`M$24rrF3Z3~L6*#Z zRO(2*JR=21n2eW;_aK*{P*;DU<5p7Whg#vs%V@)kPqaZ%SqV`^g^KUpc-l|XIE+Pf zVrh~@JJC(TuO0}xep+uKGr|hOJ?oBR0SU8ZFZEqC|GlFuHAR-m^4-}a-eAjh5?$|H z1%=UytRkF}2T?<+$7j0)M3Ibpia(t1$hXsbRr$GRUF9>Emm5B>wUl$app@>(4r}O& z+}q#cNuUN&^>YBFaw~ep@I|*z;WLN3$&MOZ7E@|tHd7`a9rt}0w~|X?ineNnxpORG zZ-+}lEX&j{*G^Px+%^t_s8wGd$g2Wh5l8f&#wGSo`GsNq#Io z0rAoI*d_YnQH|)e%<2b?Wo^8ORD~+v^p$rBZmJ zo*4oQ~YX>Ni9!1P=IG?$s1=+$0~xSpFm%Gey5C%i!%14_rizI>X{Myye%7G z=hrTyTQeU$V(Tc-<`STRA$TfRc!c(EzjwY_T?WPF^R&&IJu1gy-V6ZdV2EOVLs+d4 zu^ty?t6k&#JiR=Q4DT-?m5ljuQ_0iEdR!?yFD)Tj@oabuc>g}B)pEaOk}v{p@xfR| zTd3L;O-l6iJ>VNnag$3d4u#~U2qL2g;*D-Srxm*O!o4g}rN`-RdT0hPdPzyI;IU3s zhMu(Sx0s&R+X-)$H9e*6NfrL0$Cb!mjiBV4H@y9Rsd~!Gi*cc;=jW1UoQ_v-+Ad5PNQf3|RPWYoj=`>$CXG?O~~L4Ghiya<2; zk02QL7ry6kXD#3v2d}Whjl&J&)^3at9sVN04T52dK4G}fg(YG)zQSLmc)6W-0jdC| zdf~=PVe$FSsS$9R2B}Up=d9MIiFXz>R(?_8zREfmF23>K!WJWRNbquRE8Kh6n18>s zbnn&mM*zrf9GQPGF5uo{m5e!}cm(LkYgN?JV&~`pT?_x+do|X0c=Rvsi~~}jb>FFP zrg!GB7Yu)GBn!Agu9RF88)*9++A3miR)-+wm2_VdQSM53n@9?aQ86}#90|K#;CHaN zTA-QG%tG=k8n(owh>RKqA%LVFqcuXj@2oe8MZ&~ZeCzJQ-D*ju0T@L zoXsSa6Mv^CfJOR>w#-|Li0HbhttH&8jr|vNdLRMY>E4`_1~48W9Ij3ZKX)}-unGK|*;Sm8jA&om`%rB%e)ib#e ze08?4ojX)joi{MUe?eixrIq*7Bd)`uXShrXl!tZ=cw%pQnlS`$aUjI-k&>B_8%0VU z{qZRL(}3JAhspX})FOaP<|a?rCg4{(ud58h6`wpr_Iv!ExnXcoX26ADr6-24nDci8 zzmxZ^U^0*=c>f!mc270!ulGm+U#khVn(A=q#_}Nn0EoUXxLsS8``++#6Ib-lx)}2s zxv+6prA^==5%I=AW(L)prxh$B7IxxfPU-+$jrW-4KPfn1$QC|Dk|DjFo6Fb|{TwZT zTb0Hn6ncJ^uYH7{EyY}=QFzE4qdbI2`PiL}j9B_8{bh3-VfZ}xSliN0yfAYEExJxxbf<3r#ZzF{sJ z+th%!UdwjGFfmb9HJxLfmU~ylWb9KtU(Hnp_j+(cB|Ad)C`tRk*jpkUL^zF~n6IZbtP7bj5kX5^?Jh3v$d8T`H5_}^qYX(pWZczffB|cHt z^dl#D@s{cI=Xs-qcHNHht7lg$t+FM3i#JS(?n^m$TgN4M@xkdi@je*=_wdV&T7+t%KOlNKb1Go3pTB*#LRm#oXCi^bP&edPMutP1BvN z6T7)DS(PY@E^6Z~^7*`c2vnS^9~yHEX?+t%rTlWvCunvAxc}(V7NP%ErZ4`w7lL>%w_ie{bHLJ ziPR~H_kU$m61+PAAUOJF10r5Zvl!m%%CY05kh)KY8{_LD*QjX+-8sNd;9U-<;VDZ? zVk-^S$!Yo8nS=OEGwJZ32d(;SU@u`q9v}i9?Vpzn#1(MoxNk76?pU7&)U9C(ix~RK zch_beDswJOXo)nnJ#}Y~6NR)cvy$OE^Ya^+>N&-RPlw$H^N&N!u`@zGuIyR6mSx|m z*dI4=#EjiCFfiw;vfC< zP>r^wDml}dXl;B}A)A`hfa*Q2YJ7%Zv-{e_n~1~=VJudDVExSQG;Hi+cMBPVvB-hvl4O-eF*x=cidwadr73I78RB-YNU+Pj!a0jVHfa$bf6_jgC{uGo*!a{`<465KBZHi3nWMt96EW!ln!<1yobF|r>W2Rj zx&AXhF}8k3322om^*um{R=s}&Z<*^yHmc8wA{MhV;f3q2h>js-ZT*ctSAO?Xne3Ud zJL$Sfpmt@x#8Df9YMS|e$7N@I=Ic$uA}Mb3a>JORm;Pt6rm|^c!FKC%mfGvPZQAR| z4z!NYW1rnr(S$*anf3P2Keqf}Se@W{yR#YMcrv(H+^LVP(*=pI>ti&R(Pz$1n3=NYzU-=v48kT=753+&QB>9IPTXE9?rF6!>WEOi zxt+bMv@_0W=s&*%5B$p|Bqn9S^=ju-K7cxj)7jYldAO_K1D3FNJ?|77+-LUPt(x`% ztoRQ^IPa`9r%)XUBkY=HFJ`f5i{(;R^@h`$Cndqv_=tqc2bK9$<&q92Qi6Kx6vUMg zeLID_y3NR#AT^;sbX(8c1~Eo$6DG3^wRPbmKj(c!q(6rHEsJ86jO9aNlW8eJM8HS;X zoL?TwGZUt3Bp1#E9!n1?QqAJ80<;y6^Y&`4cqKBUP#5m`)oZx|0P{CvZt~ab<)~Sn zjv#SzXBg^_iDgohopM%WG36K49Q?HMwV^C|2((R-Ph!HClX#?L$V~oz+6|A0vzu<= zN9a9iH`3q}>5nY#>TV%UC}CgF(O_akMih5Rh%;<^TcJX-)Ma>>AR>a_AbP<|%FsLf z)5^MlG++EqLC~1&q#Sgjnbx(sdAg+dlpm}HzC#ieiXyNL`)dlNmzdaqlb3ovcMi0S zfzW=A;b*kXOWeQG?jmObJ2QAN48^+4MD&s1(L~7qp!vfD7IRzo1zWLe?VCU+)b`xT zK?z8X(l>`KcN3^;45y`}J&%xPBw{n4&zSJGgI<0IDu?i=JhYsLMp+XI8pqsKK&wd6 zbw(Bou6TP?{pK|h;l2A4r~4H}HsJ{j3|jT7Dgz}!X=t`iH8@D%Th~L_V$l1FR?N35 zky%k%VBD|zS6gLTT3jiJD=+c8JF$jA(Q_w=jpAI8>cP8H_h!|(mwCS=2!7xW`eS5B z6HImJ;!uCu*0?tayeL2o${Pw2ZyWAUAn|HepTcAZ?1L+>j&)wD3bR|66}z@+v&-?e zvQTs7sq)kDWj}$1Le>`ST6 z8B{KNVG^`b_<;S`$|cgjDOHB(?6e5;>+@LyNzP(grQdsgnRCcNN>bb8OYYWpK~Bb2 zK@v@wmBQbmAW)EA@)Zo;FD`xDLm%#-dx!j9L3 zZ5O0P4iG(()lk&EhCqoP&)ZtkP1`ALDx`Te(|2jej!?!J=5OVT?`iJUtH-16p&Ek{ zb6V;DR5vLoLe=t_hfnrTFmIMFbVz|JtMI$qeQdCOVh{_@yAaq^`tvZuTE+lBZ{rCc zuNXux6jc$p36t9kjymRGoU|Suw7h~f@qnJPow*Q_P4ACgeN|5#Jxu&_H{s~RyB2hi zs0&2TiIwQ#;sfg9TX`|rsELo7yVfV_d8XS{r61SxKWsmVWM-4|HiOz$wUFk$)rJ}} zD8Yg-CYoCs57_;Rh#a6-udv@sliEnoxC>#57>vP>Otz9i7|tB|R%2s3bDQ-SCCEGb zM=07}B%cE;3TW%Smp&{C0>Z%G&bR2al$(X zD1(nDMkXFoz`+@Sv^Y!r5ljSmB$StUR@ z=$7Gq`Eg{|r$z}7j6C&hPiP8!ZGt^hTUr=0)XGQRV}+Z#{jCuF=SR&;}jw|W+FO!#Jria&y* zmKS{e_4I(T)@Yh1v80kJAMO1RMD@dX&P6$*k2$X<_#{rOI+xv>9ONxA8){~T4~v>C z3`O~Jf(@aecE@L0{;@=e7mK#&RJQu5+H+j*pxxiCo1iP8T8eXaop|pg&})6`o;pdU z5jBfk#P_;+7v5T8-MCMg|AqEH~sClf_1dY`ne4@Fn zP3DInpO-c`SSX~96|6%8w{6NmoXVGOrev&sl!YYw+pN1nBKp#);30E?OeLEv;G&|F zzKKT+UR@`XwHMkLTMD@7kRI-0s$8)$xJ(|GL@p%rv$^7L9Z_6z1nOdUULELxzQ>L( zF@t|S1W{N9AVSIz+mj93co`yKU>S%!k0MTxhZH~{Xfq}CYh{|ejdK~jcTML@2K(efakkv{4Fp&>n3I%iMgxL7snvDgXZ6acgwZ=@Dugwt8qLG%4-rpwyQ|x&<6wR`Y zHQV}pgk8X#GR~`^tLL)=oa03wC8hknjysUvA$h@pAaQl8o?-&;Jb4lS6{QlfrC%}E z5rdSq9g#17~tWpx0H=2b}!=aZkq$`zwvl5p-ph_$pb`bj)G6*Z+_8eBVIA!7e?4ej^u>I zRPIiHZkgJ-W!@CgH@EHkQ!}UjtrBtGDMszX!YkD>$rh4?m59DUY3Vn~h@}0GbXX^H zII||oCdsPGH+N0@YlUsG8P9!|7?grCv?6*ztYD~wa|YxC((P<)?ZpB0lDJ8dSHllJ zKEBdlk_1j?6TfV88^b>GAC-o;mnGq|fGrm(RlE(QC0W3{Hflx1lku%SFM-9B`@kX% z9Y1kyli?kdwU80$1}p@$4ou*yzP#rC-@C!4K6xgp7V>9M_E(m!vXel^h!0s+RP~ur z)d@ufo_+3A%eg7z%fRX0#t0<=tr^7T+I_K3Oq?5O!aPWZE^ki{>LN>kkHw-?p_Due z;V`=kRx-o;JAjvVR=7-Zcj`U`h^T;&EJ@&YUQ8h>5PE>MdhI#9frMM4gQ1hBiVMXM z=mlxTw40s;!@VR_;QpHd{>Pgy4n2H|I8H4h`r^2nHr!dj4>(5Yy3kwC;Bze+4XG>V`W6WNmq4+H7MFvX3;KKjte|?y3iC>p zmim0O8{KIpa>BA+r7|?7_(y=Yr*6n+yQbMXsB(nAq}3(3vyBGgc>|YlTzC3VfI=ed z^G4*3;Y_3&jP1?{7-dr`$*`TO5|o~wvEpaUL&H2;HCf&8GH<-#MEuKR)0+*~h2<4$?+K=*0otgsvRd(Wcy<@%03YKqH zOqAirLF$rPiU<28$!CvXp=bP%NBns;8QZ2q3yrur`|OS%wL#L{U>qd+ksuYZ2770d zU`t+m)3?4upDq^gb;i=pq7}nph-4sY-x=6-6d4ZJ=0)R1>`Z5q4C>G;2d zj2NQNS%Jy`Q`iOXkZon3m;P?kHI7u8jJ#+)nTM~D!=o!vGmA>9$a<4$L3DiG#8w$9 zUUe6HT=XcS?^l%2q_pu>elE$#b*khzCK*}N6RIgrlt*g}y)h01pz&YW;LyY)s<9b+|k<>cKep;L?3I;A!P*f&UrPKV^ZXxw8%cC@_hrM znc;jEupz+!%{C$L%a01sJC8tUj2=vc0)(i<>Cwz3beZosNGEdEUk|yjreA0E94GD1*@Yz1Vr||n;5eT59Bmset?6I5gDFkRdkPbWUWV+ql_;dTQXkD z484@Y@nm3tz~l&}1jqf;k!$x|bNPz$e2Hu;o_x+Ap;{t43gTbkeB&S9+C#vipBD{N zVUj*Of3$OcGO*YY+NlKz2@6d~V*jw&rKWBG9NU)-iM^2Azj}P*kyC{`U*QDR3gZtVawwlMWxK z@6cxbG`Jk9ad7shgVXN$Qy^Q%l#QPp3?sac6-(j_ytf;b8-qQ^b~SESpo&GiY@^9& zzYP;L|9ng;r`OniqOzLiRk?-_!dia6)!Vgn4Cf;pS^E8HbV&h54yGo&?{I1B*#^^8 zcXWNj4^3h3W4nK1ektxO>9Z!LI^~Pm_S zI6PO!sEj?fDcp@AL4EuItoL0>Pq|%EYNsioM$l@TGk^S~;v^H)`1_kax2jf!xP8(? zpC&VeoKjfPR)7;pRpVdj^UYWt3zFo^cIw)9jNrq^%@2cW@=SkDr(FENeG*)zP=g+fUqFkAJWcG*^n?;Qvf4H@@L?p>{PI zK60d?)A`V|){v^=$fIMia&%$T(suIZAln;z#2|W2Nnbz{Fwt39+dAJR>I( zrg*ZSi41GcS$%24l#EaV&Iv`$`DAi>9^JEFYAr~J81bcu-GPRj~S|;R2k&AE6vbD4u*#n zKfZrLu}cWtxGsn%i;-@#-boV06JUtbV|r~(@Ag>j&f7!GfV zz_mP}$*uF~5DH{vWS!)MrWilxqM{3=dl077$zh0_!S^72>vTHbRHnZEh8Xl*uD}}3 zFnn}gOi=4b1x_WqMGo$$oj)T7H}n!ts~n_$Yj9EQW`-UTCe)7c!7{SKm6@Sl=X{xk zq8ubTLReSaXH!*XXv;%R(E2dvX&?r;6*4DR$Hw74freOB@oc)rzmy`ffn6!qlBsm6 z7B?wkxIoGGy_Iht5&fucVZ#2Pj*r z&u5BvFFr2Qlq={%QK!Yx6O8QTnrMm$X6}xAP8D}&=JYSmSsaCNlyN-amzm{%+d-HR zd+idMyZ4pW6Ij(gr)&A%mrMS#XX|ar7fU-lEsropQFfP6uY5_h=Z8w9aw6rvCymix z=eu?C4XS2*{6g7z*wR7Og#~811Y_EUJEuTDRI!-o4Uab8Y+Wrm*qjf3b(yWFfqIr) z5b2thz%+H;)7U1Uu6GstE*E~;_SiIoE-ki6R_XT<#qsJ1HQ~ssw4+e9e;d71QC9^t zS>x@wG2uh(nD+i;a<8LVTiu|JHI~bP3X#*6z~5DM5U;fJ2D6q9LjI}WcA4GmtstXk zoxW}klvqivhoHot$TvyLyHT}5l+OiSu6LUsf}ic}zk%57y+Ti`7ZGMt%1lEqt)5TG z7rmEVX(kKdft~8cwsZxmFhPHtnMq`X^G3!Y_3?jo4jLp?zP6#xVQ*#_?LE+K2zByX zS0C6~M#rt!+GcbqvQ6H3+Y{0nHX=GwIel)@+PjG+4_HyN-@qbz#vHVXhPW9luD8uG z!)n**cQ%rYUhKCzT~eg@MjNiHp}6c?c?%S`X|jo%5*%9x29+`=Ud*|}h3a+NKaoJ* z;-%65wXQ0WSqt4b1grRg3F0oY3~4+LNWMMs;%WbpyUohi^XneJ=a`t5vL!KzW26|r z%%YUJOM-`R{)a=MpH$ zrfN9QQ`dXLnDO2_ZeSzTgv+rqCPa4T&-b5$y|Pv-iX+MF)g?NnZ$=~v_2V3LHv*&N zT&4&P2&FTbBu=*lI6qGI*;*5t=B_pw8GnH8Pvvg}4Pc;y+pZbt51=MKFS!jQ`V}xs+HvcAiiW)i)H)(=i;)3b_7nO6b?=ym*Erzj?l@qNYL> import("./spacedrive/index").then((m) => m.generate), }, + { + id: "registry", + name: "Docker Registry", + version: "2", + description: + "Distribution implementation for storing and distributing of Docker container images and artifacts.", + links: { + github: "https://github.com/distribution/distribution", + website: "https://hub.docker.com/_/registry", + docs: "https://distribution.github.io/distribution/", + }, + logo: "registry.png", + tags: ["registry", "docker", "self-hosted"], + load: () => import("./registry/index").then((m) => m.generate), + }, { id: "alist", name: "AList", From 515d65d9934b454d9b679c43bef79dbea3da100a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 23:01:36 -0600 Subject: [PATCH 050/126] refactor: adjust queries --- .../organization/handle-organization.tsx | 119 ++++++++++ .../settings/certificates/add-certificate.tsx | 1 + .../settings/ssh-keys/handle-ssh-keys.tsx | 1 + .../dashboard/settings/users/show-users.tsx | 89 +++---- .../dashboard/settings/web-domain.tsx | 12 +- apps/dokploy/components/layouts/side.tsx | 222 ++++++++++++++++-- apps/dokploy/server/api/root.ts | 3 +- .../dokploy/server/api/routers/certificate.ts | 18 +- .../server/api/routers/organization.ts | 86 +++++++ apps/dokploy/server/api/routers/registry.ts | 2 +- apps/dokploy/server/api/routers/ssh-key.ts | 1 - apps/dokploy/server/api/routers/user.ts | 11 +- packages/server/src/db/schema/account.ts | 17 +- packages/server/src/db/schema/bitbucket.ts | 2 +- packages/server/src/db/schema/notification.ts | 8 +- packages/server/src/db/schema/server.ts | 4 + packages/server/src/services/bitbucket.ts | 8 +- packages/server/src/services/destination.ts | 10 +- packages/server/src/services/github.ts | 4 +- packages/server/src/services/gitlab.ts | 4 +- packages/server/src/services/notification.ts | 30 +-- packages/server/src/services/project.ts | 4 +- packages/server/src/services/server.ts | 20 +- 23 files changed, 538 insertions(+), 138 deletions(-) create mode 100644 apps/dokploy/components/dashboard/organization/handle-organization.tsx create mode 100644 apps/dokploy/server/api/routers/organization.ts diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx new file mode 100644 index 000000000..00bf42b9f --- /dev/null +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -0,0 +1,119 @@ +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { PenBoxIcon, Plus, SquarePen } from "lucide-react"; +import { useEffect, useState } from "react"; +import { api } from "@/utils/api"; +import { toast } from "sonner"; + +interface Props { + organizationId?: string; + children?: React.ReactNode; +} +export function AddOrganization({ organizationId, children }: Props) { + const utils = api.useUtils(); + const { data: organization } = api.organization.one.useQuery( + { + organizationId: organizationId ?? "", + }, + { + enabled: !!organizationId, + }, + ); + const { mutateAsync, isLoading } = organizationId + ? api.organization.update.useMutation() + : api.organization.create.useMutation(); + const [open, setOpen] = useState(false); + const [name, setName] = useState(""); + + useEffect(() => { + if (organization) { + setName(organization.name); + } + }, [organization]); + const handleSubmit = async () => { + await mutateAsync({ name, organizationId: organizationId ?? "" }) + .then(() => { + setOpen(false); + toast.success( + `Organization ${organizationId ? "updated" : "created"} successfully`, + ); + utils.organization.all.invalidate(); + }) + .catch((error) => { + console.error(error); + toast.error( + `Failed to ${organizationId ? "update" : "create"} organization`, + ); + }); + }; + return ( + + + {organizationId ? ( + e.preventDefault()} + > + + + ) : ( + { + setOpen(true); + }} + onSelect={(e) => e.preventDefault()} + > +
+ +
+
+ Add organization +
+
+ )} +
+ + + + {organizationId ? "Update organization" : "Add organization"} + + + {organizationId + ? "Update the organization name" + : "Create a new organization to manage your projects."} + + +
+
+ + setName(e.target.value)} + className="col-span-3" + /> +
+
+ + + +
+
+ ); +} diff --git a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx index c6546f2d1..58cad7910 100644 --- a/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx +++ b/apps/dokploy/components/dashboard/settings/certificates/add-certificate.tsx @@ -86,6 +86,7 @@ export const AddCertificate = () => { privateKey: data.privateKey, autoRenew: data.autoRenew, serverId: data.serverId, + organizationId: "", }) .then(async () => { toast.success("Certificate Created"); diff --git a/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx b/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx index fc48134a2..1a8fe9187 100644 --- a/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx +++ b/apps/dokploy/components/dashboard/settings/ssh-keys/handle-ssh-keys.tsx @@ -78,6 +78,7 @@ export const HandleSSHKeys = ({ sshKeyId }: Props) => { const onSubmit = async (data: SSHKey) => { await mutateAsync({ ...data, + organizationId: "", sshKeyId: sshKeyId || "", }) .then(async () => { diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 9dfbfc931..8aa2a37b4 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -76,10 +76,11 @@ export const ShowUsers = () => { Email - Status + Role 2FA + {/* Status */} - Expiration + Created At Actions @@ -89,30 +90,32 @@ export const ShowUsers = () => { return ( - {user.auth.email} + {user.user.email} - {user.isRegistered - ? "Registered" - : "Not Registered"} + {user.role} - {user.auth.is2FAEnabled + {user.user.is2FAEnabled ? "2FA Enabled" : "2FA Not Enabled"} + {/* + + {format(new Date(user.createdAt), "PPpp")} + + */} - {format( - new Date(user.expirationDate), - "PPpp", - )} + {format(new Date(user.createdAt), "PPpp")} @@ -131,7 +134,7 @@ export const ShowUsers = () => { Actions - {!user.isRegistered && ( + {/* {!user.isRegistered && ( { @@ -145,42 +148,44 @@ export const ShowUsers = () => { > Copy Invitation - )} + )} */} - {user.isRegistered && ( + {/* {user.isRegistered && ( - )} + )} */} - { - await mutateAsync({ - authId: user.authId, - }) - .then(() => { - toast.success( - "User deleted successfully", - ); - refetch(); + {user.role !== "owner" && ( + { + await mutateAsync({ + userId: user.userId, }) - .catch(() => { - toast.error( - "Error deleting destination", - ); - }); - }} - > - e.preventDefault()} + .then(() => { + toast.success( + "User deleted successfully", + ); + refetch(); + }) + .catch(() => { + toast.error( + "Error deleting destination", + ); + }); + }} > - Delete User - - + e.preventDefault()} + > + Delete User + + + )} diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 401981473..0b002e9d4 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -52,7 +52,7 @@ type AddServerDomain = z.infer; export const WebDomain = () => { const { t } = useTranslation("settings"); - const { data: user, refetch } = api.admin.one.useQuery(); + const { data, refetch } = api.auth.get.useQuery(); const { mutateAsync, isLoading } = api.settings.assignDomainServer.useMutation(); @@ -65,14 +65,14 @@ export const WebDomain = () => { resolver: zodResolver(addServerDomain), }); useEffect(() => { - if (user) { + if (data) { form.reset({ - domain: user?.host || "", - certificateType: user?.certificateType, - letsEncryptEmail: user?.letsEncryptEmail || "", + domain: data?.user?.host || "", + certificateType: data?.user?.certificateType, + letsEncryptEmail: data?.user?.letsEncryptEmail || "", }); } - }, [form, form.reset, user]); + }, [form, form.reset, data]); const onSubmit = async (data: AddServerDomain) => { await mutateAsync({ diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 99938a5de..8e8a74dc3 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1,6 +1,7 @@ "use client"; import { Activity, + AudioWaveform, BarChartHorizontalBigIcon, Bell, BlocksIcon, @@ -8,6 +9,7 @@ import { Boxes, ChevronRight, CircleHelp, + Command, CreditCard, Database, Folder, @@ -16,11 +18,13 @@ import { GitBranch, HeartIcon, KeyRound, + Loader2, type LucideIcon, Package, PieChart, Server, ShieldCheck, + Trash2, User, Users, } from "lucide-react"; @@ -480,37 +484,207 @@ interface Props { function LogoWrapper() { return ; } +import { ChevronsUpDown, Plus } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { AddOrganization } from "../dashboard/organization/handle-organization"; +import { authClient } from "@/lib/auth"; +import { DialogAction } from "../shared/dialog-action"; +import { Button } from "../ui/button"; +import { toast } from "sonner"; +const data = { + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + teams: [ + { + name: "Acme Inc", + logo: GalleryVerticalEnd, + plan: "Enterprise", + }, + { + name: "Acme Corp.", + logo: AudioWaveform, + plan: "Startup", + }, + { + name: "Evil Corp.", + logo: Command, + plan: "Free", + }, + ], +}; + +const teams = data.teams; function SidebarLogo() { const { state } = useSidebar(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); + const { + data: organizations, + refetch, + isLoading, + } = api.organization.all.useQuery(); + const { mutateAsync: deleteOrganization, isLoading: isRemoving } = + api.organization.delete.useMutation(); + const { isMobile } = useSidebar(); + const { data: activeOrganization } = authClient.useActiveOrganization(); + + const [activeTeam, setActiveTeam] = useState< + typeof activeOrganization | null + >(null); + + useEffect(() => { + if (activeOrganization) { + setActiveTeam(activeOrganization); + } + }, [activeOrganization]); return ( - -
+ {isLoading ? ( +
+ Loading... + +
+ ) : ( + + + + + + {/*
*/} +
+ +
+
+ + {activeTeam?.name} + +
+ + + + + + Organizations + + {organizations?.map((org, index) => ( +
+ { + await authClient.organization.setActive({ + organizationId: org.id, + }); + + window.location.reload(); + }} + className="w-full gap-2 p-2" + > +
+ +
+ {org.name} + {/* ⌘{index + 1} */} +
+ {/* */} +
+ + { + await deleteOrganization({ + organizationId: org.id, + }) + .then(() => { + refetch(); + toast.success("Port deleted successfully"); + }) + .catch(() => { + toast.error("Error deleting port"); + }); + }} + > + + +
+
+ ))} + + +
+ + + + )} + + {/* - -
+ > + +
-
-

Dokploy

-

- {dokployVersion} -

-
- +
+

Dokploy

+

+ {dokployVersion} +

+
+ */} + ); } @@ -577,12 +751,12 @@ export default function Page({ children }: Props) { > - - - + > */} + + {/* */} diff --git a/apps/dokploy/server/api/root.ts b/apps/dokploy/server/api/root.ts index 68f5e4e0c..7860b248f 100644 --- a/apps/dokploy/server/api/root.ts +++ b/apps/dokploy/server/api/root.ts @@ -33,7 +33,7 @@ import { sshRouter } from "./routers/ssh-key"; import { stripeRouter } from "./routers/stripe"; import { swarmRouter } from "./routers/swarm"; import { userRouter } from "./routers/user"; - +import { organizationRouter } from "./routers/organization"; /** * This is the primary router for your server. * @@ -75,6 +75,7 @@ export const appRouter = createTRPCRouter({ server: serverRouter, stripe: stripeRouter, swarm: swarmRouter, + organization: organizationRouter, }); // export type definition of API diff --git a/apps/dokploy/server/api/routers/certificate.ts b/apps/dokploy/server/api/routers/certificate.ts index cdca1e97c..3dc944ac8 100644 --- a/apps/dokploy/server/api/routers/certificate.ts +++ b/apps/dokploy/server/api/routers/certificate.ts @@ -32,10 +32,7 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .query(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if ( - IS_CLOUD && - certificates.organizationId !== ctx.session.activeOrganizationId - ) { + if (certificates.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this certificate", @@ -47,10 +44,7 @@ export const certificateRouter = createTRPCRouter({ .input(apiFindCertificate) .mutation(async ({ input, ctx }) => { const certificates = await findCertificateById(input.certificateId); - if ( - IS_CLOUD && - certificates.organizationId !== ctx.session.activeOrganizationId - ) { + if (certificates.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this certificate", @@ -61,13 +55,7 @@ export const certificateRouter = createTRPCRouter({ }), all: adminProcedure.query(async ({ ctx }) => { return await db.query.certificates.findMany({ - // TODO: Remove this line when the cloud version is ready - ...(IS_CLOUD && { - where: eq( - certificates.organizationId, - ctx.session.activeOrganizationId, - ), - }), + where: eq(certificates.organizationId, ctx.session.activeOrganizationId), }); }), }); diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts new file mode 100644 index 000000000..dd933c9d4 --- /dev/null +++ b/apps/dokploy/server/api/routers/organization.ts @@ -0,0 +1,86 @@ +import { adminProcedure, createTRPCRouter } from "../trpc"; +import { z } from "zod"; +import { db } from "@/server/db"; +import { member, organization } from "@/server/db/schema"; +import { nanoid } from "nanoid"; +import { desc, eq } from "drizzle-orm"; +import { TRPCError } from "@trpc/server"; +export const organizationRouter = createTRPCRouter({ + create: adminProcedure + .input( + z.object({ + name: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const result = await db + .insert(organization) + .values({ + ...input, + slug: nanoid(), + createdAt: new Date(), + ownerId: ctx.user.ownerId, + }) + .returning() + .then((res) => res[0]); + + if (!result) { + throw new TRPCError({ + code: "INTERNAL_SERVER_ERROR", + message: "Failed to create organization", + }); + } + + const memberResult = await db.insert(member).values({ + organizationId: result.id, + role: "owner", + createdAt: new Date(), + userId: ctx.user.id, + }); + return result; + }), + all: adminProcedure.query(async ({ ctx }) => { + return await db.query.organization.findMany({ + where: eq(organization.ownerId, ctx.user.ownerId), + orderBy: [desc(organization.createdAt)], + }); + }), + one: adminProcedure + .input( + z.object({ + organizationId: z.string(), + }), + ) + .query(async ({ ctx, input }) => { + return await db.query.organization.findFirst({ + where: eq(organization.id, input.organizationId), + }); + }), + update: adminProcedure + .input( + z.object({ + organizationId: z.string(), + name: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const result = await db + .update(organization) + .set({ name: input.name }) + .where(eq(organization.id, input.organizationId)) + .returning(); + return result[0]; + }), + delete: adminProcedure + .input( + z.object({ + organizationId: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const result = await db + .delete(organization) + .where(eq(organization.id, input.organizationId)); + return result; + }), +}); diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 1ac37716b..6ad7e2a94 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -16,8 +16,8 @@ import { updateRegistry, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; -import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; import { eq } from "drizzle-orm"; +import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; export const registryRouter = createTRPCRouter({ create: adminProcedure diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index 725c67fe2..90f7cd6e4 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -24,7 +24,6 @@ export const sshRouter = createTRPCRouter({ .input(apiCreateSshKey) .mutation(async ({ input, ctx }) => { try { - console.log(ctx.user.ownerId); await createSshKey({ ...input, organizationId: ctx.session.activeOrganizationId, diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 91db98261..129b0731d 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -2,10 +2,17 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; - +import { eq } from "drizzle-orm"; +import { member } from "@dokploy/server/db/schema"; +import { db } from "@dokploy/server/db"; export const userRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { - return await findUsers(ctx.user.adminId); + return await db.query.member.findMany({ + where: eq(member.organizationId, ctx.session.activeOrganizationId), + with: { + user: true, + }, + }); }), byAuthId: protectedProcedure .input(apiFindOneUserByAuth) diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 0b5ef2707..da765e8f2 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -2,6 +2,8 @@ import { relations } from "drizzle-orm"; import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { nanoid } from "nanoid"; import { users_temp } from "./user"; +import { server } from "./server"; +import { projects } from "./project"; export const account = pgTable("account", { id: text("id") @@ -60,12 +62,17 @@ export const organization = pgTable("organization", { .references(() => users_temp.id), }); -export const organizationRelations = relations(organization, ({ one }) => ({ - owner: one(users_temp, { - fields: [organization.ownerId], - references: [users_temp.id], +export const organizationRelations = relations( + organization, + ({ one, many }) => ({ + owner: one(users_temp, { + fields: [organization.ownerId], + references: [users_temp.id], + }), + servers: many(server), + projects: many(projects), }), -})); +); export const member = pgTable("member", { id: text("id") diff --git a/packages/server/src/db/schema/bitbucket.ts b/packages/server/src/db/schema/bitbucket.ts index b8ecb6682..0311202d7 100644 --- a/packages/server/src/db/schema/bitbucket.ts +++ b/packages/server/src/db/schema/bitbucket.ts @@ -61,5 +61,5 @@ export const apiUpdateBitbucket = createSchema.extend({ name: z.string().min(1), bitbucketUsername: z.string().optional(), bitbucketWorkspaceName: z.string().optional(), - userId: z.string().optional(), + organizationId: z.string().optional(), }); diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 6e29ddf67..4e2917ca9 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -150,7 +150,7 @@ export const apiCreateSlack = notificationsSchema export const apiUpdateSlack = apiCreateSlack.partial().extend({ notificationId: z.string().min(1), slackId: z.string(), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestSlackConnection = apiCreateSlack.pick({ @@ -177,7 +177,7 @@ export const apiCreateTelegram = notificationsSchema export const apiUpdateTelegram = apiCreateTelegram.partial().extend({ notificationId: z.string().min(1), telegramId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestTelegramConnection = apiCreateTelegram.pick({ @@ -204,7 +204,7 @@ export const apiCreateDiscord = notificationsSchema export const apiUpdateDiscord = apiCreateDiscord.partial().extend({ notificationId: z.string().min(1), discordId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestDiscordConnection = apiCreateDiscord @@ -238,7 +238,7 @@ export const apiCreateEmail = notificationsSchema export const apiUpdateEmail = apiCreateEmail.partial().extend({ notificationId: z.string().min(1), emailId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestEmailConnection = apiCreateEmail.pick({ diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index f1dcd1001..c94fd693e 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -118,6 +118,10 @@ export const serverRelations = relations(server, ({ one, many }) => ({ mysql: many(mysql), postgres: many(postgres), certificates: many(certificates), + organization: one(organization, { + fields: [server.organizationId], + references: [organization.id], + }), })); const createSchema = createInsertSchema(server, { diff --git a/packages/server/src/services/bitbucket.ts b/packages/server/src/services/bitbucket.ts index 4ce4a7b03..7b5be7d65 100644 --- a/packages/server/src/services/bitbucket.ts +++ b/packages/server/src/services/bitbucket.ts @@ -12,14 +12,14 @@ export type Bitbucket = typeof bitbucket.$inferSelect; export const createBitbucket = async ( input: typeof apiCreateBitbucket._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "bitbucket", - userId: userId, + organizationId: organizationId, name: input.name, }) .returning() @@ -74,12 +74,12 @@ export const updateBitbucket = async ( .where(eq(bitbucket.bitbucketId, bitbucketId)) .returning(); - if (input.name || input.userId) { + if (input.name || input.organizationId) { await tx .update(gitProvider) .set({ name: input.name, - userId: input.userId, + organizationId: input.organizationId, }) .where(eq(gitProvider.gitProviderId, input.gitProviderId)) .returning(); diff --git a/packages/server/src/services/destination.ts b/packages/server/src/services/destination.ts index add0a3dff..e66f8695a 100644 --- a/packages/server/src/services/destination.ts +++ b/packages/server/src/services/destination.ts @@ -10,13 +10,13 @@ export type Destination = typeof destinations.$inferSelect; export const createDestintation = async ( input: typeof apiCreateDestination._type, - userId: string, + organizationId: string, ) => { const newDestination = await db .insert(destinations) .values({ ...input, - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); @@ -46,14 +46,14 @@ export const findDestinationById = async (destinationId: string) => { export const removeDestinationById = async ( destinationId: string, - userId: string, + organizationId: string, ) => { const result = await db .delete(destinations) .where( and( eq(destinations.destinationId, destinationId), - eq(destinations.userId, userId), + eq(destinations.organizationId, organizationId), ), ) .returning(); @@ -73,7 +73,7 @@ export const updateDestinationById = async ( .where( and( eq(destinations.destinationId, destinationId), - eq(destinations.userId, destinationData.userId || ""), + eq(destinations.organizationId, destinationData.organizationId || ""), ), ) .returning(); diff --git a/packages/server/src/services/github.ts b/packages/server/src/services/github.ts index b23edf207..deb5f3fa2 100644 --- a/packages/server/src/services/github.ts +++ b/packages/server/src/services/github.ts @@ -12,14 +12,14 @@ import { updatePreviewDeployment } from "./preview-deployment"; export type Github = typeof github.$inferSelect; export const createGithub = async ( input: typeof apiCreateGithub._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "github", - userId: userId, + organizationId: organizationId, name: input.name, }) .returning() diff --git a/packages/server/src/services/gitlab.ts b/packages/server/src/services/gitlab.ts index c581c96ce..0822aaaba 100644 --- a/packages/server/src/services/gitlab.ts +++ b/packages/server/src/services/gitlab.ts @@ -13,14 +13,14 @@ export type Gitlab = typeof gitlab.$inferSelect; export const createGitlab = async ( input: typeof apiCreateGitlab._type, - userId: string, + organizationId: string, ) => { return await db.transaction(async (tx) => { const newGitProvider = await tx .insert(gitProvider) .values({ providerType: "gitlab", - userId: userId, + organizationId: organizationId, name: input.name, }) .returning() diff --git a/packages/server/src/services/notification.ts b/packages/server/src/services/notification.ts index 03f6bd09d..fa355a565 100644 --- a/packages/server/src/services/notification.ts +++ b/packages/server/src/services/notification.ts @@ -24,7 +24,7 @@ export type Notification = typeof notifications.$inferSelect; export const createSlackNotification = async ( input: typeof apiCreateSlack._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newSlack = await tx @@ -54,7 +54,7 @@ export const createSlackNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "slack", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -84,7 +84,7 @@ export const updateSlackNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -114,7 +114,7 @@ export const updateSlackNotification = async ( export const createTelegramNotification = async ( input: typeof apiCreateTelegram._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newTelegram = await tx @@ -144,7 +144,7 @@ export const createTelegramNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "telegram", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -174,7 +174,7 @@ export const updateTelegramNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -204,7 +204,7 @@ export const updateTelegramNotification = async ( export const createDiscordNotification = async ( input: typeof apiCreateDiscord._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newDiscord = await tx @@ -234,7 +234,7 @@ export const createDiscordNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "discord", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -264,7 +264,7 @@ export const updateDiscordNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -294,7 +294,7 @@ export const updateDiscordNotification = async ( export const createEmailNotification = async ( input: typeof apiCreateEmail._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newEmail = await tx @@ -328,7 +328,7 @@ export const createEmailNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "email", - userId: userId, + organizationId: organizationId, serverThreshold: input.serverThreshold, }) .returning() @@ -358,7 +358,7 @@ export const updateEmailNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, serverThreshold: input.serverThreshold, }) .where(eq(notifications.notificationId, input.notificationId)) @@ -392,7 +392,7 @@ export const updateEmailNotification = async ( export const createGotifyNotification = async ( input: typeof apiCreateGotify._type, - userId: string, + organizationId: string, ) => { await db.transaction(async (tx) => { const newGotify = await tx @@ -424,7 +424,7 @@ export const createGotifyNotification = async ( dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, notificationType: "gotify", - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); @@ -453,7 +453,7 @@ export const updateGotifyNotification = async ( databaseBackup: input.databaseBackup, dokployRestart: input.dokployRestart, dockerCleanup: input.dockerCleanup, - userId: input.userId, + organizationId: input.organizationId, }) .where(eq(notifications.notificationId, input.notificationId)) .returning() diff --git a/packages/server/src/services/project.ts b/packages/server/src/services/project.ts index 8b80738fd..b740834b5 100644 --- a/packages/server/src/services/project.ts +++ b/packages/server/src/services/project.ts @@ -16,13 +16,13 @@ export type Project = typeof projects.$inferSelect; export const createProject = async ( input: typeof apiCreateProject._type, - userId: string, + organizationId: string, ) => { const newProject = await db .insert(projects) .values({ ...input, - userId: userId, + organizationId: organizationId, }) .returning() .then((value) => value[0]); diff --git a/packages/server/src/services/server.ts b/packages/server/src/services/server.ts index 7702c90d6..afe851ef5 100644 --- a/packages/server/src/services/server.ts +++ b/packages/server/src/services/server.ts @@ -1,5 +1,9 @@ import { db } from "@dokploy/server/db"; -import { type apiCreateServer, server } from "@dokploy/server/db/schema"; +import { + type apiCreateServer, + organization, + server, +} from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; @@ -7,13 +11,13 @@ export type Server = typeof server.$inferSelect; export const createServer = async ( input: typeof apiCreateServer._type, - userId: string, + organizationId: string, ) => { const newServer = await db .insert(server) .values({ ...input, - userId: userId, + organizationId: organizationId, createdAt: new Date().toISOString(), }) .returning() @@ -47,11 +51,15 @@ export const findServerById = async (serverId: string) => { }; export const findServersByUserId = async (userId: string) => { - const servers = await db.query.server.findMany({ - where: eq(server.userId, userId), - orderBy: desc(server.createdAt), + const orgs = await db.query.organization.findMany({ + where: eq(organization.ownerId, userId), + with: { + servers: true, + }, }); + const servers = orgs.flatMap((org) => org.servers); + return servers; }; From ed62b4e1a3cc575b70d5b842d33d7740f0e9bb2a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 23:01:44 -0600 Subject: [PATCH 051/126] refactor: lint --- .../dashboard/organization/handle-organization.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 4 ++-- apps/dokploy/server/api/root.ts | 2 +- apps/dokploy/server/api/routers/organization.ts | 8 ++++---- apps/dokploy/server/api/routers/user.ts | 8 ++++---- packages/server/src/db/schema/account.ts | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 00bf42b9f..24bb5c0eb 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -11,9 +11,9 @@ import { import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; +import { api } from "@/utils/api"; import { PenBoxIcon, Plus, SquarePen } from "lucide-react"; import { useEffect, useState } from "react"; -import { api } from "@/utils/api"; import { toast } from "sonner"; interface Props { diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 8e8a74dc3..cc27dc389 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -495,11 +495,11 @@ import { DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { AddOrganization } from "../dashboard/organization/handle-organization"; import { authClient } from "@/lib/auth"; +import { toast } from "sonner"; +import { AddOrganization } from "../dashboard/organization/handle-organization"; import { DialogAction } from "../shared/dialog-action"; import { Button } from "../ui/button"; -import { toast } from "sonner"; const data = { user: { name: "shadcn", diff --git a/apps/dokploy/server/api/root.ts b/apps/dokploy/server/api/root.ts index 7860b248f..5f5d0bcc8 100644 --- a/apps/dokploy/server/api/root.ts +++ b/apps/dokploy/server/api/root.ts @@ -19,6 +19,7 @@ import { mongoRouter } from "./routers/mongo"; import { mountRouter } from "./routers/mount"; import { mysqlRouter } from "./routers/mysql"; import { notificationRouter } from "./routers/notification"; +import { organizationRouter } from "./routers/organization"; import { portRouter } from "./routers/port"; import { postgresRouter } from "./routers/postgres"; import { previewDeploymentRouter } from "./routers/preview-deployment"; @@ -33,7 +34,6 @@ import { sshRouter } from "./routers/ssh-key"; import { stripeRouter } from "./routers/stripe"; import { swarmRouter } from "./routers/swarm"; import { userRouter } from "./routers/user"; -import { organizationRouter } from "./routers/organization"; /** * This is the primary router for your server. * diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index dd933c9d4..db0c64385 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -1,10 +1,10 @@ -import { adminProcedure, createTRPCRouter } from "../trpc"; -import { z } from "zod"; import { db } from "@/server/db"; import { member, organization } from "@/server/db/schema"; -import { nanoid } from "nanoid"; -import { desc, eq } from "drizzle-orm"; import { TRPCError } from "@trpc/server"; +import { desc, eq } from "drizzle-orm"; +import { nanoid } from "nanoid"; +import { z } from "zod"; +import { adminProcedure, createTRPCRouter } from "../trpc"; export const organizationRouter = createTRPCRouter({ create: adminProcedure .input( diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 129b0731d..ccf9e5bc1 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -1,10 +1,10 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server"; -import { TRPCError } from "@trpc/server"; -import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; -import { eq } from "drizzle-orm"; -import { member } from "@dokploy/server/db/schema"; import { db } from "@dokploy/server/db"; +import { member } from "@dokploy/server/db/schema"; +import { TRPCError } from "@trpc/server"; +import { eq } from "drizzle-orm"; +import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; export const userRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { return await db.query.member.findMany({ diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index da765e8f2..3bb7dcfca 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,9 +1,9 @@ import { relations } from "drizzle-orm"; import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; import { nanoid } from "nanoid"; -import { users_temp } from "./user"; -import { server } from "./server"; import { projects } from "./project"; +import { server } from "./server"; +import { users_temp } from "./user"; export const account = pgTable("account", { id: text("id") From 4a1a14aeb49a022a7823ab36899317131269266b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 15 Feb 2025 23:24:45 -0600 Subject: [PATCH 052/126] refactor: update --- apps/dokploy/pages/dashboard/docker.tsx | 18 ++++----- apps/dokploy/pages/dashboard/monitoring.tsx | 4 +- .../pages/dashboard/project/[projectId].tsx | 4 +- .../services/application/[applicationId].tsx | 5 ++- .../services/compose/[composeId].tsx | 4 +- .../services/mariadb/[mariadbId].tsx | 4 +- .../[projectId]/services/mongo/[mongoId].tsx | 4 +- .../[projectId]/services/mysql/[mysqlId].tsx | 4 +- .../services/postgres/[postgresId].tsx | 4 +- .../[projectId]/services/redis/[redisId].tsx | 4 +- apps/dokploy/pages/dashboard/projects.tsx | 8 ++-- apps/dokploy/pages/dashboard/requests.tsx | 5 ++- .../pages/dashboard/settings/billing.tsx | 9 +++-- apps/dokploy/pages/dashboard/swarm.tsx | 18 ++++----- apps/dokploy/pages/dashboard/traefik.tsx | 18 ++++----- apps/dokploy/server/api/routers/compose.ts | 37 +++++++++++-------- .../server/api/routers/notification.ts | 33 ++++++++--------- apps/dokploy/server/api/routers/project.ts | 5 ++- apps/dokploy/server/api/routers/ssh-key.ts | 22 ++--------- apps/dokploy/server/api/routers/user.ts | 23 +++++++----- packages/server/src/db/schema/notification.ts | 2 +- .../utils/notifications/server-threshold.ts | 4 +- 22 files changed, 119 insertions(+), 120 deletions(-) diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index 051e4f0a9..202935aa6 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -1,7 +1,8 @@ import { ShowContainers } from "@/components/dashboard/docker/show/show-containers"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; -import { IS_CLOUD, validateRequest } from "@dokploy/server"; +import { IS_CLOUD } from "@dokploy/server/constants"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; @@ -27,7 +28,7 @@ export async function getServerSideProps( }, }; } - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -44,21 +45,20 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); try { await helpers.project.all.prefetch(); - const auth = await helpers.auth.get.fetch(); - if (auth.role === "member") { - const user = await helpers.user.byAuthId.fetch({ - authId: auth.id, + if (user.role === "member") { + const userR = await helpers.user.get.fetch({ + userId: user.id, }); - if (!user.canAccessToDocker) { + if (!userR.canAccessToDocker) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/monitoring.tsx b/apps/dokploy/pages/dashboard/monitoring.tsx index a9f712f30..a30706fc8 100644 --- a/apps/dokploy/pages/dashboard/monitoring.tsx +++ b/apps/dokploy/pages/dashboard/monitoring.tsx @@ -8,7 +8,7 @@ import { Switch } from "@/components/ui/switch"; import { useLocalStorage } from "@/hooks/useLocalStorage"; import { api } from "@/utils/api"; import { IS_CLOUD } from "@dokploy/server/constants"; -import { validateRequest } from "@dokploy/server/index"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { Loader2 } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import type React from "react"; @@ -104,7 +104,7 @@ export async function getServerSideProps( }, }; } - const { user } = await validateRequest(ctx.req, ctx.res); + const { user } = await validateRequest(ctx.req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index 87fa3df4f..b5d6b18f8 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -49,7 +49,7 @@ import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import type { findProjectById } from "@dokploy/server"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { Ban, @@ -658,7 +658,7 @@ export async function getServerSideProps( const { params } = ctx; const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 441720b94..a394f320f 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -39,7 +39,8 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; +// import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import copy from "copy-to-clipboard"; import { GlobeIcon, HelpCircle, ServerOff, Trash2 } from "lucide-react"; @@ -370,7 +371,7 @@ export async function getServerSideProps( const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index 41a346b4b..8bd3e8638 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -33,7 +33,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import copy from "copy-to-clipboard"; import { CircuitBoard, ServerOff } from "lucide-react"; @@ -366,7 +366,7 @@ export async function getServerSideProps( const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 7138be021..9adbad15c 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -35,7 +35,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; import type { @@ -316,7 +316,7 @@ export async function getServerSideProps( const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index c0ddd96d5..a5ba6b5de 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -35,7 +35,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; import type { @@ -318,7 +318,7 @@ export async function getServerSideProps( const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index d170bb76e..3e17c4fac 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -35,7 +35,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; import type { @@ -323,7 +323,7 @@ export async function getServerSideProps( const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 67c6a4a5e..0b1d2aee6 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -35,7 +35,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; import type { @@ -319,7 +319,7 @@ export async function getServerSideProps( ) { const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index bb9ebc4ae..853e6688b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -34,7 +34,7 @@ import { import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { HelpCircle, ServerOff } from "lucide-react"; import type { @@ -311,7 +311,7 @@ export async function getServerSideProps( const { query, params, req, res } = ctx; const activeTab = query.tab; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/projects.tsx b/apps/dokploy/pages/dashboard/projects.tsx index af4d0f1ec..abeee6695 100644 --- a/apps/dokploy/pages/dashboard/projects.tsx +++ b/apps/dokploy/pages/dashboard/projects.tsx @@ -2,7 +2,7 @@ import { ShowProjects } from "@/components/dashboard/projects/show"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; -import { validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import dynamic from "next/dynamic"; @@ -38,7 +38,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); const helpers = createServerSideHelpers({ router: appRouter, @@ -46,8 +46,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/requests.tsx b/apps/dokploy/pages/dashboard/requests.tsx index d1a927e34..580b936a2 100644 --- a/apps/dokploy/pages/dashboard/requests.tsx +++ b/apps/dokploy/pages/dashboard/requests.tsx @@ -1,6 +1,7 @@ import { ShowRequests } from "@/components/dashboard/requests/show-requests"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; -import { IS_CLOUD, validateRequest } from "@dokploy/server"; +import { IS_CLOUD } from "@dokploy/server/constants"; +import { validateRequest } from "@dokploy/server/lib/auth"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; import * as React from "react"; @@ -22,7 +23,7 @@ export async function getServerSideProps( }, }; } - const { user } = await validateRequest(ctx.req, ctx.res); + const { user } = await validateRequest(ctx.req); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/settings/billing.tsx b/apps/dokploy/pages/dashboard/settings/billing.tsx index b1a7cb21d..3acce0b01 100644 --- a/apps/dokploy/pages/dashboard/settings/billing.tsx +++ b/apps/dokploy/pages/dashboard/settings/billing.tsx @@ -2,7 +2,8 @@ import { ShowBilling } from "@/components/dashboard/settings/billing/show-billin import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; -import { IS_CLOUD, validateRequest } from "@dokploy/server"; +import { IS_CLOUD } from "@dokploy/server/constants"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; @@ -29,7 +30,7 @@ export async function getServerSideProps( }; } const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user || user.role === "member") { return { redirect: { @@ -45,8 +46,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index f57728209..8278ed181 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -1,7 +1,8 @@ import SwarmMonitorCard from "@/components/dashboard/swarm/monitoring-card"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; -import { IS_CLOUD, validateRequest } from "@dokploy/server"; +import { IS_CLOUD } from "@dokploy/server/constants"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; @@ -27,7 +28,7 @@ export async function getServerSideProps( }, }; } - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -44,21 +45,20 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); try { await helpers.project.all.prefetch(); - const auth = await helpers.auth.get.fetch(); - if (auth.role === "member") { - const user = await helpers.user.byAuthId.fetch({ - authId: auth.id, + if (user.role === "member") { + const userR = await helpers.user.get.fetch({ + userId: user.id, }); - if (!user.canAccessToDocker) { + if (!userR.canAccessToDocker) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 1f710c9eb..6939eabdf 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -1,7 +1,8 @@ import { ShowTraefikSystem } from "@/components/dashboard/file-system/show-traefik-system"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; -import { IS_CLOUD, validateRequest } from "@dokploy/server"; +import { IS_CLOUD } from "@dokploy/server/constants"; +import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import React, { type ReactElement } from "react"; @@ -27,7 +28,7 @@ export async function getServerSideProps( }, }; } - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -44,21 +45,20 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); try { await helpers.project.all.prefetch(); - const auth = await helpers.auth.get.fetch(); - if (auth.role === "member") { - const user = await helpers.user.byAuthId.fetch({ - authId: auth.id, + if (user.role === "member") { + const userR = await helpers.user.get.fetch({ + userId: user.id, }); - if (!user.canAccessToTraefikFiles) { + if (!userR.canAccessToTraefikFiles) { return { redirect: { permanent: true, diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 69586eb42..a74c2bfbb 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -72,7 +72,7 @@ export const composeRouter = createTRPCRouter({ }); } const project = await findProjectById(input.projectId); - if (project.userId !== ctx.user.ownerId) { + if (project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this project", @@ -98,7 +98,7 @@ export const composeRouter = createTRPCRouter({ } const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this compose", @@ -111,7 +111,7 @@ export const composeRouter = createTRPCRouter({ .input(apiUpdateCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this compose", @@ -127,7 +127,10 @@ export const composeRouter = createTRPCRouter({ } const composeResult = await findComposeById(input.composeId); - if (composeResult.project.userId !== ctx.user.ownerId) { + if ( + composeResult.project.organizationId !== + ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this compose", @@ -158,7 +161,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to clean this compose", @@ -171,7 +174,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFetchServices) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to load this compose", @@ -185,7 +188,9 @@ export const composeRouter = createTRPCRouter({ try { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if ( + compose.project.organizationId !== ctx.session.activeOrganizationId + ) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to fetch this compose", @@ -210,7 +215,7 @@ export const composeRouter = createTRPCRouter({ .input(apiRandomizeCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to randomize this compose", @@ -222,7 +227,7 @@ export const composeRouter = createTRPCRouter({ .input(apiRandomizeCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to randomize this compose", @@ -237,7 +242,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to get this compose", @@ -255,7 +260,7 @@ export const composeRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to deploy this compose", @@ -288,7 +293,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to redeploy this compose", @@ -320,7 +325,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this compose", @@ -334,7 +339,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to stop this compose", @@ -349,7 +354,7 @@ export const composeRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to get this compose", @@ -362,7 +367,7 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .mutation(async ({ input, ctx }) => { const compose = await findComposeById(input.composeId); - if (compose.project.userId !== ctx.user.ownerId) { + if (compose.project.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to refresh this compose", diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 451700065..bba6c7db9 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -57,7 +57,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateSlack) .mutation(async ({ input, ctx }) => { try { - return await createSlackNotification(input, ctx.user.ownerId); + return await createSlackNotification( + input, + ctx.session.activeOrganizationId, + ); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -80,7 +83,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateSlackNotification({ ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw error; @@ -131,7 +134,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateTelegramNotification({ ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw new TRPCError({ @@ -183,7 +186,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateDiscordNotification({ ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw new TRPCError({ @@ -243,7 +246,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateEmailNotification({ ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw new TRPCError({ @@ -314,13 +317,7 @@ export const notificationRouter = createTRPCRouter({ gotify: true, }, orderBy: desc(notifications.createdAt), - ...(IS_CLOUD && { - where: eq( - notifications.organizationId, - ctx.session.activeOrganizationId, - ), - }), - // TODO: Remove this line when the cloud version is ready + where: eq(notifications.organizationId, ctx.session.activeOrganizationId), }); }), receiveNotification: publicProcedure @@ -337,7 +334,7 @@ export const notificationRouter = createTRPCRouter({ ) .mutation(async ({ input }) => { try { - let userId = ""; + let organizationId = ""; let ServerName = ""; if (input.ServerType === "Dokploy") { const result = await db @@ -354,7 +351,7 @@ export const notificationRouter = createTRPCRouter({ }); } - userId = result?.[0]?.id; + organizationId = result?.[0]?.id; ServerName = "Dokploy"; } else { const result = await db @@ -364,18 +361,18 @@ export const notificationRouter = createTRPCRouter({ sql`${server.metricsConfig}::jsonb -> 'server' ->> 'token' = ${input.Token}`, ); - if (!result?.[0]?.userId) { + if (!result?.[0]?.organizationId) { throw new TRPCError({ code: "BAD_REQUEST", message: "Token not found", }); } - userId = result?.[0]?.userId; + organizationId = result?.[0]?.organizationId; ServerName = "Remote"; } - await sendServerThresholdNotifications(userId, { + await sendServerThresholdNotifications(organizationId, { ...input, ServerName, }); @@ -416,7 +413,7 @@ export const notificationRouter = createTRPCRouter({ } return await updateGotifyNotification({ ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); } catch (error) { throw error; diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 88bb21d76..b5beadf4d 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -49,7 +49,10 @@ export const projectRouter = createTRPCRouter({ }); } - const project = await createProject(input, ctx.user.ownerId); + const project = await createProject( + input, + ctx.session.activeOrganizationId, + ); if (ctx.user.rol === "member") { await addNewProject(ctx.user.id, project.projectId); } diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index 90f7cd6e4..d55a23794 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -41,10 +41,7 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const sshKey = await findSSHKeyById(input.sshKeyId); - if ( - IS_CLOUD && - sshKey.organizationId !== ctx.session.activeOrganizationId - ) { + if (sshKey.organizationId !== ctx.session.activeOrganizationId) { // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", @@ -62,11 +59,7 @@ export const sshRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const sshKey = await findSSHKeyById(input.sshKeyId); - if ( - IS_CLOUD && - sshKey.organizationId !== ctx.session.activeOrganizationId - ) { - // TODO: Remove isCloud in the next versions of dokploy + if (sshKey.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this SSH key", @@ -76,12 +69,9 @@ export const sshRouter = createTRPCRouter({ }), all: protectedProcedure.query(async ({ ctx }) => { return await db.query.sshKeys.findMany({ - ...(IS_CLOUD && { - where: eq(sshKeys.organizationId, ctx.session.activeOrganizationId), - }), + where: eq(sshKeys.organizationId, ctx.session.activeOrganizationId), orderBy: desc(sshKeys.createdAt), }); - // TODO: Remove this line when the cloud version is ready }), generate: protectedProcedure .input(apiGenerateSSHKey) @@ -93,11 +83,7 @@ export const sshRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { const sshKey = await findSSHKeyById(input.sshKeyId); - if ( - IS_CLOUD && - sshKey.organizationId !== ctx.session.activeOrganizationId - ) { - // TODO: Remove isCloud in the next versions of dokploy + if (sshKey.organizationId !== ctx.session.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to update this SSH key", diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index ccf9e5bc1..269da8f5a 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -5,6 +5,7 @@ import { member } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; +import { z } from "zod"; export const userRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { return await db.query.member.findMany({ @@ -14,16 +15,20 @@ export const userRouter = createTRPCRouter({ }, }); }), - byAuthId: protectedProcedure - .input(apiFindOneUserByAuth) + get: protectedProcedure + .input( + z.object({ + userId: z.string(), + }), + ) .query(async ({ input, ctx }) => { - const user = await findUserByAuthId(input.authId); - if (user.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to access this user", - }); - } + const user = await findUserById(input.userId); + // if (user.adminId !== ctx.user.adminId) { + // throw new TRPCError({ + // code: "UNAUTHORIZED", + // message: "You are not allowed to access this user", + // }); + // } return user; }), byUserId: protectedProcedure diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 4e2917ca9..0179f3c7c 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -270,7 +270,7 @@ export const apiCreateGotify = notificationsSchema export const apiUpdateGotify = apiCreateGotify.partial().extend({ notificationId: z.string().min(1), gotifyId: z.string().min(1), - userId: z.string().optional(), + organizationId: z.string().optional(), }); export const apiTestGotifyConnection = apiCreateGotify diff --git a/packages/server/src/utils/notifications/server-threshold.ts b/packages/server/src/utils/notifications/server-threshold.ts index c606f0ef3..2e63ba25a 100644 --- a/packages/server/src/utils/notifications/server-threshold.ts +++ b/packages/server/src/utils/notifications/server-threshold.ts @@ -18,7 +18,7 @@ interface ServerThresholdPayload { } export const sendServerThresholdNotifications = async ( - userId: string, + organizationId: string, payload: ServerThresholdPayload, ) => { const date = new Date(payload.Timestamp); @@ -27,7 +27,7 @@ export const sendServerThresholdNotifications = async ( const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.serverThreshold, true), - eq(notifications.userId, userId), + eq(notifications.organizationId, organizationId), ), with: { email: true, From e7db0ccb70fb3acb985998cff340c910527ea9c6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 02:57:49 -0600 Subject: [PATCH 053/126] refactor: update invitation --- .../components/dashboard/search-command.tsx | 2 +- .../git/github/add-github-provider.tsx | 8 +- .../dashboard/settings/users/add-user.tsx | 28 ++-- apps/dokploy/lib/auth.ts | 2 +- .../accept-invitation/[accept-invitation].tsx | 30 ++++ .../pages/api/providers/github/setup.ts | 17 +- apps/dokploy/server/api/routers/bitbucket.ts | 36 ++--- apps/dokploy/server/api/routers/github.ts | 24 +-- apps/dokploy/server/api/routers/gitlab.ts | 19 +-- apps/dokploy/server/api/routers/project.ts | 2 +- apps/dokploy/server/api/routers/user.ts | 2 +- apps/dokploy/server/server.ts | 16 +- .../server/wss/docker-container-logs.ts | 4 +- .../server/wss/docker-container-terminal.ts | 4 +- apps/dokploy/server/wss/docker-stats.ts | 4 +- apps/dokploy/server/wss/drawer-logs.ts | 7 + apps/dokploy/server/wss/listen-deployment.ts | 4 +- apps/dokploy/server/wss/terminal.ts | 8 +- packages/server/src/auth/auth.ts | 78 ---------- packages/server/src/auth/token.ts | 146 +++++++++--------- packages/server/src/lib/auth.ts | 17 +- packages/server/src/services/application.ts | 56 +++---- packages/server/src/services/compose.ts | 40 ++--- .../server/src/services/preview-deployment.ts | 6 +- packages/server/src/services/settings.ts | 2 - packages/server/src/services/user.ts | 22 +-- .../src/utils/notifications/build-error.ts | 6 +- .../src/utils/notifications/build-success.ts | 6 +- .../src/utils/notifications/docker-cleanup.ts | 4 +- 29 files changed, 270 insertions(+), 330 deletions(-) create mode 100644 apps/dokploy/pages/accept-invitation/[accept-invitation].tsx diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 952bbe5c8..8158a9ca8 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -36,7 +36,7 @@ export const SearchCommand = () => { const router = useRouter(); const [open, setOpen] = React.useState(false); const [search, setSearch] = React.useState(""); - const { data: session } = authClient.getSession(); + const { data: session } = authClient.useSession(); const { data } = api.project.all.useQuery(undefined, { enabled: !!session, }); diff --git a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx index 0e3e56336..5f2cb934d 100644 --- a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx @@ -10,12 +10,14 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; +import { authClient } from "@/lib/auth"; import { api } from "@/utils/api"; import { format } from "date-fns"; import { useEffect, useState } from "react"; export const AddGithubProvider = () => { const [isOpen, setIsOpen] = useState(false); + const { data: activeOrganization } = authClient.useActiveOrganization(); const { data } = api.auth.get.useQuery(); const [manifest, setManifest] = useState(""); const [isOrganization, setIsOrganization] = useState(false); @@ -25,7 +27,7 @@ export const AddGithubProvider = () => { const url = document.location.origin; const manifest = JSON.stringify( { - redirect_url: `${origin}/api/providers/github/setup?authId=${data?.id}`, + redirect_url: `${origin}/api/providers/github/setup?organizationId=${activeOrganization?.id}`, name: `Dokploy-${format(new Date(), "yyyy-MM-dd")}`, url: origin, hook_attributes: { @@ -93,8 +95,8 @@ export const AddGithubProvider = () => {
diff --git a/apps/dokploy/components/dashboard/settings/users/add-user.tsx b/apps/dokploy/components/dashboard/settings/users/add-user.tsx index 8fb6de27c..175f723f5 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-user.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-user.tsx @@ -19,6 +19,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { authClient } from "@/lib/auth"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon } from "lucide-react"; @@ -40,6 +41,7 @@ export const AddUser = () => { const [open, setOpen] = useState(false); const utils = api.useUtils(); + const { data: activeOrganization } = authClient.useActiveOrganization(); const { mutateAsync, isError, error, isLoading } = api.admin.createUserInvitation.useMutation(); @@ -54,17 +56,23 @@ export const AddUser = () => { }, [form, form.formState.isSubmitSuccessful, form.reset]); const onSubmit = async (data: AddUser) => { - await mutateAsync({ + const result = await authClient.organization.inviteMember({ email: data.email.toLowerCase(), - }) - .then(async () => { - toast.success("Invitation created"); - await utils.user.all.invalidate(); - setOpen(false); - }) - .catch(() => { - toast.error("Error creating the invitation"); - }); + role: "user", + organizationId: activeOrganization?.id, + }); + console.log(result); + // await mutateAsync({ + // email: data.email.toLowerCase(), + // }) + // .then(async () => { + // toast.success("Invitation created"); + // await utils.user.all.invalidate(); + // setOpen(false); + // }) + // .catch(() => { + // toast.error("Error creating the invitation"); + // }); }; return ( diff --git a/apps/dokploy/lib/auth.ts b/apps/dokploy/lib/auth.ts index a5b193700..12c3cc3e7 100644 --- a/apps/dokploy/lib/auth.ts +++ b/apps/dokploy/lib/auth.ts @@ -2,6 +2,6 @@ import { organizationClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient({ - baseURL: "http://localhost:3000", // the base url of your auth server + // baseURL: "http://localhost:3000", // the base url of your auth server plugins: [organizationClient()], }); diff --git a/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx b/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx new file mode 100644 index 000000000..6936a802d --- /dev/null +++ b/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx @@ -0,0 +1,30 @@ +import { Button } from "@/components/ui/button"; +import { authClient } from "@/lib/auth"; +import { useRouter } from "next/router"; + +export const AcceptInvitation = () => { + const { query } = useRouter(); + + const invitationId = query["accept-invitation"]; + + // const { data: organization } = api.organization.getById.useQuery({ + // id: id as string + // }) + + return ( +
+ +
+ ); +}; + +export default AcceptInvitation; diff --git a/apps/dokploy/pages/api/providers/github/setup.ts b/apps/dokploy/pages/api/providers/github/setup.ts index bd123c687..9a03ed3d7 100644 --- a/apps/dokploy/pages/api/providers/github/setup.ts +++ b/apps/dokploy/pages/api/providers/github/setup.ts @@ -1,10 +1,12 @@ import { db } from "@/server/db"; import { github } from "@/server/db/schema"; import { + auth, createGithub, findAdminByAuthId, findAuthById, findUserByAuthId, + findUserById, } from "@dokploy/server"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; @@ -28,7 +30,7 @@ export default async function handler( return res.status(400).json({ error: "Missing code parameter" }); } const [action, value] = state?.split(":"); - // Value could be the authId or the githubProviderId + // Value could be the organizationId or the githubProviderId if (action === "gh_init") { const octokit = new Octokit({}); @@ -39,17 +41,6 @@ export default async function handler( }, ); - const auth = await findAuthById(value as string); - - let adminId = ""; - if (auth.role === "owner") { - const admin = await findAdminByAuthId(auth.id); - adminId = admin.adminId; - } else { - const user = await findUserByAuthId(auth.id); - adminId = user.adminId; - } - await createGithub( { name: data.name, @@ -60,7 +51,7 @@ export default async function handler( githubWebhookSecret: data.webhook_secret, githubPrivateKey: data.pem, }, - adminId, + value as string, ); } else if (action === "gh_setup") { await db diff --git a/apps/dokploy/server/api/routers/bitbucket.ts b/apps/dokploy/server/api/routers/bitbucket.ts index 987555e5f..7a8462641 100644 --- a/apps/dokploy/server/api/routers/bitbucket.ts +++ b/apps/dokploy/server/api/routers/bitbucket.ts @@ -8,7 +8,6 @@ import { apiUpdateBitbucket, } from "@/server/db/schema"; import { - IS_CLOUD, createBitbucket, findBitbucketById, getBitbucketBranches, @@ -37,11 +36,9 @@ export const bitbucketRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( - IS_CLOUD && bitbucketProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this bitbucket provider", @@ -59,14 +56,11 @@ export const bitbucketRouter = createTRPCRouter({ }, }); - if (IS_CLOUD) { - // TODO: mAyBe a rEfaCtoR 🤫 - result = result.filter( - (provider) => - provider.gitProvider.organizationId === - ctx.session.activeOrganizationId, - ); - } + result = result.filter( + (provider) => + provider.gitProvider.organizationId === + ctx.session.activeOrganizationId, + ); return result; }), @@ -75,11 +69,9 @@ export const bitbucketRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( - IS_CLOUD && bitbucketProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this bitbucket provider", @@ -94,11 +86,9 @@ export const bitbucketRouter = createTRPCRouter({ input.bitbucketId || "", ); if ( - IS_CLOUD && bitbucketProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this bitbucket provider", @@ -112,11 +102,9 @@ export const bitbucketRouter = createTRPCRouter({ try { const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( - IS_CLOUD && bitbucketProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this bitbucket provider", @@ -137,11 +125,9 @@ export const bitbucketRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const bitbucketProvider = await findBitbucketById(input.bitbucketId); if ( - IS_CLOUD && bitbucketProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this bitbucket provider", @@ -149,7 +135,7 @@ export const bitbucketRouter = createTRPCRouter({ } return await updateBitbucket(input.bitbucketId, { ...input, - userId: ctx.user.ownerId, + organizationId: ctx.session.activeOrganizationId, }); }), }); diff --git a/apps/dokploy/server/api/routers/github.ts b/apps/dokploy/server/api/routers/github.ts index 2b3f15029..691030e27 100644 --- a/apps/dokploy/server/api/routers/github.ts +++ b/apps/dokploy/server/api/routers/github.ts @@ -6,7 +6,6 @@ import { apiUpdateGithub, } from "@/server/db/schema"; import { - IS_CLOUD, findGithubById, getGithubBranches, getGithubRepositories, @@ -24,7 +23,6 @@ export const githubRouter = createTRPCRouter({ githubProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this github provider", @@ -40,7 +38,6 @@ export const githubRouter = createTRPCRouter({ githubProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this github provider", @@ -71,14 +68,11 @@ export const githubRouter = createTRPCRouter({ }, }); - if (IS_CLOUD) { - // TODO: mAyBe a rEfaCtoR 🤫 - result = result.filter( - (provider) => - provider.gitProvider.organizationId === - ctx.session.activeOrganizationId, - ); - } + result = result.filter( + (provider) => + provider.gitProvider.organizationId === + ctx.session.activeOrganizationId, + ); const filtered = result .filter((provider) => haveGithubRequirements(provider)) @@ -100,11 +94,9 @@ export const githubRouter = createTRPCRouter({ try { const githubProvider = await findGithubById(input.githubId); if ( - IS_CLOUD && githubProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this github provider", @@ -124,11 +116,9 @@ export const githubRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { const githubProvider = await findGithubById(input.githubId); if ( - IS_CLOUD && githubProvider.gitProvider.organizationId !== - ctx.session.activeOrganizationId + ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this github provider", diff --git a/apps/dokploy/server/api/routers/gitlab.ts b/apps/dokploy/server/api/routers/gitlab.ts index 338b8c64d..b702bc2a4 100644 --- a/apps/dokploy/server/api/routers/gitlab.ts +++ b/apps/dokploy/server/api/routers/gitlab.ts @@ -9,7 +9,6 @@ import { import { db } from "@/server/db"; import { - IS_CLOUD, createGitlab, findGitlabById, getGitlabBranches, @@ -43,7 +42,6 @@ export const gitlabRouter = createTRPCRouter({ gitlabProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this Gitlab provider", @@ -58,14 +56,11 @@ export const gitlabRouter = createTRPCRouter({ }, }); - if (IS_CLOUD) { - // TODO: mAyBe a rEfaCtoR 🤫 - result = result.filter( - (provider) => - provider.gitProvider.organizationId === - ctx.session.activeOrganizationId, - ); - } + result = result.filter( + (provider) => + provider.gitProvider.organizationId === + ctx.session.activeOrganizationId, + ); const filtered = result .filter((provider) => haveGitlabRequirements(provider)) .map((provider) => { @@ -87,7 +82,6 @@ export const gitlabRouter = createTRPCRouter({ gitlabProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this Gitlab provider", @@ -104,7 +98,6 @@ export const gitlabRouter = createTRPCRouter({ gitlabProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this Gitlab provider", @@ -121,7 +114,6 @@ export const gitlabRouter = createTRPCRouter({ gitlabProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this Gitlab provider", @@ -145,7 +137,6 @@ export const gitlabRouter = createTRPCRouter({ gitlabProvider.gitProvider.organizationId !== ctx.session.activeOrganizationId ) { - //TODO: Remove this line when the cloud version is ready throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to access this Gitlab provider", diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index b5beadf4d..5fc79f437 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -71,7 +71,7 @@ export const projectRouter = createTRPCRouter({ .input(apiFindOneProject) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - const { accessedServices } = await findUserByAuthId(ctx.user.id); + const { accessedServices } = await findUserById(ctx.user.id); await checkProjectAccess(ctx.user.id, "access", input.projectId); diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 269da8f5a..00c2bb82e 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -4,8 +4,8 @@ import { db } from "@dokploy/server/db"; import { member } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; import { z } from "zod"; +import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; export const userRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { return await db.query.member.findMany({ diff --git a/apps/dokploy/server/server.ts b/apps/dokploy/server/server.ts index a277dc0ac..c8f53f6f9 100644 --- a/apps/dokploy/server/server.ts +++ b/apps/dokploy/server/server.ts @@ -34,14 +34,14 @@ void app.prepare().then(async () => { }); // WEBSOCKET - // setupDrawerLogsWebSocketServer(server); - // setupDeploymentLogsWebSocketServer(server); - // setupDockerContainerLogsWebSocketServer(server); - // setupDockerContainerTerminalWebSocketServer(server); - // setupTerminalWebSocketServer(server); - // if (!IS_CLOUD) { - // setupDockerStatsMonitoringSocketServer(server); - // } + setupDrawerLogsWebSocketServer(server); + setupDeploymentLogsWebSocketServer(server); + setupDockerContainerLogsWebSocketServer(server); + setupDockerContainerTerminalWebSocketServer(server); + setupTerminalWebSocketServer(server); + if (!IS_CLOUD) { + setupDockerStatsMonitoringSocketServer(server); + } if (process.env.NODE_ENV === "production" && !IS_CLOUD) { setupDirectories(); diff --git a/apps/dokploy/server/wss/docker-container-logs.ts b/apps/dokploy/server/wss/docker-container-logs.ts index 092f39735..8d08ebd46 100644 --- a/apps/dokploy/server/wss/docker-container-logs.ts +++ b/apps/dokploy/server/wss/docker-container-logs.ts @@ -1,5 +1,5 @@ import type http from "node:http"; -import { findServerById, validateWebSocketRequest } from "@dokploy/server"; +import { findServerById, validateRequest } from "@dokploy/server"; import { spawn } from "node-pty"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; @@ -35,7 +35,7 @@ export const setupDockerContainerLogsWebSocketServer = ( const since = url.searchParams.get("since"); const serverId = url.searchParams.get("serverId"); const runType = url.searchParams.get("runType"); - const { user, session } = await validateWebSocketRequest(req); + const { user, session } = await validateRequest(req); if (!containerId) { ws.close(4000, "containerId no provided"); diff --git a/apps/dokploy/server/wss/docker-container-terminal.ts b/apps/dokploy/server/wss/docker-container-terminal.ts index 8981ccbc2..04ef5c96c 100644 --- a/apps/dokploy/server/wss/docker-container-terminal.ts +++ b/apps/dokploy/server/wss/docker-container-terminal.ts @@ -1,5 +1,5 @@ import type http from "node:http"; -import { findServerById, validateWebSocketRequest } from "@dokploy/server"; +import { findServerById, validateRequest } from "@dokploy/server"; import { spawn } from "node-pty"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; @@ -32,7 +32,7 @@ export const setupDockerContainerTerminalWebSocketServer = ( const containerId = url.searchParams.get("containerId"); const activeWay = url.searchParams.get("activeWay"); const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); + const { user, session } = await validateRequest(req); if (!containerId) { ws.close(4000, "containerId no provided"); diff --git a/apps/dokploy/server/wss/docker-stats.ts b/apps/dokploy/server/wss/docker-stats.ts index b1e4585c1..99e993dce 100644 --- a/apps/dokploy/server/wss/docker-stats.ts +++ b/apps/dokploy/server/wss/docker-stats.ts @@ -4,7 +4,7 @@ import { execAsync, getLastAdvancedStatsFile, recordAdvancedStats, - validateWebSocketRequest, + validateRequest, } from "@dokploy/server"; import { WebSocketServer } from "ws"; @@ -36,7 +36,7 @@ export const setupDockerStatsMonitoringSocketServer = ( | "application" | "stack" | "docker-compose"; - const { user, session } = await validateWebSocketRequest(req); + const { user, session } = await validateRequest(req); if (!appName) { ws.close(4000, "appName no provided"); diff --git a/apps/dokploy/server/wss/drawer-logs.ts b/apps/dokploy/server/wss/drawer-logs.ts index c1dec315b..da2c529ac 100644 --- a/apps/dokploy/server/wss/drawer-logs.ts +++ b/apps/dokploy/server/wss/drawer-logs.ts @@ -3,6 +3,7 @@ import { applyWSSHandler } from "@trpc/server/adapters/ws"; import { WebSocketServer } from "ws"; import { appRouter } from "../api/root"; import { createTRPCContext } from "../api/trpc"; +import { validateRequest } from "@dokploy/server/index"; export const setupDrawerLogsWebSocketServer = ( server: http.Server, @@ -32,5 +33,11 @@ export const setupDrawerLogsWebSocketServer = ( wssTerm.on("connection", async (ws, req) => { const url = new URL(req.url || "", `http://${req.headers.host}`); + const { user, session } = await validateRequest(req); + + if (!user || !session) { + ws.close(); + return; + } }); }; diff --git a/apps/dokploy/server/wss/listen-deployment.ts b/apps/dokploy/server/wss/listen-deployment.ts index df77ceb41..ee470fcbf 100644 --- a/apps/dokploy/server/wss/listen-deployment.ts +++ b/apps/dokploy/server/wss/listen-deployment.ts @@ -1,6 +1,6 @@ import { spawn } from "node:child_process"; import type http from "node:http"; -import { findServerById, validateWebSocketRequest } from "@dokploy/server"; +import { findServerById, validateRequest } from "@dokploy/server"; import { Client } from "ssh2"; import { WebSocketServer } from "ws"; @@ -29,7 +29,7 @@ export const setupDeploymentLogsWebSocketServer = ( const url = new URL(req.url || "", `http://${req.headers.host}`); const logPath = url.searchParams.get("logPath"); const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); + const { user, session } = await validateRequest(req); if (!logPath) { console.log("logPath no provided"); diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index 5fa1accc2..6e9c3614f 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -1,9 +1,5 @@ import type http from "node:http"; -import { - IS_CLOUD, - findServerById, - validateWebSocketRequest, -} from "@dokploy/server"; +import { IS_CLOUD, findServerById, validateRequest } from "@dokploy/server"; import { publicIpv4, publicIpv6 } from "public-ip"; import { Client, type ConnectConfig } from "ssh2"; import { WebSocketServer } from "ws"; @@ -71,7 +67,7 @@ export const setupTerminalWebSocketServer = ( wssTerm.on("connection", async (ws, req) => { const url = new URL(req.url || "", `http://${req.headers.host}`); const serverId = url.searchParams.get("serverId"); - const { user, session } = await validateWebSocketRequest(req); + const { user, session } = await validateRequest(req); if (!user || !session || !serverId) { ws.close(); return; diff --git a/packages/server/src/auth/auth.ts b/packages/server/src/auth/auth.ts index ab18955f2..28052f5f6 100644 --- a/packages/server/src/auth/auth.ts +++ b/packages/server/src/auth/auth.ts @@ -1,6 +1,3 @@ -import type { IncomingMessage, ServerResponse } from "node:http"; -import { findAdminByAuthId } from "@dokploy/server/services/admin"; -import { findUserByAuthId } from "@dokploy/server/services/user"; import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; import { TimeSpan } from "lucia"; import { Lucia } from "lucia/dist/core.js"; @@ -42,78 +39,3 @@ export type ReturnValidateToken = Promise<{ user: (User & { authId: string; adminId: string }) | null; session: Session | null; }>; - -export async function validateRequest( - req: IncomingMessage, - res: ServerResponse, -): ReturnValidateToken { - console.log(session); - const sessionId = lucia.readSessionCookie(req.headers.cookie ?? ""); - - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await lucia.validateSession(sessionId); - if (result?.session?.fresh) { - res.appendHeader( - "Set-Cookie", - lucia.createSessionCookie(result.session.id).serialize(), - ); - } - if (!result.session) { - res.appendHeader( - "Set-Cookie", - lucia.createBlankSessionCookie().serialize(), - ); - } - if (result.user) { - try { - if (result.user?.rol === "owner") { - const admin = await findAdminByAuthId(result.user.id); - result.user.adminId = admin.adminId; - } else if (result.user?.rol === "member") { - const userResult = await findUserByAuthId(result.user.id); - result.user.adminId = userResult.adminId; - } - } catch (error) { - return { - user: null, - session: null, - }; - } - } - - return { - session: result.session, - ...((result.user && { - user: { - authId: result.user.id, - email: result.user.email, - rol: result.user.rol, - id: result.user.id, - secret: result.user.secret, - adminId: result.user.adminId, - }, - }) || { - user: null, - }), - }; -} - -export async function validateWebSocketRequest( - req: IncomingMessage, -): Promise<{ user: User; session: Session } | { user: null; session: null }> { - const sessionId = lucia.readSessionCookie(req.headers.cookie ?? ""); - - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await lucia.validateSession(sessionId); - return result; -} diff --git a/packages/server/src/auth/token.ts b/packages/server/src/auth/token.ts index 9dcf99736..e3f404b05 100644 --- a/packages/server/src/auth/token.ts +++ b/packages/server/src/auth/token.ts @@ -21,79 +21,79 @@ export const luciaToken = new Lucia(adapter, { }, }); -export const validateBearerToken = async ( - req: IncomingMessage, -): ReturnValidateToken => { - const authorizationHeader = req.headers.authorization; - const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await luciaToken.validateSession(sessionId); +// export const validateBearerToken = async ( +// req: IncomingMessage, +// ): ReturnValidateToken => { +// const authorizationHeader = req.headers.authorization; +// const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); +// if (!sessionId) { +// return { +// user: null, +// session: null, +// }; +// } +// const result = await luciaToken.validateSession(sessionId); - if (result.user) { - if (result.user?.rol === "owner") { - const admin = await findAdminByAuthId(result.user.id); - result.user.adminId = admin.adminId; - } else if (result.user?.rol === "member") { - const userResult = await findUserByAuthId(result.user.id); - result.user.adminId = userResult.adminId; - } - } - return { - session: result.session, - ...((result.user && { - user: { - adminId: result.user.adminId, - authId: result.user.id, - email: result.user.email, - rol: result.user.rol, - id: result.user.id, - secret: result.user.secret, - }, - }) || { - user: null, - }), - }; -}; +// if (result.user) { +// if (result.user?.rol === "owner") { +// const admin = await findAdminByAuthId(result.user.id); +// result.user.adminId = admin.adminId; +// } else if (result.user?.rol === "member") { +// const userResult = await findUserByAuthId(result.user.id); +// result.user.adminId = userResult.adminId; +// } +// } +// return { +// session: result.session, +// ...((result.user && { +// user: { +// adminId: result.user.adminId, +// authId: result.user.id, +// email: result.user.email, +// rol: result.user.rol, +// id: result.user.id, +// secret: result.user.secret, +// }, +// }) || { +// user: null, +// }), +// }; +// }; -export const validateBearerTokenAPI = async ( - authorizationHeader: string, -): ReturnValidateToken => { - const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); - if (!sessionId) { - return { - user: null, - session: null, - }; - } - const result = await luciaToken.validateSession(sessionId); +// export const validateBearerTokenAPI = async ( +// authorizationHeader: string, +// ): ReturnValidateToken => { +// const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); +// if (!sessionId) { +// return { +// user: null, +// session: null, +// }; +// } +// const result = await luciaToken.validateSession(sessionId); - if (result.user) { - if (result.user?.rol === "owner") { - const admin = await findAdminByAuthId(result.user.id); - result.user.adminId = admin.adminId; - } else if (result.user?.rol === "member") { - const userResult = await findUserByAuthId(result.user.id); - result.user.adminId = userResult.adminId; - } - } - return { - session: result.session, - ...((result.user && { - user: { - adminId: result.user.adminId, - authId: result.user.id, - email: result.user.email, - rol: result.user.rol, - id: result.user.id, - secret: result.user.secret, - }, - }) || { - user: null, - }), - }; -}; +// if (result.user) { +// if (result.user?.rol === "owner") { +// const admin = await findAdminByAuthId(result.user.id); +// result.user.adminId = admin.adminId; +// } else if (result.user?.rol === "member") { +// const userResult = await findUserByAuthId(result.user.id); +// result.user.adminId = userResult.adminId; +// } +// } +// return { +// session: result.session, +// ...((result.user && { +// user: { +// adminId: result.user.adminId, +// authId: result.user.id, +// email: result.user.email, +// rol: result.user.rol, +// id: result.user.id, +// secret: result.user.secret, +// }, +// }) || { +// user: null, +// }), +// }; +// }; diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 3b29e0240..a7fbde9a5 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -84,7 +84,22 @@ export const auth = betterAuth({ }, }, - plugins: [organization()], + plugins: [ + organization({ + async sendInvitationEmail(data, request) { + const inviteLink = `https://example.com/accept-invitation/${data.id}`; + // https://example.com/accept-invitation/8jlBi9Tb9isDb8mc8Sb85u1BaJYklKB2 + // sendOrganizationInvitation({ + // email: data.email, + // invitedByUsername: data.inviter.user.name, + // invitedByEmail: data.inviter.user.email, + // teamName: data.organization.name, + // inviteLink + // }) + console.log("Invitation link", inviteLink); + }, + }), + ], }); export const validateRequest = async (request: IncomingMessage) => { diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index b60665304..ddc3a4504 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -185,11 +185,11 @@ export const deployApplication = async ({ }); try { - const admin = await findUserById(application.project.userId); + // const admin = await findUserById(application.project.userId); - if (admin.cleanupCacheApplications) { - await cleanupFullDocker(application?.serverId); - } + // if (admin.cleanupCacheApplications) { + // await cleanupFullDocker(application?.serverId); + // } if (application.sourceType === "github") { await cloneGithubRepository({ @@ -220,7 +220,7 @@ export const deployApplication = async ({ applicationName: application.name, applicationType: "application", buildLink, - userId: application.project.userId, + organizationId: application.project.organizationId, domains: application.domains, }); } catch (error) { @@ -233,7 +233,7 @@ export const deployApplication = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - userId: application.project.userId, + organizationId: application.project.organizationId, }); throw error; @@ -260,11 +260,11 @@ export const rebuildApplication = async ({ }); try { - const admin = await findUserById(application.project.userId); + // const admin = await findUserById(application.project.userId); - if (admin.cleanupCacheApplications) { - await cleanupFullDocker(application?.serverId); - } + // if (admin.cleanupCacheApplications) { + // await cleanupFullDocker(application?.serverId); + // } if (application.sourceType === "github") { await buildApplication(application, deployment.logPath); } else if (application.sourceType === "gitlab") { @@ -309,11 +309,11 @@ export const deployRemoteApplication = async ({ try { if (application.serverId) { - const admin = await findUserById(application.project.userId); + // const admin = await findUserById(application.project.userId); - if (admin.cleanupCacheApplications) { - await cleanupFullDocker(application?.serverId); - } + // if (admin.cleanupCacheApplications) { + // await cleanupFullDocker(application?.serverId); + // } let command = "set -e;"; if (application.sourceType === "github") { command += await getGithubCloneCommand({ @@ -352,7 +352,7 @@ export const deployRemoteApplication = async ({ applicationName: application.name, applicationType: "application", buildLink, - userId: application.project.userId, + organizationId: application.project.organizationId, domains: application.domains, }); } catch (error) { @@ -376,7 +376,7 @@ export const deployRemoteApplication = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - userId: application.project.userId, + organizationId: application.project.organizationId, }); throw error; @@ -454,11 +454,11 @@ export const deployPreviewApplication = async ({ application.env = `${application.previewEnv}\nDOKPLOY_DEPLOY_URL=${previewDeployment?.domain}`; application.buildArgs = application.previewBuildArgs; - const admin = await findUserById(application.project.userId); + // const admin = await findUserById(application.project.userId); - if (admin.cleanupCacheOnPreviews) { - await cleanupFullDocker(application?.serverId); - } + // if (admin.cleanupCacheOnPreviews) { + // await cleanupFullDocker(application?.serverId); + // } if (application.sourceType === "github") { await cloneGithubRepository({ @@ -568,11 +568,11 @@ export const deployRemotePreviewApplication = async ({ application.buildArgs = application.previewBuildArgs; if (application.serverId) { - const admin = await findUserById(application.project.userId); + // const admin = await findUserById(application.project.userId); - if (admin.cleanupCacheOnPreviews) { - await cleanupFullDocker(application?.serverId); - } + // if (admin.cleanupCacheOnPreviews) { + // await cleanupFullDocker(application?.serverId); + // } let command = "set -e;"; if (application.sourceType === "github") { command += await getGithubCloneCommand({ @@ -637,11 +637,11 @@ export const rebuildRemoteApplication = async ({ try { if (application.serverId) { - const admin = await findUserById(application.project.userId); + // const admin = await findUserById(application.project.userId); - if (admin.cleanupCacheApplications) { - await cleanupFullDocker(application?.serverId); - } + // if (admin.cleanupCacheApplications) { + // await cleanupFullDocker(application?.serverId); + // } if (application.sourceType !== "docker") { let command = "set -e;"; command += getBuildCommand(application, deployment.logPath); diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index a4126a0d0..70bc411c1 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -217,10 +217,10 @@ export const deployCompose = async ({ }); try { - const admin = await findUserById(compose.project.userId); - if (admin.cleanupCacheOnCompose) { - await cleanupFullDocker(compose?.serverId); - } + // const admin = await findUserById(compose.project.userId); + // if (admin.cleanupCacheOnCompose) { + // await cleanupFullDocker(compose?.serverId); + // } if (compose.sourceType === "github") { await cloneGithubRepository({ ...compose, @@ -247,7 +247,7 @@ export const deployCompose = async ({ applicationName: compose.name, applicationType: "compose", buildLink, - userId: compose.project.userId, + organizationId: compose.project.organizationId, domains: compose.domains, }); } catch (error) { @@ -262,7 +262,7 @@ export const deployCompose = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - userId: compose.project.userId, + organizationId: compose.project.organizationId, }); throw error; } @@ -286,10 +286,10 @@ export const rebuildCompose = async ({ }); try { - const admin = await findUserById(compose.project.userId); - if (admin.cleanupCacheOnCompose) { - await cleanupFullDocker(compose?.serverId); - } + // const admin = await findUserById(compose.project.userId); + // if (admin.cleanupCacheOnCompose) { + // await cleanupFullDocker(compose?.serverId); + // } if (compose.serverId) { await getBuildComposeCommand(compose, deployment.logPath); } else { @@ -332,10 +332,10 @@ export const deployRemoteCompose = async ({ }); try { if (compose.serverId) { - const admin = await findUserById(compose.project.userId); - if (admin.cleanupCacheOnCompose) { - await cleanupFullDocker(compose?.serverId); - } + // const admin = await findUserById(compose.project.userId); + // if (admin.cleanupCacheOnCompose) { + // await cleanupFullDocker(compose?.serverId); + // } let command = "set -e;"; if (compose.sourceType === "github") { @@ -381,7 +381,7 @@ export const deployRemoteCompose = async ({ applicationName: compose.name, applicationType: "compose", buildLink, - userId: compose.project.userId, + organizationId: compose.project.organizationId, domains: compose.domains, }); } catch (error) { @@ -406,7 +406,7 @@ export const deployRemoteCompose = async ({ // @ts-ignore errorMessage: error?.message || "Error building", buildLink, - userId: compose.project.userId, + organizationId: compose.project.organizationId, }); throw error; } @@ -430,10 +430,10 @@ export const rebuildRemoteCompose = async ({ }); try { - const admin = await findUserById(compose.project.userId); - if (admin.cleanupCacheOnCompose) { - await cleanupFullDocker(compose?.serverId); - } + // const admin = await findUserById(compose.project.userId); + // if (admin.cleanupCacheOnCompose) { + // await cleanupFullDocker(compose?.serverId); + // } if (compose.serverId) { await getBuildComposeCommand(compose, deployment.logPath); } diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index 69279b027..d5a2149a7 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -2,6 +2,7 @@ import { db } from "@dokploy/server/db"; import { type apiCreatePreviewDeployment, deployments, + organization, previewDeployments, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; @@ -154,11 +155,14 @@ export const createPreviewDeployment = async ( const application = await findApplicationById(schema.applicationId); const appName = `preview-${application.appName}-${generatePassword(6)}`; + const org = await db.query.organization.findFirst({ + where: eq(organization.id, application.project.organizationId), + }); const generateDomain = await generateWildcardDomain( application.previewWildcard || "*.traefik.me", appName, application.server?.ipAddress || "", - application.project.userId, + org?.ownerId || "", ); const octokit = authGithub(application?.github as Github); diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index dd5b4e183..01ac43a14 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -5,8 +5,6 @@ import { execAsync, execAsyncRemote, } from "@dokploy/server/utils/process/execAsync"; -import { findAdminById } from "./admin"; -// import packageInfo from "../../../package.json"; export interface IUpdateData { latestVersion: string | null; diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index c32ceef2c..fbd81cf1b 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -20,17 +20,17 @@ export type User = typeof users_temp.$inferSelect; // }; export const findUserByAuthId = async (authId: string) => { - const userR = await db.query.user.findFirst({ - where: eq(user.id, authId), - with: {}, - }); - if (!userR) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - return userR; + // const userR = await db.query.user.findFirst({ + // where: eq(user.id, authId), + // with: {}, + // }); + // if (!userR) { + // throw new TRPCError({ + // code: "NOT_FOUND", + // message: "User not found", + // }); + // } + // return userR; }; export const findUsers = async (adminId: string) => { diff --git a/packages/server/src/utils/notifications/build-error.ts b/packages/server/src/utils/notifications/build-error.ts index 4ed227e1e..c873c9ab5 100644 --- a/packages/server/src/utils/notifications/build-error.ts +++ b/packages/server/src/utils/notifications/build-error.ts @@ -18,7 +18,7 @@ interface Props { applicationType: string; errorMessage: string; buildLink: string; - userId: string; + organizationId: string; } export const sendBuildErrorNotifications = async ({ @@ -27,14 +27,14 @@ export const sendBuildErrorNotifications = async ({ applicationType, errorMessage, buildLink, - userId, + organizationId, }: Props) => { const date = new Date(); const unixDate = ~~(Number(date) / 1000); const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.appBuildError, true), - eq(notifications.userId, userId), + eq(notifications.organizationId, organizationId), ), with: { email: true, diff --git a/packages/server/src/utils/notifications/build-success.ts b/packages/server/src/utils/notifications/build-success.ts index dfa4824f6..ac470c49f 100644 --- a/packages/server/src/utils/notifications/build-success.ts +++ b/packages/server/src/utils/notifications/build-success.ts @@ -18,7 +18,7 @@ interface Props { applicationName: string; applicationType: string; buildLink: string; - userId: string; + organizationId: string; domains: Domain[]; } @@ -27,7 +27,7 @@ export const sendBuildSuccessNotifications = async ({ applicationName, applicationType, buildLink, - userId, + organizationId, domains, }: Props) => { const date = new Date(); @@ -35,7 +35,7 @@ export const sendBuildSuccessNotifications = async ({ const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.appDeploy, true), - eq(notifications.userId, userId), + eq(notifications.organizationId, organizationId), ), with: { email: true, diff --git a/packages/server/src/utils/notifications/docker-cleanup.ts b/packages/server/src/utils/notifications/docker-cleanup.ts index 0acf1b6c4..b3959cccd 100644 --- a/packages/server/src/utils/notifications/docker-cleanup.ts +++ b/packages/server/src/utils/notifications/docker-cleanup.ts @@ -13,7 +13,7 @@ import { } from "./utils"; export const sendDockerCleanupNotifications = async ( - userId: string, + organizationId: string, message = "Docker cleanup for dokploy", ) => { const date = new Date(); @@ -21,7 +21,7 @@ export const sendDockerCleanupNotifications = async ( const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.dockerCleanup, true), - eq(notifications.userId, userId), + eq(notifications.organizationId, organizationId), ), with: { email: true, From 27736c7c97679896f724efc41285490520393c99 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 03:06:22 -0600 Subject: [PATCH 054/126] refactor: update role and validation handling across multiple pages --- .../dashboard/settings/users/add-user.tsx | 2 +- .../pages/dashboard/project/[projectId].tsx | 4 ++-- .../pages/dashboard/settings/certificates.tsx | 6 +++--- .../pages/dashboard/settings/cluster.tsx | 6 +++--- .../pages/dashboard/settings/destinations.tsx | 6 +++--- .../dashboard/settings/git-providers.tsx | 16 +++++++------- .../pages/dashboard/settings/index.tsx | 6 +++--- .../dashboard/settings/notifications.tsx | 6 +++--- .../pages/dashboard/settings/profile.tsx | 21 +++++++++++-------- .../pages/dashboard/settings/registry.tsx | 6 +++--- .../pages/dashboard/settings/server.tsx | 6 +++--- .../pages/dashboard/settings/servers.tsx | 6 +++--- .../pages/dashboard/settings/ssh-keys.tsx | 15 +++++++------ .../pages/dashboard/settings/users.tsx | 6 +++--- apps/dokploy/pages/register.tsx | 2 +- apps/dokploy/server/wss/drawer-logs.ts | 2 +- 16 files changed, 58 insertions(+), 58 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/users/add-user.tsx b/apps/dokploy/components/dashboard/settings/users/add-user.tsx index 175f723f5..fa1c8bf95 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-user.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-user.tsx @@ -58,7 +58,7 @@ export const AddUser = () => { const onSubmit = async (data: AddUser) => { const result = await authClient.organization.inviteMember({ email: data.email.toLowerCase(), - role: "user", + role: "member", organizationId: activeOrganization?.id, }); console.log(result); diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index b5d6b18f8..abedd10fe 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -674,8 +674,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/certificates.tsx b/apps/dokploy/pages/dashboard/settings/certificates.tsx index 14d020c41..d045208e1 100644 --- a/apps/dokploy/pages/dashboard/settings/certificates.tsx +++ b/apps/dokploy/pages/dashboard/settings/certificates.tsx @@ -24,7 +24,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user || user.role === "member") { return { redirect: { @@ -40,8 +40,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index dd12723c4..8d70f8692 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -33,7 +33,7 @@ export async function getServerSideProps( }, }; } - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user || user.role === "member") { return { redirect: { @@ -48,8 +48,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/destinations.tsx b/apps/dokploy/pages/dashboard/settings/destinations.tsx index 5d34ec5f4..d9d17d538 100644 --- a/apps/dokploy/pages/dashboard/settings/destinations.tsx +++ b/apps/dokploy/pages/dashboard/settings/destinations.tsx @@ -25,7 +25,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user || user.role === "member") { return { redirect: { @@ -41,8 +41,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index c9135fee6..d0d9ca7f1 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -24,7 +24,7 @@ Page.getLayout = (page: ReactElement) => { export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -40,8 +40,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); @@ -49,14 +49,12 @@ export async function getServerSideProps( try { await helpers.project.all.prefetch(); await helpers.settings.isCloud.prefetch(); - const auth = await helpers.auth.get.fetch(); - - if (auth.role === "member") { - const user = await helpers.user.byAuthId.fetch({ - authId: auth.id, + if (user.role === "member") { + const userR = await helpers.user.get.fetch({ + userId: user.id, }); - if (!user.canAccessToGitProviders) { + if (!userR.canAccessToGitProviders) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/index.tsx b/apps/dokploy/pages/dashboard/settings/index.tsx index 577c2b7c7..a12db093d 100644 --- a/apps/dokploy/pages/dashboard/settings/index.tsx +++ b/apps/dokploy/pages/dashboard/settings/index.tsx @@ -181,7 +181,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -205,8 +205,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/notifications.tsx b/apps/dokploy/pages/dashboard/settings/notifications.tsx index 840eb9d4a..d0e0c65fe 100644 --- a/apps/dokploy/pages/dashboard/settings/notifications.tsx +++ b/apps/dokploy/pages/dashboard/settings/notifications.tsx @@ -25,7 +25,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user || user.role === "member") { return { redirect: { @@ -41,8 +41,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 656c30996..73b90c722 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -14,12 +14,12 @@ import superjson from "superjson"; const Page = () => { const { data } = api.auth.get.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( + const { data: user } = api.user.get.useQuery( { authId: data?.id || "", }, { - enabled: !!data?.id && data?.role === "user", + enabled: !!data?.id && data?.role === "member", }, ); @@ -46,7 +46,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const locale = getLocale(req.cookies); - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); const helpers = createServerSideHelpers({ router: appRouter, @@ -54,18 +54,21 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); await helpers.settings.isCloud.prefetch(); await helpers.auth.get.prefetch(); - if (user?.role === "user") { - await helpers.user.byAuthId.prefetch({ - authId: user.authId, - }); + if (user?.role === "member") { + // const userR = await helpers.user.get.fetch({ + // userId: user.id, + // }); + // await helpers.user.byAuthId.prefetch({ + // authId: user.authId, + // }); } if (!user) { diff --git a/apps/dokploy/pages/dashboard/settings/registry.tsx b/apps/dokploy/pages/dashboard/settings/registry.tsx index 61f62614a..0a01e255f 100644 --- a/apps/dokploy/pages/dashboard/settings/registry.tsx +++ b/apps/dokploy/pages/dashboard/settings/registry.tsx @@ -25,7 +25,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user || user.role === "member") { return { redirect: { @@ -40,8 +40,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index 498e97ce1..7a9eff3c6 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -98,7 +98,7 @@ export async function getServerSideProps( }, }; } - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -122,8 +122,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/servers.tsx b/apps/dokploy/pages/dashboard/settings/servers.tsx index 6f83fb669..85ca5bb31 100644 --- a/apps/dokploy/pages/dashboard/settings/servers.tsx +++ b/apps/dokploy/pages/dashboard/settings/servers.tsx @@ -27,7 +27,7 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const locale = await getLocale(req.cookies); - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user) { return { redirect: { @@ -51,8 +51,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index d7876641f..f3b9cf1b8 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -24,7 +24,7 @@ Page.getLayout = (page: ReactElement) => { export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { - const { user, session } = await validateRequest(ctx.req, ctx.res); + const { user, session } = await validateRequest(ctx.req); if (!user) { return { redirect: { @@ -40,23 +40,22 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); try { await helpers.project.all.prefetch(); - const auth = await helpers.auth.get.fetch(); await helpers.settings.isCloud.prefetch(); - if (auth.role === "member") { - const user = await helpers.user.byAuthId.fetch({ - authId: auth.id, + if (user.role === "member") { + const userR = await helpers.user.get.fetch({ + userId: user.id, }); - if (!user.canAccessToSSHKeys) { + if (!userR.canAccessToSSHKeys) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index 8f97e2b6c..1c53c82b1 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -25,7 +25,7 @@ export async function getServerSideProps( ctx: GetServerSidePropsContext<{ serviceId: string }>, ) { const { req, res } = ctx; - const { user, session } = await validateRequest(req, res); + const { user, session } = await validateRequest(req); if (!user || user.role === "member") { return { redirect: { @@ -41,8 +41,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index f79024788..73dce5e77 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -274,7 +274,7 @@ Register.getLayout = (page: ReactElement) => { }; export async function getServerSideProps(context: GetServerSidePropsContext) { if (IS_CLOUD) { - const { user } = await validateRequest(context.req, context.res); + const { user } = await validateRequest(context.req); if (user) { return { diff --git a/apps/dokploy/server/wss/drawer-logs.ts b/apps/dokploy/server/wss/drawer-logs.ts index da2c529ac..9fe947ffa 100644 --- a/apps/dokploy/server/wss/drawer-logs.ts +++ b/apps/dokploy/server/wss/drawer-logs.ts @@ -1,9 +1,9 @@ import type http from "node:http"; +import { validateRequest } from "@dokploy/server/index"; import { applyWSSHandler } from "@trpc/server/adapters/ws"; import { WebSocketServer } from "ws"; import { appRouter } from "../api/root"; import { createTRPCContext } from "../api/trpc"; -import { validateRequest } from "@dokploy/server/index"; export const setupDrawerLogsWebSocketServer = ( server: http.Server, From a8d1471b16377be400c5e6406a2127307aed0bca Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 13:28:29 -0600 Subject: [PATCH 055/126] refactor: update --- apps/dokploy/server/api/routers/auth.ts | 137 +++++++++--------- apps/dokploy/server/api/routers/user.ts | 26 ++-- apps/dokploy/server/api/trpc.ts | 4 +- packages/server/src/auth/auth.ts | 41 ------ packages/server/src/auth/token.ts | 99 ------------- packages/server/src/index.ts | 2 - packages/server/src/services/admin.ts | 47 +++--- packages/server/src/services/user.ts | 14 -- .../server/src/utils/access-log/handler.ts | 35 +++-- packages/server/src/utils/backups/mariadb.ts | 4 +- packages/server/src/utils/backups/mongo.ts | 4 +- packages/server/src/utils/backups/mysql.ts | 4 +- packages/server/src/utils/backups/postgres.ts | 4 +- .../utils/notifications/database-backup.ts | 6 +- 14 files changed, 133 insertions(+), 294 deletions(-) delete mode 100644 packages/server/src/auth/auth.ts delete mode 100644 packages/server/src/auth/token.ts diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index cc149b3c0..9fc66cf2a 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -20,8 +20,6 @@ import { findUserById, generate2FASecret, getUserByToken, - lucia, - luciaToken, removeAdminByAuthId, sendDiscordNotification, sendEmailNotification, @@ -68,11 +66,11 @@ export const authRouter = createTRPCRouter({ type: "cloud", }; } - const session = await lucia.createSession(newAdmin.id || "", {}); - ctx.res.appendHeader( - "Set-Cookie", - lucia.createSessionCookie(session.id).serialize(), - ); + // const session = await lucia.createSession(newAdmin.id || "", {}); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); return { status: "success", type: "selfhosted", @@ -91,24 +89,24 @@ export const authRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { try { const token = await getUserByToken(input.token); - if (token.isExpired) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Invalid token", - }); - } + // if (token.isExpired) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Invalid token", + // }); + // } - const newUser = await createUser(input); + // const newUser = await createUser(input); - if (IS_CLOUD) { - await sendVerificationEmail(token.authId); - return true; - } - const session = await lucia.createSession(newUser?.authId || "", {}); - ctx.res.appendHeader( - "Set-Cookie", - lucia.createSessionCookie(session.id).serialize(), - ); + // if (IS_CLOUD) { + // await sendVerificationEmail(token.authId); + // return true; + // } + // const session = await lucia.createSession(newUser?.authId || "", {}); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); return true; } catch (error) { throw new TRPCError({ @@ -151,12 +149,12 @@ export const authRouter = createTRPCRouter({ }; } - const session = await lucia.createSession(auth?.id || "", {}); + // const session = await lucia.createSession(auth?.id || "", {}); - ctx.res.appendHeader( - "Set-Cookie", - lucia.createSessionCookie(session.id).serialize(), - ); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); return { is2FAEnabled: false, authId: auth?.id, @@ -186,11 +184,11 @@ export const authRouter = createTRPCRouter({ logout: protectedProcedure.mutation(async ({ ctx }) => { const { req, res } = ctx; - const { session } = await validateRequest(req, res); + const { session } = await validateRequest(req); if (!session) return false; - await lucia.invalidateSession(session.id); - res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); + // await lucia.invalidateSession(session.id); + // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); return true; }), @@ -211,13 +209,13 @@ export const authRouter = createTRPCRouter({ }); } } - const auth = await updateAuthById(ctx.user.authId, { - ...(input.email && { email: input.email.toLowerCase() }), - ...(input.password && { - password: bcrypt.hashSync(input.password, 10), - }), - ...(input.image && { image: input.image }), - }); + // const auth = await updateAuthById(ctx.user.authId, { + // ...(input.email && { email: input.email.toLowerCase() }), + // ...(input.password && { + // password: bcrypt.hashSync(input.password, 10), + // }), + // ...(input.image && { image: input.image }), + // }); return auth; }), @@ -248,17 +246,17 @@ export const authRouter = createTRPCRouter({ }); } const { req, res } = ctx; - const { session } = await validateRequest(req, res); + const { session } = await validateRequest(req); if (!session) return false; - await lucia.invalidateSession(session.id); - res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); + // await lucia.invalidateSession(session.id); + // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); - if (ctx.user.rol === "owner") { - await removeAdminByAuthId(ctx.user.authId); - } else { - await removeUserByAuthId(ctx.user.authId); - } + // if (ctx.user.rol === "owner") { + // await removeAdminByAuthId(ctx.user.authId); + // } else { + // await removeUserByAuthId(ctx.user.authId); + // } return true; }), @@ -267,9 +265,9 @@ export const authRouter = createTRPCRouter({ const auth = await findUserById(ctx.user.id); console.log(auth); - if (auth.token) { - await luciaToken.invalidateSession(auth.token); - } + // if (auth.token) { + // await luciaToken.invalidateSession(auth.token); + // } // const session = await luciaToken.createSession(auth?.id || "", { // expiresIn: 60 * 60 * 24 * 30, // }); @@ -292,39 +290,38 @@ export const authRouter = createTRPCRouter({ verify2FASetup: protectedProcedure .input(apiVerify2FA) .mutation(async ({ ctx, input }) => { - const auth = await findAuthById(ctx.user.authId); - - await verify2FA(auth, input.secret, input.pin); - await updateAuthById(auth.id, { - is2FAEnabled: true, - secret: input.secret, - }); - return auth; + // const auth = await findAuthById(ctx.user.authId); + // await verify2FA(auth, input.secret, input.pin); + // await updateAuthById(auth.id, { + // is2FAEnabled: true, + // secret: input.secret, + // }); + // return auth; }), verifyLogin2FA: publicProcedure .input(apiVerifyLogin2FA) .mutation(async ({ ctx, input }) => { - const auth = await findAuthById(input.id); + // const auth = await findAuthById(input.id); - await verify2FA(auth, auth.secret || "", input.pin); + // await verify2FA(auth, auth.secret || "", input.pin); - const session = await lucia.createSession(auth.id, {}); + // const session = await lucia.createSession(auth.id, {}); - ctx.res.appendHeader( - "Set-Cookie", - lucia.createSessionCookie(session.id).serialize(), - ); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); return true; }), disable2FA: protectedProcedure.mutation(async ({ ctx }) => { - const auth = await findAuthById(ctx.user.authId); - await updateAuthById(auth.id, { - is2FAEnabled: false, - secret: null, - }); - return auth; + // const auth = await findAuthById(ctx.user.authId); + // await updateAuthById(auth.id, { + // is2FAEnabled: false, + // secret: null, + // }); + // return auth; }), sendResetPasswordEmail: publicProcedure .input( diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 00c2bb82e..f4de4d9f7 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -1,5 +1,5 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; -import { findUserByAuthId, findUserById, findUsers } from "@dokploy/server"; +import { findUserByAuthId, findUserById } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { member } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; @@ -31,16 +31,16 @@ export const userRouter = createTRPCRouter({ // } return user; }), - byUserId: protectedProcedure - .input(apiFindOneUser) - .query(async ({ input, ctx }) => { - const user = await findUserById(input.userId); - if (user.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to access this user", - }); - } - return user; - }), + // byUserId: protectedProcedure + // .input(apiFindOneUser) + // .query(async ({ input, ctx }) => { + // const user = await findUserById(input.userId); + // if (user.adminId !== ctx.user.adminId) { + // throw new TRPCError({ + // code: "UNAUTHORIZED", + // message: "You are not allowed to access this user", + // }); + // } + // return user; + // }), }); diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 7f8f0f756..c63839c5e 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -75,8 +75,8 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { // user = cookieResult.user; // } - console.log("session", session); - console.log("user", user); + // console.log("session", session); + // console.log("user", user); return createInnerTRPCContext({ req, diff --git a/packages/server/src/auth/auth.ts b/packages/server/src/auth/auth.ts deleted file mode 100644 index 28052f5f6..000000000 --- a/packages/server/src/auth/auth.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { DrizzlePostgreSQLAdapter } from "@lucia-auth/adapter-drizzle"; -import { TimeSpan } from "lucia"; -import { Lucia } from "lucia/dist/core.js"; -import type { Session, User } from "lucia/dist/core.js"; -import { db } from "../db"; -import { type DatabaseUser, auth, session } from "../db/schema"; - -export const adapter = new DrizzlePostgreSQLAdapter(db, session, auth); - -export const lucia = new Lucia(adapter, { - sessionCookie: { - attributes: { - secure: false, - }, - }, - - sessionExpiresIn: new TimeSpan(1, "d"), - getUserAttributes: (attributes) => { - return { - email: attributes.email, - rol: attributes.rol, - secret: attributes.secret !== null, - adminId: attributes.adminId, - }; - }, -}); - -declare module "lucia" { - interface Register { - Lucia: typeof lucia; - DatabaseUserAttributes: Omit & { - authId: string; - adminId: string; - }; - } -} - -export type ReturnValidateToken = Promise<{ - user: (User & { authId: string; adminId: string }) | null; - session: Session | null; -}>; diff --git a/packages/server/src/auth/token.ts b/packages/server/src/auth/token.ts deleted file mode 100644 index e3f404b05..000000000 --- a/packages/server/src/auth/token.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { IncomingMessage } from "node:http"; -import { TimeSpan } from "lucia"; -import { Lucia } from "lucia/dist/core.js"; -import { findAdminByAuthId } from "../services/admin"; -import { findUserByAuthId } from "../services/user"; -import { type ReturnValidateToken, adapter } from "./auth"; - -export const luciaToken = new Lucia(adapter, { - sessionCookie: { - attributes: { - secure: false, - }, - }, - sessionExpiresIn: new TimeSpan(365, "d"), - getUserAttributes: (attributes) => { - return { - email: attributes.email, - rol: attributes.rol, - secret: attributes.secret !== null, - }; - }, -}); - -// export const validateBearerToken = async ( -// req: IncomingMessage, -// ): ReturnValidateToken => { -// const authorizationHeader = req.headers.authorization; -// const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); -// if (!sessionId) { -// return { -// user: null, -// session: null, -// }; -// } -// const result = await luciaToken.validateSession(sessionId); - -// if (result.user) { -// if (result.user?.rol === "owner") { -// const admin = await findAdminByAuthId(result.user.id); -// result.user.adminId = admin.adminId; -// } else if (result.user?.rol === "member") { -// const userResult = await findUserByAuthId(result.user.id); -// result.user.adminId = userResult.adminId; -// } -// } -// return { -// session: result.session, -// ...((result.user && { -// user: { -// adminId: result.user.adminId, -// authId: result.user.id, -// email: result.user.email, -// rol: result.user.rol, -// id: result.user.id, -// secret: result.user.secret, -// }, -// }) || { -// user: null, -// }), -// }; -// }; - -// export const validateBearerTokenAPI = async ( -// authorizationHeader: string, -// ): ReturnValidateToken => { -// const sessionId = luciaToken.readBearerToken(authorizationHeader ?? ""); -// if (!sessionId) { -// return { -// user: null, -// session: null, -// }; -// } -// const result = await luciaToken.validateSession(sessionId); - -// if (result.user) { -// if (result.user?.rol === "owner") { -// const admin = await findAdminByAuthId(result.user.id); -// result.user.adminId = admin.adminId; -// } else if (result.user?.rol === "member") { -// const userResult = await findUserByAuthId(result.user.id); -// result.user.adminId = userResult.adminId; -// } -// } -// return { -// session: result.session, -// ...((result.user && { -// user: { -// adminId: result.user.adminId, -// authId: result.user.id, -// email: result.user.email, -// rol: result.user.rol, -// id: result.user.id, -// secret: result.user.secret, -// }, -// }) || { -// user: null, -// }), -// }; -// }; diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 345849ed9..554b4c124 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,5 +1,3 @@ -export * from "./auth/auth"; -export * from "./auth/token"; export * from "./auth/random-password"; // export * from "./db"; export * from "./services/admin"; diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 78a0375a8..41b92587f 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -143,27 +143,26 @@ export const findAdmin = async () => { }; export const getUserByToken = async (token: string) => { - const user = await db.query.users.findFirst({ - where: eq(users.token, token), - with: { - auth: { - columns: { - password: false, - }, - }, - }, - }); - - if (!user) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Invitation not found", - }); - } - return { - ...user, - isExpired: user.isRegistered, - }; + // const user = await db.query.users.findFirst({ + // where: eq(users.token, token), + // with: { + // auth: { + // columns: { + // password: false, + // }, + // }, + // }, + // }); + // if (!user) { + // throw new TRPCError({ + // code: "NOT_FOUND", + // message: "Invitation not found", + // }); + // } + // return { + // ...user, + // isExpired: user.isRegistered, + // }; }; export const removeUserById = async (userId: string) => { @@ -181,9 +180,9 @@ export const removeAdminByAuthId = async (authId: string) => { // First delete all associated users const users = admin.users; - for (const user of users) { - await removeUserById(user.id); - } + // for (const user of users) { + // await removeUserById(user.id); + // } // Then delete the auth record which will cascade delete the admin return await db .delete(auth) diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index fbd81cf1b..170af9084 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -33,20 +33,6 @@ export const findUserByAuthId = async (authId: string) => { // return userR; }; -export const findUsers = async (adminId: string) => { - const currentUsers = await db.query.user.findMany({ - where: eq(user.adminId, adminId), - with: { - auth: { - columns: { - secret: false, - }, - }, - }, - }); - return currentUsers; -}; - export const addNewProject = async (userId: string, projectId: string) => { const userR = await findUserById(userId); diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index 668855144..574717323 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -1,5 +1,4 @@ import { IS_CLOUD, paths } from "@dokploy/server/constants"; -import { updateAdmin } from "@dokploy/server/services/admin"; import { type RotatingFileStream, createStream } from "rotating-file-stream"; import { db } from "../../db"; import { execAsync } from "../process/execAsync"; @@ -23,27 +22,27 @@ class LogRotationManager { } private async initialize(): Promise { - // const isActive = await this.getStateFromDB(); - // if (isActive) { - // await this.activateStream(); - // } + const isActive = await this.getStateFromDB(); + if (isActive) { + await this.activateStream(); + } } - // private async getStateFromDB(): Promise { - // const setting = await db.query.admins.findFirst({}); - // return setting?.enableLogRotation ?? false; - // } + private async getStateFromDB(): Promise { + const setting = await db.query.admins.findFirst({}); + return setting?.enableLogRotation ?? false; + } - // private async setStateInDB(active: boolean): Promise { - // const admin = await db.query.admins.findFirst({}); + private async setStateInDB(active: boolean): Promise { + const admin = await db.query.admins.findFirst({}); - // if (!admin) { - // return; - // } - // await updateAdmin(admin?.authId, { - // enableLogRotation: active, - // }); - // } + if (!admin) { + return; + } + // await updateAdmin(admin?.authId, { + // enableLogRotation: active, + // }); + } private async activateStream(): Promise { const { DYNAMIC_TRAEFIK_PATH } = paths(); diff --git a/packages/server/src/utils/backups/mariadb.ts b/packages/server/src/utils/backups/mariadb.ts index 7ffa16e11..56c2919c4 100644 --- a/packages/server/src/utils/backups/mariadb.ts +++ b/packages/server/src/utils/backups/mariadb.ts @@ -49,7 +49,7 @@ export const runMariadbBackup = async ( projectName: project.name, databaseType: "mariadb", type: "success", - userId: project.userId, + organizationId: project.organizationId, }); } catch (error) { console.log(error); @@ -60,7 +60,7 @@ export const runMariadbBackup = async ( type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - userId: project.userId, + organizationId: project.organizationId, }); throw error; } diff --git a/packages/server/src/utils/backups/mongo.ts b/packages/server/src/utils/backups/mongo.ts index d6860a010..a40ec4f47 100644 --- a/packages/server/src/utils/backups/mongo.ts +++ b/packages/server/src/utils/backups/mongo.ts @@ -46,7 +46,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { projectName: project.name, databaseType: "mongodb", type: "success", - userId: project.userId, + organizationId: project.organizationId, }); } catch (error) { console.log(error); @@ -57,7 +57,7 @@ export const runMongoBackup = async (mongo: Mongo, backup: BackupSchedule) => { type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - userId: project.userId, + organizationId: project.organizationId, }); throw error; } diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts index a73179417..009a02cf2 100644 --- a/packages/server/src/utils/backups/mysql.ts +++ b/packages/server/src/utils/backups/mysql.ts @@ -46,7 +46,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => { projectName: project.name, databaseType: "mysql", type: "success", - userId: project.userId, + organizationId: project.organizationId, }); } catch (error) { console.log(error); @@ -57,7 +57,7 @@ export const runMySqlBackup = async (mysql: MySql, backup: BackupSchedule) => { type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - userId: project.userId, + organizationId: project.organizationId, }); throw error; } diff --git a/packages/server/src/utils/backups/postgres.ts b/packages/server/src/utils/backups/postgres.ts index 33c37b862..5ada2aa9d 100644 --- a/packages/server/src/utils/backups/postgres.ts +++ b/packages/server/src/utils/backups/postgres.ts @@ -49,7 +49,7 @@ export const runPostgresBackup = async ( projectName: project.name, databaseType: "postgres", type: "success", - userId: project.userId, + organizationId: project.organizationId, }); } catch (error) { await sendDatabaseBackupNotifications({ @@ -59,7 +59,7 @@ export const runPostgresBackup = async ( type: "error", // @ts-ignore errorMessage: error?.message || "Error message not provided", - userId: project.userId, + organizationId: project.organizationId, }); throw error; diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 3ce36aa9f..08cff4b54 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -19,13 +19,13 @@ export const sendDatabaseBackupNotifications = async ({ databaseType, type, errorMessage, - userId, + organizationId, }: { projectName: string; applicationName: string; databaseType: "postgres" | "mysql" | "mongodb" | "mariadb"; type: "error" | "success"; - userId: string; + organizationId: string; errorMessage?: string; }) => { const date = new Date(); @@ -33,7 +33,7 @@ export const sendDatabaseBackupNotifications = async ({ const notificationList = await db.query.notifications.findMany({ where: and( eq(notifications.databaseBackup, true), - eq(notifications.userId, userId), + eq(notifications.organizationId, organizationId), ), with: { email: true, From 9856502ece59841a1861213746b9df137fe055a1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 13:55:27 -0600 Subject: [PATCH 056/126] refactor: remove old references --- apps/dokploy/__test__/drop/drop.test.test.ts | 2 +- .../server/update-server-config.test.ts | 8 +- apps/dokploy/pages/api/stripe/webhook.ts | 2 +- .../pages/dashboard/project/[projectId].tsx | 11 +- .../services/application/[applicationId].tsx | 16 +- .../services/compose/[composeId].tsx | 15 +- .../services/mariadb/[mariadbId].tsx | 16 +- .../[projectId]/services/mongo/[mongoId].tsx | 15 +- .../[projectId]/services/mysql/[mysqlId].tsx | 15 +- .../services/postgres/[postgresId].tsx | 16 +- .../[projectId]/services/redis/[redisId].tsx | 15 +- apps/dokploy/server/api/routers/admin.ts | 25 +- apps/dokploy/server/api/routers/auth.ts | 241 ++++++++---------- packages/server/src/db/schema/admin.ts | 210 --------------- packages/server/src/db/schema/auth.ts | 130 ---------- packages/server/src/db/schema/certificate.ts | 3 - packages/server/src/db/schema/deployment.ts | 2 +- packages/server/src/db/schema/destination.ts | 8 - packages/server/src/db/schema/git-provider.ts | 3 - packages/server/src/db/schema/index.ts | 2 - packages/server/src/db/schema/notification.ts | 2 - packages/server/src/db/schema/project.ts | 4 - packages/server/src/db/schema/registry.ts | 7 - packages/server/src/db/schema/server.ts | 8 - packages/server/src/db/schema/session.ts | 13 - packages/server/src/db/schema/source.ts | 27 -- packages/server/src/db/schema/ssh-key.ts | 3 - packages/server/src/db/schema/user.ts | 142 +++++++---- packages/server/src/services/admin.ts | 2 - packages/server/src/services/auth.ts | 106 +------- 30 files changed, 250 insertions(+), 819 deletions(-) delete mode 100644 packages/server/src/db/schema/admin.ts delete mode 100644 packages/server/src/db/schema/auth.ts delete mode 100644 packages/server/src/db/schema/source.ts diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index c4b2ba8d8..4e6f20d3f 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -45,7 +45,7 @@ const baseApp: ApplicationNested = { previewWildcard: "", project: { env: "", - adminId: "", + organizationId: "", name: "", description: "", createdAt: "", diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index e38c19e40..f7bf3595a 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -5,7 +5,7 @@ vi.mock("node:fs", () => ({ default: fs, })); -import type { Admin, FileConfig } from "@dokploy/server"; +import type { Admin, FileConfig, User } from "@dokploy/server"; import { createDefaultServerTraefikConfig, loadOrCreateConfig, @@ -13,7 +13,7 @@ import { } from "@dokploy/server"; import { beforeEach, expect, test, vi } from "vitest"; -const baseAdmin: Admin = { +const baseAdmin: Partial = { enablePaidFeatures: false, metricsConfig: { containers: { @@ -40,9 +40,7 @@ const baseAdmin: Admin = { cleanupCacheApplications: false, cleanupCacheOnCompose: false, cleanupCacheOnPreviews: false, - createdAt: "", - authId: "", - adminId: "string", + createdAt: new Date(), serverIp: null, certificateType: "none", host: null, diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index e8416c5d0..d9cbedc8a 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -1,6 +1,6 @@ import { buffer } from "node:stream/consumers"; import { db } from "@/server/db"; -import { admins, server, users_temp } from "@/server/db/schema"; +import { server, users_temp } from "@/server/db/schema"; import { findAdminById, findUserById } from "@dokploy/server"; import { asc, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index abedd10fe..db70a59af 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -201,14 +201,7 @@ const Project = ( const [isBulkActionLoading, setIsBulkActionLoading] = useState(false); const { projectId } = props; const { data: auth } = api.auth.get.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); + const { data, isLoading, refetch } = api.project.one.useQuery({ projectId }); const router = useRouter(); @@ -335,7 +328,7 @@ const Project = ( {data?.description} - {(auth?.role === "owner" || user?.canCreateServices) && ( + {(auth?.role === "owner" || auth?.user?.canCreateServices) && (
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index a394f320f..5bdb2ee6c 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -40,7 +40,6 @@ import { cn } from "@/lib/utils"; import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server/lib/auth"; -// import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import copy from "copy-to-clipboard"; import { GlobeIcon, HelpCircle, ServerOff, Trash2 } from "lucide-react"; @@ -89,14 +88,6 @@ const Service = ( const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: auth } = api.auth.get.useQuery(); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); return (
@@ -187,7 +178,8 @@ const Service = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -387,8 +379,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index 8bd3e8638..f2c4062a7 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -82,14 +82,6 @@ const Service = ( const { data: auth } = api.auth.get.useQuery(); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); return (
@@ -181,7 +173,8 @@ const Service = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -382,8 +375,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 9adbad15c..b9ad1e226 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -63,14 +63,7 @@ const Mariadb = ( const { data } = api.mariadb.one.useQuery({ mariadbId }); const { data: auth } = api.auth.get.useQuery(); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); + const { data: isCloud } = api.settings.isCloud.useQuery(); return ( @@ -154,7 +147,8 @@ const Mariadb = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -332,8 +326,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index a5ba6b5de..218e3da9c 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -63,14 +63,6 @@ const Mongo = ( const { data: auth } = api.auth.get.useQuery(); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -156,7 +148,8 @@ const Mongo = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -334,8 +327,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index 3e17c4fac..87011428e 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -62,14 +62,6 @@ const MySql = ( const { data } = api.mysql.one.useQuery({ mysqlId }); const { data: auth } = api.auth.get.useQuery(); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -156,7 +148,8 @@ const MySql = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -339,8 +332,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 0b1d2aee6..b78fd569c 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -61,14 +61,7 @@ const Postgresql = ( const [tab, setSab] = useState(activeTab); const { data } = api.postgres.one.useQuery({ postgresId }); const { data: auth } = api.auth.get.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); + const { data: monitoring } = api.admin.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -154,7 +147,8 @@ const Postgresql = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -335,8 +329,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 853e6688b..9ed8c47a1 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -62,14 +62,6 @@ const Redis = ( const { data: auth } = api.auth.get.useQuery(); const { data: monitoring } = api.admin.getMetricsToken.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -155,7 +147,8 @@ const Redis = (
- {(auth?.role === "owner" || user?.canDeleteServices) && ( + {(auth?.role === "owner" || + auth?.user?.canDeleteServices) && ( )}
@@ -327,8 +320,8 @@ export async function getServerSideProps( req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 55df35afe..695cdb336 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -4,7 +4,6 @@ import { apiCreateUserInvitation, apiFindOneToken, apiRemoveUser, - apiUpdateAdmin, apiUpdateWebServerMonitoring, } from "@/server/db/schema"; import { @@ -36,19 +35,17 @@ export const adminRouter = createTRPCRouter({ ...rest, }; }), - update: adminProcedure - .input(apiUpdateAdmin) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "member") { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to update this admin", - }); - } - const { id } = await findUserById(ctx.user.id); - // @ts-ignore - return updateAdmin(id, input); - }), + update: adminProcedure.mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "member") { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not allowed to update this admin", + }); + } + const { id } = await findUserById(ctx.user.id); + // @ts-ignore + return updateAdmin(id, input); + }), createUserInvitation: adminProcedure .input(apiCreateUserInvitation) .mutation(async ({ input, ctx }) => { diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 9fc66cf2a..ad2fab07e 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -1,30 +1,23 @@ import { - apiCreateAdmin, - apiCreateUser, - apiFindOneAuth, - apiLogin, - apiUpdateAuth, - apiVerify2FA, - apiVerifyLogin2FA, - auth, + // apiCreateAdmin, + // apiCreateUser, + // apiFindOneAuth, + // apiLogin, + // apiUpdateAuth, + // apiVerify2FA, + // apiVerifyLogin2FA, + // auth, member, } from "@/server/db/schema"; import { WEBSITE_URL } from "@/server/utils/stripe"; import { - type Auth, IS_CLOUD, - createAdmin, - createUser, - findAuthByEmail, findAuthById, findUserById, generate2FASecret, getUserByToken, - removeAdminByAuthId, sendDiscordNotification, sendEmailNotification, - updateAuthById, - updateUser, validateRequest, verify2FA, } from "@dokploy/server"; @@ -43,81 +36,77 @@ import { } from "../trpc"; export const authRouter = createTRPCRouter({ - createAdmin: publicProcedure - .input(apiCreateAdmin) - .mutation(async ({ ctx, input }) => { - try { - if (!IS_CLOUD) { - const admin = await db.query.admins.findFirst({}); - if (admin) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Admin already exists", - }); - } + createAdmin: publicProcedure.mutation(async ({ ctx, input }) => { + try { + if (!IS_CLOUD) { + const admin = await db.query.admins.findFirst({}); + if (admin) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Admin already exists", + }); } - const newAdmin = await createAdmin(input); + } + const newAdmin = await createAdmin(input); - if (IS_CLOUD) { - await sendDiscordNotificationWelcome(newAdmin); - await sendVerificationEmail(newAdmin.id); - return { - status: "success", - type: "cloud", - }; - } - // const session = await lucia.createSession(newAdmin.id || "", {}); - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); + if (IS_CLOUD) { + await sendDiscordNotificationWelcome(newAdmin); + await sendVerificationEmail(newAdmin.id); return { status: "success", - type: "selfhosted", + type: "cloud", }; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - // @ts-ignore - message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`, - cause: error, - }); } - }), - createUser: publicProcedure - .input(apiCreateUser) - .mutation(async ({ ctx, input }) => { - try { - const token = await getUserByToken(input.token); - // if (token.isExpired) { - // throw new TRPCError({ - // code: "BAD_REQUEST", - // message: "Invalid token", - // }); - // } + // const session = await lucia.createSession(newAdmin.id || "", {}); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); + return { + status: "success", + type: "selfhosted", + }; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + // @ts-ignore + message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`, + cause: error, + }); + } + }), + createUser: publicProcedure.mutation(async ({ ctx, input }) => { + try { + const token = await getUserByToken(input.token); + // if (token.isExpired) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Invalid token", + // }); + // } - // const newUser = await createUser(input); + // const newUser = await createUser(input); - // if (IS_CLOUD) { - // await sendVerificationEmail(token.authId); - // return true; - // } - // const session = await lucia.createSession(newUser?.authId || "", {}); - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); - return true; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - cause: error, - }); - } - }), + // if (IS_CLOUD) { + // await sendVerificationEmail(token.authId); + // return true; + // } + // const session = await lucia.createSession(newUser?.authId || "", {}); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); + return true; + } catch (error) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Error creating the user", + cause: error, + }); + } + }), - login: publicProcedure.input(apiLogin).mutation(async ({ ctx, input }) => { + login: publicProcedure.mutation(async ({ ctx, input }) => { try { const auth = await findAuthByEmail(input.email); @@ -192,33 +181,31 @@ export const authRouter = createTRPCRouter({ return true; }), - update: protectedProcedure - .input(apiUpdateAuth) - .mutation(async ({ ctx, input }) => { - const currentAuth = await findAuthByEmail(ctx.user.email); + update: protectedProcedure.mutation(async ({ ctx, input }) => { + const currentAuth = await findAuthByEmail(ctx.user.email); - if (input.currentPassword || input.password) { - const correctPassword = bcrypt.compareSync( - input.currentPassword || "", - currentAuth?.password || "", - ); - if (!correctPassword) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Current password is incorrect", - }); - } + if (input.currentPassword || input.password) { + const correctPassword = bcrypt.compareSync( + input.currentPassword || "", + currentAuth?.password || "", + ); + if (!correctPassword) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Current password is incorrect", + }); } - // const auth = await updateAuthById(ctx.user.authId, { - // ...(input.email && { email: input.email.toLowerCase() }), - // ...(input.password && { - // password: bcrypt.hashSync(input.password, 10), - // }), - // ...(input.image && { image: input.image }), - // }); + } + // const auth = await updateAuthById(ctx.user.authId, { + // ...(input.email && { email: input.email.toLowerCase() }), + // ...(input.password && { + // password: bcrypt.hashSync(input.password, 10), + // }), + // ...(input.image && { image: input.image }), + // }); - return auth; - }), + return auth; + }), removeSelfAccount: protectedProcedure .input( z.object({ @@ -279,7 +266,7 @@ export const authRouter = createTRPCRouter({ verifyToken: protectedProcedure.mutation(async () => { return true; }), - one: adminProcedure.input(apiFindOneAuth).query(async ({ input }) => { + one: adminProcedure.query(async ({ input }) => { const auth = await findAuthById(input.id); return auth; }), @@ -287,34 +274,30 @@ export const authRouter = createTRPCRouter({ generate2FASecret: protectedProcedure.query(async ({ ctx }) => { return await generate2FASecret(ctx.user.id); }), - verify2FASetup: protectedProcedure - .input(apiVerify2FA) - .mutation(async ({ ctx, input }) => { - // const auth = await findAuthById(ctx.user.authId); - // await verify2FA(auth, input.secret, input.pin); - // await updateAuthById(auth.id, { - // is2FAEnabled: true, - // secret: input.secret, - // }); - // return auth; - }), + verify2FASetup: protectedProcedure.mutation(async ({ ctx, input }) => { + // const auth = await findAuthById(ctx.user.authId); + // await verify2FA(auth, input.secret, input.pin); + // await updateAuthById(auth.id, { + // is2FAEnabled: true, + // secret: input.secret, + // }); + // return auth; + }), - verifyLogin2FA: publicProcedure - .input(apiVerifyLogin2FA) - .mutation(async ({ ctx, input }) => { - // const auth = await findAuthById(input.id); + verifyLogin2FA: publicProcedure.mutation(async ({ ctx, input }) => { + // const auth = await findAuthById(input.id); - // await verify2FA(auth, auth.secret || "", input.pin); + // await verify2FA(auth, auth.secret || "", input.pin); - // const session = await lucia.createSession(auth.id, {}); + // const session = await lucia.createSession(auth.id, {}); - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); + // ctx.res.appendHeader( + // "Set-Cookie", + // lucia.createSessionCookie(session.id).serialize(), + // ); - return true; - }), + return true; + }), disable2FA: protectedProcedure.mutation(async ({ ctx }) => { // const auth = await findAuthById(ctx.user.authId); // await updateAuthById(auth.id, { diff --git a/packages/server/src/db/schema/admin.ts b/packages/server/src/db/schema/admin.ts deleted file mode 100644 index 983f99fd6..000000000 --- a/packages/server/src/db/schema/admin.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { relations } from "drizzle-orm"; -import { - boolean, - integer, - json, - jsonb, - pgTable, - text, -} from "drizzle-orm/pg-core"; -import { createInsertSchema } from "drizzle-zod"; -import { nanoid } from "nanoid"; -import { z } from "zod"; -import { auth } from "./auth"; -import { certificates } from "./certificate"; -import { registry } from "./registry"; -import { certificateType } from "./shared"; -import { sshKeys } from "./ssh-key"; -import { users } from "./user"; - -export const admins = pgTable("admin", { - adminId: text("adminId") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - serverIp: text("serverIp"), - certificateType: certificateType("certificateType").notNull().default("none"), - host: text("host"), - letsEncryptEmail: text("letsEncryptEmail"), - sshPrivateKey: text("sshPrivateKey"), - enableDockerCleanup: boolean("enableDockerCleanup").notNull().default(false), - enableLogRotation: boolean("enableLogRotation").notNull().default(false), - authId: text("authId") - .notNull() - .references(() => auth.id, { onDelete: "cascade" }), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), - stripeCustomerId: text("stripeCustomerId"), - stripeSubscriptionId: text("stripeSubscriptionId"), - serversQuantity: integer("serversQuantity").notNull().default(0), - - // Metrics - enablePaidFeatures: boolean("enablePaidFeatures").notNull().default(false), - metricsConfig: jsonb("metricsConfig") - .$type<{ - server: { - type: "Dokploy" | "Remote"; - refreshRate: number; - port: number; - token: string; - urlCallback: string; - retentionDays: number; - cronJob: string; - thresholds: { - cpu: number; - memory: number; - }; - }; - containers: { - refreshRate: number; - services: { - include: string[]; - exclude: string[]; - }; - }; - }>() - .notNull() - .default({ - server: { - type: "Dokploy", - refreshRate: 60, - port: 4500, - token: "", - retentionDays: 2, - cronJob: "", - urlCallback: "", - thresholds: { - cpu: 0, - memory: 0, - }, - }, - containers: { - refreshRate: 60, - services: { - include: [], - exclude: [], - }, - }, - }), - cleanupCacheApplications: boolean("cleanupCacheApplications") - .notNull() - .default(false), - cleanupCacheOnPreviews: boolean("cleanupCacheOnPreviews") - .notNull() - .default(false), - cleanupCacheOnCompose: boolean("cleanupCacheOnCompose") - .notNull() - .default(false), -}); - -export const adminsRelations = relations(admins, ({ one, many }) => ({ - auth: one(auth, { - fields: [admins.authId], - references: [auth.id], - }), - users: many(users), - registry: many(registry), - sshKeys: many(sshKeys), - certificates: many(certificates), -})); - -const createSchema = createInsertSchema(admins, { - adminId: z.string(), - enableDockerCleanup: z.boolean().optional(), - sshPrivateKey: z.string().optional(), - certificateType: z.enum(["letsencrypt", "none"]).default("none"), - serverIp: z.string().optional(), - letsEncryptEmail: z.string().optional(), -}); - -export const apiUpdateAdmin = createSchema.partial(); - -export const apiSaveSSHKey = createSchema - .pick({ - sshPrivateKey: true, - }) - .required(); - -export const apiAssignDomain = createSchema - .pick({ - host: true, - certificateType: true, - letsEncryptEmail: true, - }) - .required() - .partial({ - letsEncryptEmail: true, - }); - -export const apiUpdateDockerCleanup = createSchema - .pick({ - enableDockerCleanup: true, - }) - .required() - .extend({ - serverId: z.string().optional(), - }); - -export const apiTraefikConfig = z.object({ - traefikConfig: z.string().min(1), -}); - -export const apiModifyTraefikConfig = z.object({ - path: z.string().min(1), - traefikConfig: z.string().min(1), - serverId: z.string().optional(), -}); -export const apiReadTraefikConfig = z.object({ - path: z.string().min(1), - serverId: z.string().optional(), -}); - -export const apiEnableDashboard = z.object({ - enableDashboard: z.boolean().optional(), - serverId: z.string().optional(), -}); - -export const apiServerSchema = z - .object({ - serverId: z.string().optional(), - }) - .optional(); - -export const apiReadStatsLogs = z.object({ - page: z - .object({ - pageIndex: z.number(), - pageSize: z.number(), - }) - .optional(), - status: z.string().array().optional(), - search: z.string().optional(), - sort: z.object({ id: z.string(), desc: z.boolean() }).optional(), -}); - -export const apiUpdateWebServerMonitoring = z.object({ - metricsConfig: z - .object({ - server: z.object({ - refreshRate: z.number().min(2), - port: z.number().min(1), - token: z.string(), - urlCallback: z.string().url(), - retentionDays: z.number().min(1), - cronJob: z.string().min(1), - thresholds: z.object({ - cpu: z.number().min(0), - memory: z.number().min(0), - }), - }), - containers: z.object({ - refreshRate: z.number().min(2), - services: z.object({ - include: z.array(z.string()).optional(), - exclude: z.array(z.string()).optional(), - }), - }), - }) - .required(), -}); diff --git a/packages/server/src/db/schema/auth.ts b/packages/server/src/db/schema/auth.ts deleted file mode 100644 index 7093a40c3..000000000 --- a/packages/server/src/db/schema/auth.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { getRandomValues } from "node:crypto"; -import { relations } from "drizzle-orm"; -import { boolean, pgEnum, pgTable, text } from "drizzle-orm/pg-core"; -import { createInsertSchema } from "drizzle-zod"; -import { nanoid } from "nanoid"; -import { z } from "zod"; -// import { admins } from "./admin"; -import { users } from "./user"; - -const randomImages = [ - "/avatars/avatar-1.png", - "/avatars/avatar-2.png", - "/avatars/avatar-3.png", - "/avatars/avatar-4.png", - "/avatars/avatar-5.png", - "/avatars/avatar-6.png", - "/avatars/avatar-7.png", - "/avatars/avatar-8.png", - "/avatars/avatar-9.png", - "/avatars/avatar-10.png", - "/avatars/avatar-11.png", - "/avatars/avatar-12.png", -]; - -const generateRandomImage = () => { - return ( - randomImages[ - // @ts-ignore - getRandomValues(new Uint32Array(1))[0] % randomImages.length - ] || "/avatars/avatar-1.png" - ); -}; -export type DatabaseUser = typeof auth.$inferSelect; -export const roles = pgEnum("Roles", ["admin", "user"]); - -export const auth = pgTable("auth", { - id: text("id") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - email: text("email").notNull().unique(), - password: text("password").notNull(), - rol: roles("rol").notNull(), - image: text("image").$defaultFn(() => generateRandomImage()), - secret: text("secret"), - token: text("token"), - is2FAEnabled: boolean("is2FAEnabled").notNull().default(false), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), - resetPasswordToken: text("resetPasswordToken"), - resetPasswordExpiresAt: text("resetPasswordExpiresAt"), - confirmationToken: text("confirmationToken"), - confirmationExpiresAt: text("confirmationExpiresAt"), -}); - -export const authRelations = relations(auth, ({ many }) => ({ - // admins: many(admins), - users: many(users), -})); -const createSchema = createInsertSchema(auth, { - email: z.string().email(), - password: z.string().min(8), - rol: z.enum(["admin", "user"]), - image: z.string().optional(), -}); - -export const apiCreateAdmin = createSchema.pick({ - email: true, - password: true, -}); - -export const apiCreateUser = createSchema - .pick({ - password: true, - id: true, - token: true, - }) - .required() - .extend({ - token: z.string().min(1), - }); - -export const apiLogin = createSchema - .pick({ - email: true, - password: true, - }) - .required(); - -export const apiUpdateAuth = createSchema.partial().extend({ - email: z.string().nullable(), - password: z.string().nullable(), - image: z.string().optional(), - currentPassword: z.string().nullable(), -}); - -export const apiUpdateAuthByAdmin = createSchema.partial().extend({ - email: z.string().nullable(), - password: z.string().nullable(), - image: z.string().optional(), - id: z.string().min(1), -}); - -export const apiFindOneAuth = createSchema - .pick({ - id: true, - }) - .required(); - -export const apiVerify2FA = createSchema - .extend({ - pin: z.string().min(6), - secret: z.string().min(1), - }) - .pick({ - pin: true, - secret: true, - }) - .required(); - -export const apiVerifyLogin2FA = createSchema - .extend({ - pin: z.string().min(6), - }) - .pick({ - pin: true, - id: true, - }) - .required(); diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index dd121b504..80d533508 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -4,10 +4,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { organization } from "./account"; -import { admins } from "./admin"; import { server } from "./server"; -import { users_temp } from "./user"; -// import { user } from "./user"; import { generateAppName } from "./utils"; export const certificates = pgTable("certificate", { diff --git a/packages/server/src/db/schema/deployment.ts b/packages/server/src/db/schema/deployment.ts index 1be5db5ef..4dfed76b6 100644 --- a/packages/server/src/db/schema/deployment.ts +++ b/packages/server/src/db/schema/deployment.ts @@ -1,4 +1,4 @@ -import { is, relations } from "drizzle-orm"; +import { relations } from "drizzle-orm"; import { type AnyPgColumn, boolean, diff --git a/packages/server/src/db/schema/destination.ts b/packages/server/src/db/schema/destination.ts index 4d8dbc13d..0aeb14902 100644 --- a/packages/server/src/db/schema/destination.ts +++ b/packages/server/src/db/schema/destination.ts @@ -4,10 +4,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { organization } from "./account"; -import { admins } from "./admin"; import { backups } from "./backups"; -import { users_temp } from "./user"; -// import { user } from "./user"; export const destinations = pgTable("destination", { destinationId: text("destinationId") @@ -20,7 +17,6 @@ export const destinations = pgTable("destination", { secretAccessKey: text("secretAccessKey").notNull(), bucket: text("bucket").notNull(), region: text("region").notNull(), - // maybe it can be null endpoint: text("endpoint").notNull(), organizationId: text("organizationId") .notNull() @@ -35,10 +31,6 @@ export const destinationsRelations = relations( fields: [destinations.organizationId], references: [organization.id], }), - // user: one(user, { - // fields: [destinations.userId], - // references: [user.id], - // }), }), ); diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index e42557141..dc88131a9 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -4,12 +4,9 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { organization } from "./account"; -import { admins } from "./admin"; import { bitbucket } from "./bitbucket"; import { github } from "./github"; import { gitlab } from "./gitlab"; -import { users_temp } from "./user"; -// import { user } from "./user"; export const gitProviderType = pgEnum("gitProviderType", [ "github", diff --git a/packages/server/src/db/schema/index.ts b/packages/server/src/db/schema/index.ts index 405fa383b..5eb7f369e 100644 --- a/packages/server/src/db/schema/index.ts +++ b/packages/server/src/db/schema/index.ts @@ -1,8 +1,6 @@ export * from "./application"; export * from "./postgres"; export * from "./user"; -export * from "./admin"; -export * from "./auth"; export * from "./project"; export * from "./domain"; export * from "./mariadb"; diff --git a/packages/server/src/db/schema/notification.ts b/packages/server/src/db/schema/notification.ts index 0179f3c7c..4adce37d1 100644 --- a/packages/server/src/db/schema/notification.ts +++ b/packages/server/src/db/schema/notification.ts @@ -4,8 +4,6 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { organization } from "./account"; -import { users_temp } from "./user"; -// import { user } from "./user"; export const notificationType = pgEnum("notificationType", [ "slack", diff --git a/packages/server/src/db/schema/project.ts b/packages/server/src/db/schema/project.ts index cf4ea8a86..deeba4aca 100644 --- a/packages/server/src/db/schema/project.ts +++ b/packages/server/src/db/schema/project.ts @@ -1,12 +1,9 @@ import { relations } from "drizzle-orm"; - import { pgTable, text } from "drizzle-orm/pg-core"; import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { organization } from "./account"; -import { admins } from "./admin"; -// import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { mariadb } from "./mariadb"; @@ -14,7 +11,6 @@ import { mongo } from "./mongo"; import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; -import { users, users_temp } from "./user"; export const projects = pgTable("project", { projectId: text("projectId") diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index aa362a050..35526f90c 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -4,10 +4,7 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { organization } from "./account"; -import { admins } from "./admin"; import { applications } from "./application"; -import { users_temp } from "./user"; -// import { user } from "./user"; /** * This is an example of how to use the multi-project schema feature of Drizzle ORM. Use the same * database instance for multiple projects. @@ -36,10 +33,6 @@ export const registry = pgTable("registry", { }); export const registryRelations = relations(registry, ({ one, many }) => ({ - // user: one(user, { - // fields: [registry.userId], - // references: [user.id], - // }), applications: many(applications), })); diff --git a/packages/server/src/db/schema/server.ts b/packages/server/src/db/schema/server.ts index c94fd693e..26bb46326 100644 --- a/packages/server/src/db/schema/server.ts +++ b/packages/server/src/db/schema/server.ts @@ -10,9 +10,7 @@ import { import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; - import { organization } from "./account"; -import { admins } from "./admin"; import { applications } from "./application"; import { certificates } from "./certificate"; import { compose } from "./compose"; @@ -23,8 +21,6 @@ import { mysql } from "./mysql"; import { postgres } from "./postgres"; import { redis } from "./redis"; import { sshKeys } from "./ssh-key"; -import { users_temp } from "./user"; -// import { user } from "./user"; import { generateAppName } from "./utils"; export const serverStatus = pgEnum("serverStatus", ["active", "inactive"]); @@ -101,10 +97,6 @@ export const server = pgTable("server", { }); export const serverRelations = relations(server, ({ one, many }) => ({ - // user: one(user, { - // fields: [server.userId], - // references: [user.id], - // }), deployments: many(deployments), sshKey: one(sshKeys, { fields: [server.sshKeyId], diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index 4e9d28835..99df9218e 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -1,6 +1,4 @@ -import { sql } from "drizzle-orm"; import { pgTable, text, timestamp } from "drizzle-orm/pg-core"; -import { auth } from "./auth"; import { users_temp } from "./user"; // OLD TABLE @@ -18,14 +16,3 @@ export const session = pgTable("session_temp", { impersonatedBy: text("impersonated_by"), activeOrganizationId: text("active_organization_id"), }); - -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(), -}); diff --git a/packages/server/src/db/schema/source.ts b/packages/server/src/db/schema/source.ts deleted file mode 100644 index 6618ced77..000000000 --- a/packages/server/src/db/schema/source.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { pgTable, text } from "drizzle-orm/pg-core"; -import { createInsertSchema } from "drizzle-zod"; -import { nanoid } from "nanoid"; -import { z } from "zod"; - -export const source = pgTable("project", { - projectId: text("projectId") - .notNull() - .primaryKey() - .$defaultFn(() => nanoid()), - name: text("name").notNull(), - description: text("description"), - createdAt: text("createdAt") - .notNull() - .$defaultFn(() => new Date().toISOString()), -}); - -const createSchema = createInsertSchema(source, { - name: z.string().min(1), - description: z.string(), - projectId: z.string(), -}); - -export const apiCreate = createSchema.pick({ - name: true, - description: true, -}); diff --git a/packages/server/src/db/schema/ssh-key.ts b/packages/server/src/db/schema/ssh-key.ts index b705be36e..8a66d6d9d 100644 --- a/packages/server/src/db/schema/ssh-key.ts +++ b/packages/server/src/db/schema/ssh-key.ts @@ -4,12 +4,9 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { sshKeyCreate, sshKeyType } from "../validations"; import { organization } from "./account"; -import { admins } from "./admin"; import { applications } from "./application"; import { compose } from "./compose"; import { server } from "./server"; -import { users_temp } from "./user"; -// import { user } from "./user"; export const sshKeys = pgTable("ssh-key", { sshKeyId: text("sshKeyId") diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index e2755b8f9..33e9e4fcd 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -11,8 +11,6 @@ import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; import { account, organization } from "./account"; -import { admins } from "./admin"; -import { auth } from "./auth"; import { projects } from "./project"; import { certificateType } from "./shared"; /** @@ -24,50 +22,6 @@ import { certificateType } from "./shared"; // OLD TABLE -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), - canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), - canCreateServices: boolean("canCreateServices").notNull().default(false), - canDeleteProjects: boolean("canDeleteProjects").notNull().default(false), - canDeleteServices: boolean("canDeleteServices").notNull().default(false), - canAccessToDocker: boolean("canAccessToDocker").notNull().default(false), - canAccessToAPI: boolean("canAccessToAPI").notNull().default(false), - canAccessToGitProviders: boolean("canAccessToGitProviders") - .notNull() - .default(false), - canAccessToTraefikFiles: boolean("canAccessToTraefikFiles") - .notNull() - .default(false), - accessedProjects: text("accesedProjects") - .array() - .notNull() - .default(sql`ARRAY[]::text[]`), - accessedServices: text("accesedServices") - .array() - .notNull() - .default(sql`ARRAY[]::text[]`), - adminId: text("adminId") - .notNull() - .references(() => admins.adminId, { onDelete: "cascade" }), - authId: text("authId") - .notNull() - .references(() => auth.id, { onDelete: "cascade" }), -}); - // TEMP export const users_temp = pgTable("user_temp", { id: text("id") @@ -187,19 +141,11 @@ export const users_temp = pgTable("user_temp", { }); export const usersRelations = relations(users_temp, ({ one, many }) => ({ - // auth: one(auth, { - // fields: [users.authId], - // references: [auth.id], - // }), account: one(account, { fields: [users_temp.id], references: [account.userId], }), organizations: many(organization), - // admin: one(admins, { - // fields: [users.adminId], - // references: [admins.adminId], - // }), projects: many(projects), })); @@ -263,3 +209,91 @@ export const apiFindOneUserByAuth = createSchema // authId: true, }) .required(); +export const apiSaveSSHKey = createSchema + .pick({ + sshPrivateKey: true, + }) + .required(); + +export const apiAssignDomain = createSchema + .pick({ + host: true, + certificateType: true, + letsEncryptEmail: true, + }) + .required() + .partial({ + letsEncryptEmail: true, + }); + +export const apiUpdateDockerCleanup = createSchema + .pick({ + enableDockerCleanup: true, + }) + .required() + .extend({ + serverId: z.string().optional(), + }); + +export const apiTraefikConfig = z.object({ + traefikConfig: z.string().min(1), +}); + +export const apiModifyTraefikConfig = z.object({ + path: z.string().min(1), + traefikConfig: z.string().min(1), + serverId: z.string().optional(), +}); +export const apiReadTraefikConfig = z.object({ + path: z.string().min(1), + serverId: z.string().optional(), +}); + +export const apiEnableDashboard = z.object({ + enableDashboard: z.boolean().optional(), + serverId: z.string().optional(), +}); + +export const apiServerSchema = z + .object({ + serverId: z.string().optional(), + }) + .optional(); + +export const apiReadStatsLogs = z.object({ + page: z + .object({ + pageIndex: z.number(), + pageSize: z.number(), + }) + .optional(), + status: z.string().array().optional(), + search: z.string().optional(), + sort: z.object({ id: z.string(), desc: z.boolean() }).optional(), +}); + +export const apiUpdateWebServerMonitoring = z.object({ + metricsConfig: z + .object({ + server: z.object({ + refreshRate: z.number().min(2), + port: z.number().min(1), + token: z.string(), + urlCallback: z.string().url(), + retentionDays: z.number().min(1), + cronJob: z.string().min(1), + thresholds: z.object({ + cpu: z.number().min(0), + memory: z.number().min(0), + }), + }), + containers: z.object({ + refreshRate: z.number().min(2), + services: z.object({ + include: z.array(z.string()).optional(), + exclude: z.array(z.string()).optional(), + }), + }), + }) + .required(), +}); diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 41b92587f..4e1b1bb33 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -2,9 +2,7 @@ import { randomBytes } from "node:crypto"; import { db } from "@dokploy/server/db"; import { account, - admins, type apiCreateUserInvitation, - auth, member, organization, users_temp, diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 74cd04197..dbdf538b8 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -1,12 +1,6 @@ import { randomBytes } from "node:crypto"; import { db } from "@dokploy/server/db"; -import { - admins, - type apiCreateAdmin, - type apiCreateUser, - auth, - users_temp, -} from "@dokploy/server/db/schema"; +import { users_temp } from "@dokploy/server/db/schema"; import { getPublicIpWithFallback } from "@dokploy/server/wss/utils"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; @@ -17,89 +11,6 @@ import QRCode from "qrcode"; import { IS_CLOUD } from "../constants"; import { findUserById } from "./admin"; -export type Auth = typeof auth.$inferSelect; - -export const createAdmin = async (input: typeof apiCreateAdmin._type) => { - return await db.transaction(async (tx) => { - const hashedPassword = bcrypt.hashSync(input.password, 10); - const newAuth = await tx - .insert(auth) - .values({ - email: input.email.toLowerCase(), - password: hashedPassword, - rol: "admin", - }) - .returning() - .then((res) => res[0]); - - if (!newAuth) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - }); - } - - await tx - .insert(admins) - .values({ - authId: newAuth.id, - ...(!IS_CLOUD && { - serverIp: - process.env.ADVERTISE_ADDR || (await getPublicIpWithFallback()), - }), - }) - .returning(); - - return newAuth; - }); -}; - -export const createUser = async (input: typeof apiCreateUser._type) => { - return await db.transaction(async (tx) => { - const hashedPassword = bcrypt.hashSync(input.password, 10); - const res = await tx - .update(auth) - .set({ - password: hashedPassword, - }) - .where(eq(auth.id, input.id)) - .returning() - .then((res) => res[0]); - - if (!res) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - }); - } - - const user = await tx - .update(users) - .set({ - isRegistered: true, - expirationDate: undefined, - }) - .where(eq(users.token, input.token)) - .returning() - .then((res) => res[0]); - - return user; - }); -}; - -export const findAuthByEmail = async (email: string) => { - const result = await db.query.auth.findFirst({ - where: eq(auth.email, email), - }); - if (!result) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - return result; -}; - export const findAuthById = async (authId: string) => { const result = await db.query.users_temp.findFirst({ where: eq(users_temp.id, authId), @@ -117,21 +28,6 @@ export const findAuthById = async (authId: string) => { return result; }; -export const updateAuthById = async ( - authId: string, - authData: Partial, -) => { - const result = await db - .update(auth) - .set({ - ...authData, - }) - .where(eq(auth.id, authId)) - .returning(); - - return result[0]; -}; - export const generate2FASecret = async (userId: string) => { const user = await findUserById(userId); From 90156da5709214b6d0a7463612e4433b0bc015e4 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 14:11:47 -0600 Subject: [PATCH 057/126] refactor: remove tables --- .../server/update-server-config.test.ts | 28 +- apps/dokploy/__test__/traefik/traefik.test.ts | 2 +- .../components/dashboard/projects/show.tsx | 12 +- .../servers/welcome-stripe/create-ssh-key.tsx | 1 + apps/dokploy/drizzle/0073_brave_wolfpack.sql | 5 + apps/dokploy/drizzle/meta/0073_snapshot.json | 4821 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + .../pages/dashboard/settings/profile.tsx | 12 +- .../server/src/utils/traefik/web-server.ts | 6 +- 9 files changed, 4869 insertions(+), 25 deletions(-) create mode 100644 apps/dokploy/drizzle/0073_brave_wolfpack.sql create mode 100644 apps/dokploy/drizzle/meta/0073_snapshot.json diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index f7bf3595a..458266dc2 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -5,7 +5,7 @@ vi.mock("node:fs", () => ({ default: fs, })); -import type { Admin, FileConfig, User } from "@dokploy/server"; +import type { FileConfig, User } from "@dokploy/server"; import { createDefaultServerTraefikConfig, loadOrCreateConfig, @@ -13,7 +13,7 @@ import { } from "@dokploy/server"; import { beforeEach, expect, test, vi } from "vitest"; -const baseAdmin: Partial = { +const baseAdmin: User = { enablePaidFeatures: false, metricsConfig: { containers: { @@ -51,6 +51,30 @@ const baseAdmin: Partial = { serversQuantity: 0, stripeCustomerId: "", stripeSubscriptionId: "", + accessedProjects: [], + accessedServices: [], + banExpires: new Date(), + banned: true, + banReason: "", + canAccessToAPI: false, + canCreateProjects: false, + canDeleteProjects: false, + canDeleteServices: false, + canAccessToDocker: false, + canAccessToSSHKeys: false, + canCreateServices: false, + canAccessToTraefikFiles: false, + canAccessToGitProviders: false, + email: "", + expirationDate: "", + id: "", + isRegistered: false, + name: "", + createdAt2: new Date().toISOString(), + emailVerified: false, + image: "", + token: "", + updatedAt: new Date(), }; beforeEach(() => { diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index d05dda81d..955103dec 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -26,7 +26,7 @@ const baseApp: ApplicationNested = { previewWildcard: "", project: { env: "", - adminId: "", + organizationId: "", name: "", description: "", createdAt: "", diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index 206137f56..b9c96d314 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -52,14 +52,6 @@ export const ShowProjects = () => { const utils = api.useUtils(); const { data, isLoading } = api.project.all.useQuery(); const { data: auth } = api.auth.get.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); const { mutateAsync } = api.project.remove.useMutation(); const [searchQuery, setSearchQuery] = useState(""); @@ -91,7 +83,7 @@ export const ShowProjects = () => { - {(auth?.role === "owner" || user?.canCreateProjects) && ( + {(auth?.role === "owner" || auth?.user?.canCreateProjects) && (
@@ -294,7 +286,7 @@ export const ShowProjects = () => { onClick={(e) => e.stopPropagation()} > {(auth?.role === "owner" || - user?.canDeleteProjects) && ( + auth?.user?.canDeleteProjects) && ( { description: "Used on Dokploy Cloud", privateKey: keys.privateKey, publicKey: keys.publicKey, + organizationId: "", }); await refetch(); } catch (error) { diff --git a/apps/dokploy/drizzle/0073_brave_wolfpack.sql b/apps/dokploy/drizzle/0073_brave_wolfpack.sql new file mode 100644 index 000000000..05bf5cb47 --- /dev/null +++ b/apps/dokploy/drizzle/0073_brave_wolfpack.sql @@ -0,0 +1,5 @@ +DROP TABLE "user" CASCADE;--> statement-breakpoint +DROP TABLE "admin" CASCADE;--> statement-breakpoint +DROP TABLE "auth" CASCADE;--> statement-breakpoint +DROP TABLE "session" CASCADE;--> statement-breakpoint +DROP TYPE "public"."Roles"; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0073_snapshot.json b/apps/dokploy/drizzle/meta/0073_snapshot.json new file mode 100644 index 000000000..e61b447ca --- /dev/null +++ b/apps/dokploy/drizzle/meta/0073_snapshot.json @@ -0,0 +1,4821 @@ +{ + "id": "07170d9f-4d67-48f5-890f-393043396973", + "prevId": "4eb71c0e-5bdb-427b-b198-39b1059dcd16", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index aae9728d3..b591cceda 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -512,6 +512,13 @@ "when": 1739672367223, "tag": "0072_lazy_pixie", "breakpoints": true + }, + { + "idx": 73, + "version": "7", + "when": 1739735739336, + "tag": "0073_brave_wolfpack", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 73b90c722..bfae14e62 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -14,21 +14,15 @@ import superjson from "superjson"; const Page = () => { const { data } = api.auth.get.useQuery(); - const { data: user } = api.user.get.useQuery( - { - authId: data?.id || "", - }, - { - enabled: !!data?.id && data?.role === "member", - }, - ); const { data: isCloud } = api.settings.isCloud.useQuery(); return (
- {(user?.canAccessToAPI || data?.role === "owner") && } + {(data?.user?.canAccessToAPI || data?.role === "owner") && ( + + )} {isCloud && }
diff --git a/packages/server/src/utils/traefik/web-server.ts b/packages/server/src/utils/traefik/web-server.ts index 0aa4d35d5..76733e751 100644 --- a/packages/server/src/utils/traefik/web-server.ts +++ b/packages/server/src/utils/traefik/web-server.ts @@ -1,14 +1,14 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; -import type { Admin } from "@dokploy/server/services/admin"; import { dump, load } from "js-yaml"; import { loadOrCreateConfig, writeTraefikConfig } from "./application"; import type { FileConfig } from "./file-types"; import type { MainTraefikConfig } from "./types"; +import type { User } from "@dokploy/server/services/user"; export const updateServerTraefik = ( - admin: Admin | null, + user: User | null, newHost: string | null, ) => { const appName = "dokploy"; @@ -22,7 +22,7 @@ export const updateServerTraefik = ( if (currentRouterConfig && newHost) { currentRouterConfig.rule = `Host(\`${newHost}\`)`; - if (admin?.certificateType === "letsencrypt") { + if (user?.certificateType === "letsencrypt") { config.http.routers[`${appName}-router-app-secure`] = { ...currentRouterConfig, entryPoints: ["websecure"], From 48c4ec55f90c953b62e8a6b56c68c95e46908ebc Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sat, 15 Feb 2025 23:31:24 -0500 Subject: [PATCH 058/126] fix(template): switch outline to png for dark mode --- apps/dokploy/public/templates/outline.png | Bin 0 -> 2158 bytes apps/dokploy/public/templates/outline.svg | 1 - .../dokploy/templates/outline/docker-compose.yml | 2 +- apps/dokploy/templates/templates.ts | 4 ++-- 4 files changed, 3 insertions(+), 4 deletions(-) create mode 100644 apps/dokploy/public/templates/outline.png delete mode 100644 apps/dokploy/public/templates/outline.svg diff --git a/apps/dokploy/public/templates/outline.png b/apps/dokploy/public/templates/outline.png new file mode 100644 index 0000000000000000000000000000000000000000..b241f01d77ba1fc7f829ff3a577a94b69136ee8b GIT binary patch literal 2158 zcmcIl`CF5B5}!9A31GxXMG=w^v=QXcAS#GJ;>uQ#DtHn>G>9&72^c`}BFSrM70_ZI zs$dl~D+*L@EK!a^ST1cUQV};*0x=OhV#o$axkF$3H|#$1!^|`D`Ofvs%r`T9`#$g?0P0;8E>Z?5?-_=NEBLjN`tqI!#|F&PFBFQ<@rdbhlJvH_li;+t0Jl`| zoj&wn)Ey~HoA4ZC& zBQb>al00t#ff3ZMF<;%-_3zEpfz+Sx;UK+STAOAnC$iWhNmT8{%Cc!YBFq@cvO_-> zY%i`Uv*vKKF3@!1@0Z&GF=}J}@G&B9=uGd)SD?1-im}fPZKiXMY_tbC&zE&xda@-1J~GxJla;#*TdN(ReN^KhIF|_KFufS^Z61m-ubfUxLB85*CxU7s%lAS zL9@BBTf1d#c5EZrGf;iPcyrTC!YN=FdGk=yHwRK$y}*hX1p-OoQg}j^0hb=D?XyB< zn9k#jSWINhVZf=?SZcqd5kIsU>kv-uGEc;WqN9;?lJ^1`xayGb=Bd$J!+4M`?8#S&d> z%{agRFV4GOq@xNDkW1>>19__t%4TU5o!X=ukE?%bZ7>s~^mK%ZG>K>Hdsll^+@gk_ zou`}b=as6Kqs!&vhJk`%OyqbROfOHp4E@A)N^I($G{qoQ33JDHw$>8VgM)Xcy;cr7 zH)zJUU1{4us3Dbbr&5?n0O8eR2uSTfad!d0x4>N`0^nQ7li%ZkgNNKuCjbWsPpVxB z@rc?AFTYF433dJ){(oeF{trp6*C&h*-x;~^>b227AYj%nJ*RQlCtjnAxbhi?ke=fY zP;w|dhXk;PymbUXnzbo}4zSzH^)Vh$HzM;i)}eOk8!~`}{Ez6K&$-Df+eSxITtwGG zof8ui`;$&TO{=LGD|Y<@3{70U*(|ov}8bAnqqNP^!E@N`oC` z&l7F%;9x-s@b^D%H%QM%fHTWwjGOHNhGykcriGcrIJl%e6?kiPUMM*YN}7)a{N4d* zduUjG2Hoa@);O`t9y}HhT3N?UdVgQM6h{Ph3}wl$gMIc8z7Q@RHEJ$BQj<}xZ6>0` zRC6y<-C?ozE8qAwK)F`s2se=&i3O&(bUZ=0tc)Z7l=Q7YPvg5+po1OKmX@VpY{c^< zeXi)_pcDE9DGu811E(8Q9M6f($2x zN)JNygiE0XjNNYfrLYHW^~=I+DMf$qWSh{Dhhx_B8_jyA|p86B{&CQQ#&VV;eCp zT2$&13?Hw5d+uy1!tzyEGsKr*_hDf!mGf~Klsr;LRDG7`=FI-C5BYcXN1!H`b01h$Y(Qh{yIfO^A)odgA=P zY69Bw!$uiYd6n>~o!DTqgpUKUpeCpLs~|^zSygR=d2ipceG$?<)WImC=x@&AdBRk` zf-K;t#8UFJAlTnGrFj|V&G7KU>q1qfB@pdyRo!62PsxTuWz$Ut_6RKs_{NyRlsv_t i$|u1J``@kLy`=Yc-sU1JeJD225c2o!K^1@Fef1~y9Dh>) literal 0 HcmV?d00001 diff --git a/apps/dokploy/public/templates/outline.svg b/apps/dokploy/public/templates/outline.svg deleted file mode 100644 index 5dfa63d02..000000000 --- a/apps/dokploy/public/templates/outline.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/apps/dokploy/templates/outline/docker-compose.yml b/apps/dokploy/templates/outline/docker-compose.yml index 90ec6d975..aaf98ac0b 100644 --- a/apps/dokploy/templates/outline/docker-compose.yml +++ b/apps/dokploy/templates/outline/docker-compose.yml @@ -1,6 +1,6 @@ services: outline: - image: outlinewiki/outline:0.81.0 + image: outlinewiki/outline:0.82.0 restart: always depends_on: - postgres diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index f1900ec28..31668a6f9 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -20,7 +20,7 @@ export const templates: TemplateData[] = [ { id: "outline", name: "Outline", - version: "0.81.0", + version: "0.82.0", description: "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", links: { @@ -28,7 +28,7 @@ export const templates: TemplateData[] = [ website: "https://outline.com/", docs: "https://docs.outline.com/", }, - logo: "outline.svg", + logo: "outline.png", load: () => import("./outline/index").then((m) => m.generate), tags: ["documentation", "knowledge-base", "self-hosted"], }, From e1632cbdb3cb6688be1dda359585cd2e985a40f3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:32:57 -0600 Subject: [PATCH 059/126] refactor: update user and authentication schema with two-factor support --- .../settings/users/add-permissions.tsx | 4 +- .../dashboard/settings/users/show-users.tsx | 8 +- .../settings/web-server/update-server-ip.tsx | 5 +- apps/dokploy/drizzle/0066_yielding_echo.sql | 11 +- ...fpack.sql => 0073_polite_miss_america.sql} | 1 + apps/dokploy/drizzle/meta/0066_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0067_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0068_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0069_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0070_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0071_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0072_snapshot.json | 57 +++++++++ apps/dokploy/drizzle/meta/0073_snapshot.json | 59 ++++++++- apps/dokploy/drizzle/meta/_journal.json | 4 +- apps/dokploy/pages/dashboard/docker.tsx | 2 +- .../dashboard/settings/git-providers.tsx | 2 +- .../pages/dashboard/settings/profile.tsx | 2 +- .../pages/dashboard/settings/ssh-keys.tsx | 2 +- apps/dokploy/pages/dashboard/swarm.tsx | 2 +- apps/dokploy/pages/dashboard/traefik.tsx | 2 +- apps/dokploy/pages/swagger.tsx | 12 +- apps/dokploy/server/api/routers/admin.ts | 27 ++-- apps/dokploy/server/api/routers/auth.ts | 11 +- apps/dokploy/server/api/routers/settings.ts | 50 ++++---- apps/dokploy/server/api/routers/user.ts | 46 ++++--- packages/server/auth-schema.ts | 14 ++- packages/server/src/db/schema/account.ts | 9 ++ packages/server/src/db/schema/user.ts | 31 ++++- packages/server/src/lib/auth.ts | 7 +- packages/server/src/services/admin.ts | 117 +++++------------- packages/server/src/services/auth.ts | 7 +- packages/server/src/utils/backups/index.ts | 1 - .../server/src/utils/traefik/web-server.ts | 2 +- 33 files changed, 657 insertions(+), 180 deletions(-) rename apps/dokploy/drizzle/{0073_brave_wolfpack.sql => 0073_polite_miss_america.sql} (90%) diff --git a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx index 7c1f50375..bde2e71ca 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-permissions.tsx @@ -52,7 +52,7 @@ interface Props { export const AddUserPermissions = ({ userId }: Props) => { const { data: projects } = api.project.all.useQuery(); - const { data, refetch } = api.user.byUserId.useQuery( + const { data, refetch } = api.auth.one.useQuery( { userId, }, @@ -92,7 +92,7 @@ export const AddUserPermissions = ({ userId }: Props) => { const onSubmit = async (data: AddPermissions) => { await mutateAsync({ - userId, + id: userId, canCreateServices: data.canCreateServices, canCreateProjects: data.canCreateProjects, canDeleteServices: data.canDeleteServices, diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 8aa2a37b4..e0ffac139 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -104,9 +104,9 @@ export const ShowUsers = () => { - {user.user.is2FAEnabled + {/* {user.user.is2FAEnabled ? "2FA Enabled" - : "2FA Not Enabled"} + : "2FA Not Enabled"} */} {/* @@ -156,7 +156,7 @@ export const ShowUsers = () => { /> )} */} - {user.role !== "owner" && ( + {/* {user.role !== "owner" && ( { Delete User - )} + )} */} diff --git a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx index dd3f81fdc..07103d5ba 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx @@ -1,5 +1,4 @@ import { AlertBlock } from "@/components/shared/alert-block"; -import { CodeEditor } from "@/components/shared/code-editor"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -47,11 +46,11 @@ interface Props { export const UpdateServerIp = ({ children, serverId }: Props) => { const [isOpen, setIsOpen] = useState(false); - const { data } = api.admin.one.useQuery(); + const { data } = api.user.get.useQuery(); const { data: ip } = api.server.publicIp.useQuery(); const { mutateAsync, isLoading, error, isError } = - api.admin.update.useMutation(); + api.user.update.useMutation(); const form = useForm({ defaultValues: { diff --git a/apps/dokploy/drizzle/0066_yielding_echo.sql b/apps/dokploy/drizzle/0066_yielding_echo.sql index c66e2318b..d69a4c659 100644 --- a/apps/dokploy/drizzle/0066_yielding_echo.sql +++ b/apps/dokploy/drizzle/0066_yielding_echo.sql @@ -16,6 +16,7 @@ CREATE TABLE "user_temp" ( "canAccessToTraefikFiles" boolean DEFAULT false NOT NULL, "accesedProjects" text[] DEFAULT ARRAY[]::text[] NOT NULL, "accesedServices" text[] DEFAULT ARRAY[]::text[] NOT NULL, + "two_factor_enabled" boolean DEFAULT false NOT NULL, "email" text NOT NULL, "email_verified" boolean NOT NULL, "image" text, @@ -113,6 +114,13 @@ CREATE TABLE "verification" ( "created_at" timestamp, "updated_at" timestamp ); + +CREATE TABLE "two_factor" ( + "id" text PRIMARY KEY NOT NULL, + "secret" text NOT NULL, + "backup_codes" text NOT NULL, + "user_id" text NOT NULL +); --> statement-breakpoint ALTER TABLE "certificate" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint ALTER TABLE "notification" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint @@ -124,4 +132,5 @@ ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_temp_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint -ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action; \ No newline at end of file +ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action; +ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint diff --git a/apps/dokploy/drizzle/0073_brave_wolfpack.sql b/apps/dokploy/drizzle/0073_polite_miss_america.sql similarity index 90% rename from apps/dokploy/drizzle/0073_brave_wolfpack.sql rename to apps/dokploy/drizzle/0073_polite_miss_america.sql index 05bf5cb47..030f8a883 100644 --- a/apps/dokploy/drizzle/0073_brave_wolfpack.sql +++ b/apps/dokploy/drizzle/0073_polite_miss_america.sql @@ -1,3 +1,4 @@ +--> statement-breakpoint DROP TABLE "user" CASCADE;--> statement-breakpoint DROP TABLE "admin" CASCADE;--> statement-breakpoint DROP TABLE "auth" CASCADE;--> statement-breakpoint diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json index bcb7807a7..06b899346 100644 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -1010,6 +1010,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5045,6 +5051,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0067_snapshot.json b/apps/dokploy/drizzle/meta/0067_snapshot.json index 69d4a4ee8..be43b406b 100644 --- a/apps/dokploy/drizzle/meta/0067_snapshot.json +++ b/apps/dokploy/drizzle/meta/0067_snapshot.json @@ -1010,6 +1010,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5045,6 +5051,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0068_snapshot.json b/apps/dokploy/drizzle/meta/0068_snapshot.json index 02e33084f..2139b1321 100644 --- a/apps/dokploy/drizzle/meta/0068_snapshot.json +++ b/apps/dokploy/drizzle/meta/0068_snapshot.json @@ -1010,6 +1010,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5045,6 +5051,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0069_snapshot.json b/apps/dokploy/drizzle/meta/0069_snapshot.json index 41b53582e..d3e5be98b 100644 --- a/apps/dokploy/drizzle/meta/0069_snapshot.json +++ b/apps/dokploy/drizzle/meta/0069_snapshot.json @@ -1018,6 +1018,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5053,6 +5059,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0070_snapshot.json b/apps/dokploy/drizzle/meta/0070_snapshot.json index 681bded7e..e3864a19c 100644 --- a/apps/dokploy/drizzle/meta/0070_snapshot.json +++ b/apps/dokploy/drizzle/meta/0070_snapshot.json @@ -1018,6 +1018,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5205,6 +5211,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0071_snapshot.json b/apps/dokploy/drizzle/meta/0071_snapshot.json index 4214343a0..cce94ce93 100644 --- a/apps/dokploy/drizzle/meta/0071_snapshot.json +++ b/apps/dokploy/drizzle/meta/0071_snapshot.json @@ -1018,6 +1018,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5205,6 +5211,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0072_snapshot.json b/apps/dokploy/drizzle/meta/0072_snapshot.json index 2f38d98d9..53797c0b9 100644 --- a/apps/dokploy/drizzle/meta/0072_snapshot.json +++ b/apps/dokploy/drizzle/meta/0072_snapshot.json @@ -1018,6 +1018,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -5053,6 +5059,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/0073_snapshot.json b/apps/dokploy/drizzle/meta/0073_snapshot.json index e61b447ca..6b65df2f7 100644 --- a/apps/dokploy/drizzle/meta/0073_snapshot.json +++ b/apps/dokploy/drizzle/meta/0073_snapshot.json @@ -1,5 +1,5 @@ { - "id": "07170d9f-4d67-48f5-890f-393043396973", + "id": "e357a19a-dd1e-4843-b567-0c0243ade7a8", "prevId": "4eb71c0e-5bdb-427b-b198-39b1059dcd16", "version": "7", "dialect": "postgresql", @@ -858,6 +858,12 @@ "notNull": true, "default": "ARRAY[]::text[]" }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, "email": { "name": "email", "type": "text", @@ -4602,6 +4608,57 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.verification": { "name": "verification", "schema": "", diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index b591cceda..39903f65a 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -516,8 +516,8 @@ { "idx": 73, "version": "7", - "when": 1739735739336, - "tag": "0073_brave_wolfpack", + "when": 1739740193879, + "tag": "0073_polite_miss_america", "breakpoints": true } ] diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index 202935aa6..fee202fce 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -54,7 +54,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); if (user.role === "member") { - const userR = await helpers.user.get.fetch({ + const userR = await helpers.user.one.fetch({ userId: user.id, }); diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index d0d9ca7f1..cfded9915 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -50,7 +50,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); await helpers.settings.isCloud.prefetch(); if (user.role === "member") { - const userR = await helpers.user.get.fetch({ + const userR = await helpers.user.one.fetch({ userId: user.id, }); diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index bfae14e62..a84fb4dba 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -57,7 +57,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); await helpers.auth.get.prefetch(); if (user?.role === "member") { - // const userR = await helpers.user.get.fetch({ + // const userR = await helpers.user.one.fetch({ // userId: user.id, // }); // await helpers.user.byAuthId.prefetch({ diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index f3b9cf1b8..c97df7ba1 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -51,7 +51,7 @@ export async function getServerSideProps( await helpers.settings.isCloud.prefetch(); if (user.role === "member") { - const userR = await helpers.user.get.fetch({ + const userR = await helpers.user.one.fetch({ userId: user.id, }); diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index 8278ed181..3b59c47b0 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -54,7 +54,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); if (user.role === "member") { - const userR = await helpers.user.get.fetch({ + const userR = await helpers.user.one.fetch({ userId: user.id, }); diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 6939eabdf..8dcd3f084 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -54,7 +54,7 @@ export async function getServerSideProps( await helpers.project.all.prefetch(); if (user.role === "member") { - const userR = await helpers.user.get.fetch({ + const userR = await helpers.user.one.fetch({ userId: user.id, }); diff --git a/apps/dokploy/pages/swagger.tsx b/apps/dokploy/pages/swagger.tsx index 765194f1c..e4a6fac8d 100644 --- a/apps/dokploy/pages/swagger.tsx +++ b/apps/dokploy/pages/swagger.tsx @@ -38,7 +38,7 @@ const Home: NextPage = () => { export default Home; export async function getServerSideProps(context: GetServerSidePropsContext) { const { req, res } = context; - const { user, session } = await validateRequest(context.req, context.res); + const { user, session } = await validateRequest(context.req); if (!user) { return { redirect: { @@ -53,17 +53,17 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { req: req as any, res: res as any, db: null as any, - session: session, - user: user, + session: session as any, + user: user as any, }, transformer: superjson, }); if (user.role === "member") { - const result = await helpers.user.byAuthId.fetch({ - authId: user.id, + const userR = await helpers.user.one.fetch({ + userId: user.id, }); - if (!result.canAccessToAPI) { + if (!userR.canAccessToAPI) { return { redirect: { permanent: true, diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 695cdb336..97e3a1fa4 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -35,17 +35,22 @@ export const adminRouter = createTRPCRouter({ ...rest, }; }), - update: adminProcedure.mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "member") { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to update this admin", - }); - } - const { id } = await findUserById(ctx.user.id); - // @ts-ignore - return updateAdmin(id, input); - }), + update: adminProcedure + .input( + z.object({ + enableDockerCleanup: z.boolean(), + }), + ) + .mutation(async ({ input, ctx }) => { + if (ctx.user.rol === "member") { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not allowed to update this admin", + }); + } + const user = await findUserById(ctx.user.ownerId); + return updateUser(user.id, {}); + }), createUserInvitation: adminProcedure .input(apiCreateUserInvitation) .mutation(async ({ input, ctx }) => { diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index ad2fab07e..4cfbe71a2 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -266,10 +266,13 @@ export const authRouter = createTRPCRouter({ verifyToken: protectedProcedure.mutation(async () => { return true; }), - one: adminProcedure.query(async ({ input }) => { - const auth = await findAuthById(input.id); - return auth; - }), + one: adminProcedure + .input(z.object({ userId: z.string().min(1) })) + .query(async ({ input }) => { + // TODO: Check if the user is admin or member + const user = await findUserById(input.userId); + return user; + }), generate2FASecret: protectedProcedure.query(async ({ ctx }) => { return await generate2FASecret(ctx.user.id); diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index cd1f8bd3b..ee69da22d 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -22,9 +22,8 @@ import { cleanUpUnusedVolumes, execAsync, execAsyncRemote, - findAdmin, - findAdminById, findServerById, + findUserById, getDokployImage, getDokployImageTag, getUpdateData, @@ -50,6 +49,7 @@ import { updateLetsEncryptEmail, updateServerById, updateServerTraefik, + updateUser, writeConfig, writeMainConfig, writeTraefikConfigInPath, @@ -163,7 +163,7 @@ export const settingsRouter = createTRPCRouter({ if (IS_CLOUD) { return true; } - await updateAdmin(ctx.user.authId, { + await updateUser(ctx.user.id, { sshPrivateKey: input.sshPrivateKey, }); @@ -175,7 +175,7 @@ export const settingsRouter = createTRPCRouter({ if (IS_CLOUD) { return true; } - const admin = await updateAdmin(ctx.user.authId, { + const user = await updateUser(ctx.user.id, { host: input.host, ...(input.letsEncryptEmail && { letsEncryptEmail: input.letsEncryptEmail, @@ -183,25 +183,25 @@ export const settingsRouter = createTRPCRouter({ certificateType: input.certificateType, }); - if (!admin) { + if (!user) { throw new TRPCError({ code: "NOT_FOUND", - message: "Admin not found", + message: "User not found", }); } - updateServerTraefik(admin, input.host); + updateServerTraefik(user, input.host); if (input.letsEncryptEmail) { updateLetsEncryptEmail(input.letsEncryptEmail); } - return admin; + return user; }), cleanSSHPrivateKey: adminProcedure.mutation(async ({ ctx }) => { if (IS_CLOUD) { return true; } - await updateAdmin(ctx.user.authId, { + await updateUser(ctx.user.id, { sshPrivateKey: null, }); return true; @@ -216,7 +216,7 @@ export const settingsRouter = createTRPCRouter({ const server = await findServerById(input.serverId); - if (server.adminId !== ctx.user.adminId) { + if (server.organizationId !== ctx.session?.activeOrganizationId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this server", @@ -245,7 +245,7 @@ export const settingsRouter = createTRPCRouter({ await cleanUpUnusedImages(server.serverId); await cleanUpDockerBuilder(server.serverId); await cleanUpSystemPrune(server.serverId); - await sendDockerCleanupNotifications(server.adminId); + await sendDockerCleanupNotifications(server.organizationId); }); } } else { @@ -261,19 +261,11 @@ export const settingsRouter = createTRPCRouter({ } } } else if (!IS_CLOUD) { - const admin = await findAdminById(ctx.user.adminId); - - if (admin.adminId !== ctx.user.adminId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not authorized to access this admin", - }); - } - const adminUpdated = await updateAdmin(ctx.user.authId, { + const userUpdated = await updateUser(ctx.user.id, { enableDockerCleanup: input.enableDockerCleanup, }); - if (adminUpdated?.enableDockerCleanup) { + if (userUpdated?.enableDockerCleanup) { scheduleJob("docker-cleanup", "0 0 * * *", async () => { console.log( `Docker Cleanup ${new Date().toLocaleString()}] Running...`, @@ -281,7 +273,9 @@ export const settingsRouter = createTRPCRouter({ await cleanUpUnusedImages(); await cleanUpDockerBuilder(); await cleanUpSystemPrune(); - await sendDockerCleanupNotifications(admin.adminId); + await sendDockerCleanupNotifications( + ctx.session.activeOrganizationId, + ); }); } else { const currentJob = scheduledJobs["docker-cleanup"]; @@ -383,7 +377,7 @@ export const settingsRouter = createTRPCRouter({ .query(async ({ ctx, input }) => { try { if (ctx.user.rol === "member") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + const canAccess = await canAccessToTraefikFiles(ctx.user.id); if (!canAccess) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -401,7 +395,7 @@ export const settingsRouter = createTRPCRouter({ .input(apiModifyTraefikConfig) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + const canAccess = await canAccessToTraefikFiles(ctx.user.id); if (!canAccess) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -419,7 +413,7 @@ export const settingsRouter = createTRPCRouter({ .input(apiReadTraefikConfig) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - const canAccess = await canAccessToTraefikFiles(ctx.user.authId); + const canAccess = await canAccessToTraefikFiles(ctx.user.id); if (!canAccess) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -427,12 +421,12 @@ export const settingsRouter = createTRPCRouter({ } return readConfigInPath(input.path, input.serverId); }), - getIp: protectedProcedure.query(async () => { + getIp: protectedProcedure.query(async ({ ctx }) => { if (IS_CLOUD) { return true; } - const admin = await findAdmin(); - return admin.serverIp; + const user = await findUserById(ctx.user.ownerId); + return user.serverIp; }), getOpenApiDocument: protectedProcedure.query( diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index f4de4d9f7..addbdb23c 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -1,7 +1,12 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; -import { findUserByAuthId, findUserById } from "@dokploy/server"; +import { + findUserByAuthId, + findUserById, + updateUser, + verify2FA, +} from "@dokploy/server"; import { db } from "@dokploy/server/db"; -import { member } from "@dokploy/server/db/schema"; +import { apiUpdateUser, member } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { z } from "zod"; @@ -15,7 +20,7 @@ export const userRouter = createTRPCRouter({ }, }); }), - get: protectedProcedure + one: protectedProcedure .input( z.object({ userId: z.string(), @@ -31,16 +36,27 @@ export const userRouter = createTRPCRouter({ // } return user; }), - // byUserId: protectedProcedure - // .input(apiFindOneUser) - // .query(async ({ input, ctx }) => { - // const user = await findUserById(input.userId); - // if (user.adminId !== ctx.user.adminId) { - // throw new TRPCError({ - // code: "UNAUTHORIZED", - // message: "You are not allowed to access this user", - // }); - // } - // return user; - // }), + get: protectedProcedure.query(async ({ ctx }) => { + return await findUserById(ctx.user.id); + }), + update: protectedProcedure + .input(apiUpdateUser) + .mutation(async ({ input, ctx }) => { + return await updateUser(ctx.user.id, input); + }), + verify2FASetup: protectedProcedure + .input( + z.object({ + secret: z.string(), + pin: z.string(), + }), + ) + .mutation(async ({ ctx, input }) => { + const user = await findUserById(ctx.user.id); + await verify2FA(user, input.secret, input.pin); + await updateUser(user.id, { + secret: input.secret, + }); + return user; + }), }); diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 38839afbf..045a75235 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,9 +1,9 @@ import { - boolean, - integer, pgTable, text, + integer, timestamp, + boolean, } from "drizzle-orm/pg-core"; export const users_temp = pgTable("users_temp", { @@ -14,6 +14,7 @@ export const users_temp = pgTable("users_temp", { image: text("image"), createdAt: timestamp("created_at").notNull(), updatedAt: timestamp("updated_at").notNull(), + twoFactorEnabled: boolean("two_factor_enabled"), role: text("role").notNull(), ownerId: text("owner_id").notNull(), }); @@ -59,6 +60,15 @@ export const verification = pgTable("verification", { updatedAt: timestamp("updated_at"), }); +export const twoFactor = pgTable("two_factor", { + id: text("id").primaryKey(), + secret: text("secret").notNull(), + backupCodes: text("backup_codes").notNull(), + userId: text("user_id") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), +}); + export const organization = pgTable("organization", { id: text("id").primaryKey(), name: text("name").notNull(), diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 3bb7dcfca..1c0b10e9a 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -119,3 +119,12 @@ export const invitationRelations = relations(invitation, ({ one }) => ({ references: [organization.id], }), })); + +export const twoFactor = pgTable("two_factor", { + id: text("id").primaryKey(), + secret: text("secret").notNull(), + backupCodes: text("backup_codes").notNull(), + userId: text("user_id") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), +}); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 33e9e4fcd..a8f4cbcf0 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -59,10 +59,12 @@ export const users_temp = pgTable("user_temp", { .array() .notNull() .default(sql`ARRAY[]::text[]`), + // authId: text("authId") // .notNull() // .references(() => auth.id, { onDelete: "cascade" }), // Auth + twoFactorEnabled: boolean("two_factor_enabled"), email: text("email").notNull().unique(), emailVerified: boolean("email_verified").notNull(), image: text("image"), @@ -151,10 +153,8 @@ export const usersRelations = relations(users_temp, ({ one, many }) => ({ const createSchema = createInsertSchema(users_temp, { id: z.string().min(1), - // authId: z.string().min(1), token: z.string().min(1), isRegistered: z.boolean().optional(), - // adminId: z.string(), accessedProjects: z.array(z.string()).optional(), accessedServices: z.array(z.string()).optional(), canCreateProjects: z.boolean().optional(), @@ -297,3 +297,30 @@ export const apiUpdateWebServerMonitoring = z.object({ }) .required(), }); + +export const apiUpdateUser = createSchema.partial().extend({ + metricsConfig: z + .object({ + server: z.object({ + type: z.enum(["Dokploy", "Remote"]), + refreshRate: z.number(), + port: z.number(), + token: z.string(), + urlCallback: z.string(), + retentionDays: z.number(), + cronJob: z.string(), + thresholds: z.object({ + cpu: z.number(), + memory: z.number(), + }), + }), + containers: z.object({ + refreshRate: z.number(), + services: z.object({ + include: z.array(z.string()), + exclude: z.array(z.string()), + }), + }), + }) + .optional(), +}); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index a7fbde9a5..fece335b2 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -2,7 +2,11 @@ import type { IncomingMessage } from "node:http"; import * as bcrypt from "bcrypt"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { createAuthMiddleware, organization } from "better-auth/plugins"; +import { + createAuthMiddleware, + organization, + twoFactor, +} from "better-auth/plugins"; import { desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; @@ -85,6 +89,7 @@ export const auth = betterAuth({ }, plugins: [ + twoFactor(), organization({ async sendInvitationEmail(data, request) { const inviteLink = `https://example.com/accept-invitation/${data.id}`; diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 4e1b1bb33..53de805e9 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -12,41 +12,40 @@ import * as bcrypt from "bcrypt"; import { eq } from "drizzle-orm"; import { IS_CLOUD } from "../constants"; -export type Admin = typeof users_temp.$inferSelect; +export type User = typeof users_temp.$inferSelect; export const createInvitation = async ( input: typeof apiCreateUserInvitation._type, adminId: string, ) => { - await db.transaction(async (tx) => { - const result = await tx - .insert(auth) - .values({ - email: input.email.toLowerCase(), - rol: "user", - password: bcrypt.hashSync("01231203012312", 10), - }) - .returning() - .then((res) => res[0]); - - if (!result) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - }); - } - const expiresIn24Hours = new Date(); - expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); - const token = randomBytes(32).toString("hex"); - // await tx - // .insert(users) - // .values({ - // adminId: adminId, - // authId: result.id, - // token, - // expirationDate: expiresIn24Hours.toISOString(), - // }) - // .returning(); - }); + // await db.transaction(async (tx) => { + // const result = await tx + // .insert(auth) + // .values({ + // email: input.email.toLowerCase(), + // rol: "user", + // password: bcrypt.hashSync("01231203012312", 10), + // }) + // .returning() + // .then((res) => res[0]); + // if (!result) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Error creating the user", + // }); + // } + // const expiresIn24Hours = new Date(); + // expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); + // const token = randomBytes(32).toString("hex"); + // await tx + // .insert(users) + // .values({ + // adminId: adminId, + // authId: result.id, + // token, + // expirationDate: expiresIn24Hours.toISOString(), + // }) + // .returning(); + // }); }; export const findUserById = async (userId: string) => { @@ -65,7 +64,7 @@ export const findUserById = async (userId: string) => { return user; }; -export const updateUser = async (userId: string, userData: Partial) => { +export const updateUser = async (userId: string, userData: Partial) => { const user = await db .update(users_temp) .set({ @@ -80,7 +79,7 @@ export const updateUser = async (userId: string, userData: Partial) => { export const updateAdminById = async ( adminId: string, - adminData: Partial, + adminData: Partial, ) => { // const admin = await db // .update(admins) @@ -93,13 +92,6 @@ export const updateAdminById = async ( // return admin; }; -export const findAdminById = async (userId: string) => { - const admin = await db.query.admins.findFirst({ - // where: eq(admins.userId, userId), - }); - return admin; -}; - export const isAdminPresent = async () => { const admin = await db.query.member.findFirst({ where: eq(member.role, "owner"), @@ -113,33 +105,6 @@ export const isAdminPresent = async () => { return true; }; -export const findAdminByAuthId = async (authId: string) => { - const admin = await db.query.admins.findFirst({ - where: eq(admins.authId, authId), - with: { - users: true, - }, - }); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } - return admin; -}; - -export const findAdmin = async () => { - const admin = await db.query.admins.findFirst({}); - if (!admin) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Admin not found", - }); - } - return admin; -}; - export const getUserByToken = async (token: string) => { // const user = await db.query.users.findFirst({ // where: eq(users.token, token), @@ -171,24 +136,6 @@ export const removeUserById = async (userId: string) => { .then((res) => res[0]); }; -export const removeAdminByAuthId = async (authId: string) => { - const admin = await findAdminByAuthId(authId); - if (!admin) return null; - - // First delete all associated users - const users = admin.users; - - // for (const user of users) { - // await removeUserById(user.id); - // } - // Then delete the auth record which will cascade delete the admin - return await db - .delete(auth) - .where(eq(auth.id, authId)) - .returning() - .then((res) => res[0]); -}; - export const getDokployUrl = async () => { if (IS_CLOUD) { return "https://app.dokploy.com"; diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index dbdf538b8..8f3564be9 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -10,6 +10,7 @@ import { TOTP } from "otpauth"; import QRCode from "qrcode"; import { IS_CLOUD } from "../constants"; import { findUserById } from "./admin"; +import type { User } from "./user"; export const findAuthById = async (authId: string) => { const result = await db.query.users_temp.findFirst({ @@ -51,11 +52,7 @@ export const generate2FASecret = async (userId: string) => { }; }; -export const verify2FA = async ( - auth: Omit, - secret: string, - pin: string, -) => { +export const verify2FA = async (auth: User, secret: string, pin: string) => { const totp = new TOTP({ issuer: "Dokploy", label: `${auth?.email}`, diff --git a/packages/server/src/utils/backups/index.ts b/packages/server/src/utils/backups/index.ts index 922232a0c..d1b87d692 100644 --- a/packages/server/src/utils/backups/index.ts +++ b/packages/server/src/utils/backups/index.ts @@ -1,4 +1,3 @@ -import { findAdmin } from "@dokploy/server/services/admin"; import { getAllServers } from "@dokploy/server/services/server"; import { scheduleJob } from "node-schedule"; import { db } from "../../db/index"; diff --git a/packages/server/src/utils/traefik/web-server.ts b/packages/server/src/utils/traefik/web-server.ts index 76733e751..78046c673 100644 --- a/packages/server/src/utils/traefik/web-server.ts +++ b/packages/server/src/utils/traefik/web-server.ts @@ -1,11 +1,11 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { paths } from "@dokploy/server/constants"; +import type { User } from "@dokploy/server/services/user"; import { dump, load } from "js-yaml"; import { loadOrCreateConfig, writeTraefikConfig } from "./application"; import type { FileConfig } from "./file-types"; import type { MainTraefikConfig } from "./types"; -import type { User } from "@dokploy/server/services/user"; export const updateServerTraefik = ( user: User | null, From 0e8e92c71574e2bb8e5609fedcf8a5f8fb8405c6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 20:56:50 -0600 Subject: [PATCH 060/126] refactor: add 2fa --- .../dashboard/settings/users/show-users.tsx | 59 ++++++++++--------- apps/dokploy/drizzle/0067_migrate-data.sql | 49 +++++++++++++-- apps/dokploy/pages/invitation.tsx | 9 ++- packages/server/auth-schema.ts | 4 +- packages/server/src/lib/auth.ts | 11 +++- packages/server/src/services/admin.ts | 39 ++++++------ .../server/src/utils/access-log/handler.ts | 13 ++-- 7 files changed, 119 insertions(+), 65 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index e0ffac139..7e3ed6f15 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -78,7 +78,9 @@ export const ShowUsers = () => { Email Role 2FA - {/* Status */} + + Is Registered + Created At @@ -104,15 +106,15 @@ export const ShowUsers = () => { - {/* {user.user.is2FAEnabled - ? "2FA Enabled" - : "2FA Not Enabled"} */} + {user.user.twoFactorEnabled + ? "Enabled" + : "Disabled"} + + + {user.user.isRegistered || user.role === "owner" + ? "Registered" + : "Not Registered"} - {/* - - {format(new Date(user.createdAt), "PPpp")} - - */} {format(new Date(user.createdAt), "PPpp")} @@ -134,29 +136,30 @@ export const ShowUsers = () => { Actions - {/* {!user.isRegistered && ( - { - copy( - `${origin}/invitation?token=${user.token}`, - ); - toast.success( - "Invitation Copied to clipboard", - ); - }} - > - Copy Invitation - - )} */} + {!user.user.isRegistered && + user.role !== "owner" && ( + { + copy( + `${origin}/invitation?token=${user.user.token}`, + ); + toast.success( + "Invitation Copied to clipboard", + ); + }} + > + Copy Invitation + + )} - {/* {user.isRegistered && ( + {user.user.isRegistered && ( - )} */} + )} - {/* {user.role !== "owner" && ( + {user.role !== "owner" && ( { Delete User - )} */} + )} diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql index 074de9fa6..4b860a32f 100644 --- a/apps/dokploy/drizzle/0067_migrate-data.sql +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -25,7 +25,8 @@ WITH inserted_users AS ( "stripeSubscriptionId", "serversQuantity", "expirationDate", - "createdAt" + "createdAt", + "two_factor_enabled" ) SELECT a."adminId", @@ -50,11 +51,30 @@ WITH inserted_users AS ( a."stripeSubscriptionId", a."serversQuantity", NOW() + INTERVAL '1 year', - NOW() + NOW(), + COALESCE(auth."is2FAEnabled", false) FROM admin a JOIN auth ON auth.id = a."authId" RETURNING * ), +inserted_two_factor_admin AS ( + -- Insertar registros en two_factor para admins con 2FA habilitado + INSERT INTO two_factor ( + id, + secret, + backup_codes, + user_id + ) + SELECT + gen_random_uuid(), + auth.secret, + gen_random_uuid()::text, + a."adminId" + FROM admin a + JOIN auth ON auth.id = a."authId" + WHERE auth."is2FAEnabled" = true + RETURNING * +), inserted_accounts AS ( -- Insertar cuentas para los admins INSERT INTO account ( @@ -120,7 +140,8 @@ inserted_members AS ( "canDeleteServices", "accesedProjects", "accesedServices", - "expirationDate" + "expirationDate", + "two_factor_enabled" ) SELECT u."userId", @@ -141,7 +162,8 @@ inserted_members AS ( COALESCE(u."canDeleteServices", false), COALESCE(u."accesedProjects", '{}'), COALESCE(u."accesedServices", '{}'), - NOW() + INTERVAL '1 year' + NOW() + INTERVAL '1 year', + COALESCE(auth."is2FAEnabled", false) FROM "user" u JOIN admin a ON u."adminId" = a."adminId" JOIN auth ON auth.id = u."authId" @@ -173,6 +195,25 @@ inserted_member_accounts AS ( JOIN auth ON auth.id = u."authId" RETURNING * ), +inserted_two_factor_members AS ( + -- Insertar registros en two_factor para miembros con 2FA habilitado + INSERT INTO two_factor ( + id, + secret, + backup_codes, + user_id + ) + SELECT + gen_random_uuid(), + auth.secret, + gen_random_uuid()::text, + u."userId" + FROM "user" u + JOIN admin a ON u."adminId" = a."adminId" + JOIN auth ON auth.id = u."authId" + WHERE auth."is2FAEnabled" = true + RETURNING * +), inserted_admin_members AS ( -- Insertar miembros en las organizaciones (admins como owners) INSERT INTO member ( diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx index 77f9f2493..e8bfc3fc3 100644 --- a/apps/dokploy/pages/invitation.tsx +++ b/apps/dokploy/pages/invitation.tsx @@ -27,6 +27,7 @@ import { type ReactElement, useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import superjson from "superjson"; const registerSchema = z .object({ @@ -98,9 +99,9 @@ const Invitation = ({ token, invitation, isCloud }: Props) => { }); useEffect(() => { - if (data?.auth?.email) { + if (data?.email) { form.reset({ - email: data?.auth?.email || "", + email: data?.email || "", password: "", confirmPassword: "", }); @@ -109,7 +110,7 @@ const Invitation = ({ token, invitation, isCloud }: Props) => { const onSubmit = async (values: Register) => { await mutateAsync({ - id: data?.authId, + id: data?.id, password: values.password, token: token, }) @@ -254,6 +255,7 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) { const { query } = ctx; const token = query.token; + console.log("query", query); if (typeof token !== "string") { return { @@ -266,6 +268,7 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) { try { const invitation = await getUserByToken(token); + console.log("invitation", invitation); if (invitation.isExpired) { return { diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index 045a75235..2500b615f 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,9 +1,9 @@ import { + boolean, + integer, pgTable, text, - integer, timestamp, - boolean, } from "drizzle-orm/pg-core"; export const users_temp = pgTable("users_temp", { diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index fece335b2..cc144345b 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -16,7 +16,16 @@ export const auth = betterAuth({ provider: "pg", schema: schema, }), - + socialProviders: { + github: { + clientId: process.env.GITHUB_CLIENT_ID as string, + clientSecret: process.env.GITHUB_CLIENT_SECRET as string, + }, + google: { + clientId: process.env.GOOGLE_CLIENT_ID as string, + clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, + }, + }, emailAndPassword: { enabled: true, diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 53de805e9..eee6bb37c 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -106,26 +106,25 @@ export const isAdminPresent = async () => { }; export const getUserByToken = async (token: string) => { - // const user = await db.query.users.findFirst({ - // where: eq(users.token, token), - // with: { - // auth: { - // columns: { - // password: false, - // }, - // }, - // }, - // }); - // if (!user) { - // throw new TRPCError({ - // code: "NOT_FOUND", - // message: "Invitation not found", - // }); - // } - // return { - // ...user, - // isExpired: user.isRegistered, - // }; + const user = await db.query.users_temp.findFirst({ + where: eq(users_temp.token, token), + columns: { + id: true, + email: true, + token: true, + isRegistered: true, + }, + }); + if (!user) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Invitation not found", + }); + } + return { + ...user, + isExpired: user.isRegistered, + }; }; export const removeUserById = async (userId: string) => { diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index 574717323..30b18ea42 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -29,16 +29,15 @@ class LogRotationManager { } private async getStateFromDB(): Promise { - const setting = await db.query.admins.findFirst({}); - return setting?.enableLogRotation ?? false; + // const setting = await db.query.admins.findFirst({}); + // return setting?.enableLogRotation ?? false; } private async setStateInDB(active: boolean): Promise { - const admin = await db.query.admins.findFirst({}); - - if (!admin) { - return; - } + // const admin = await db.query.admins.findFirst({}); + // if (!admin) { + // return; + // } // await updateAdmin(admin?.authId, { // enableLogRotation: active, // }); From c78b8cfead9cf9555d1804f58f88d8e74227fb9a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 16 Feb 2025 21:30:57 -0600 Subject: [PATCH 061/126] Update package.json --- apps/dokploy/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 30666f83d..d66a6e67f 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -1,6 +1,6 @@ { "name": "dokploy", - "version": "v0.18.3", + "version": "v0.18.4", "private": true, "license": "Apache-2.0", "type": "module", From 7abe060fcf3dea922d9850acfed97385fe5a0f14 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:07:36 -0600 Subject: [PATCH 062/126] feat: enhance two-factor authentication and auth client implementation --- .../server/update-server-config.test.ts | 1 + .../dashboard/projects/handle-project.tsx | 2 +- .../components/dashboard/search-command.tsx | 2 +- .../git/github/add-github-provider.tsx | 2 +- .../settings/profile/disable-2fa.tsx | 127 ++++-- .../dashboard/settings/profile/enable-2fa.tsx | 365 +++++++++++++----- .../settings/profile/profile-form.tsx | 7 +- .../settings/users/add-invitation.tsx | 166 ++++++++ .../dashboard/settings/users/add-user.tsx | 2 +- .../settings/users/show-invitations.tsx | 191 +++++++++ .../dashboard/settings/users/show-users.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 2 +- apps/dokploy/components/layouts/user-nav.tsx | 2 +- apps/dokploy/drizzle/0067_migrate-data.sql | 12 +- apps/dokploy/lib/{auth.ts => auth-client.ts} | 3 +- .../accept-invitation/[accept-invitation].tsx | 2 +- .../pages/dashboard/settings/users.tsx | 2 + apps/dokploy/pages/index.tsx | 307 +++++++++------ apps/dokploy/pages/invitation.tsx | 2 +- apps/dokploy/pages/register.tsx | 2 +- .../server/api/routers/organization.ts | 8 +- packages/server/package.json | 1 + packages/server/src/lib/auth.ts | 1 + packages/server/src/services/auth.ts | 159 +++++++- pnpm-lock.yaml | 3 + 25 files changed, 1103 insertions(+), 270 deletions(-) create mode 100644 apps/dokploy/components/dashboard/settings/users/add-invitation.tsx create mode 100644 apps/dokploy/components/dashboard/settings/users/show-invitations.tsx rename apps/dokploy/lib/{auth.ts => auth-client.ts} (67%) diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index 458266dc2..49d71bc4c 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -75,6 +75,7 @@ const baseAdmin: User = { image: "", token: "", updatedAt: new Date(), + twoFactorEnabled: false, }; beforeEach(() => { diff --git a/apps/dokploy/components/dashboard/projects/handle-project.tsx b/apps/dokploy/components/dashboard/projects/handle-project.tsx index fb2cbf67a..492c03c93 100644 --- a/apps/dokploy/components/dashboard/projects/handle-project.tsx +++ b/apps/dokploy/components/dashboard/projects/handle-project.tsx @@ -21,7 +21,7 @@ import { import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon, SquarePen } from "lucide-react"; diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 8158a9ca8..7ea53d72f 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -18,7 +18,7 @@ import { CommandList, CommandSeparator, } from "@/components/ui/command"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { type Services, extractServices, diff --git a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx index 5f2cb934d..819d8e703 100644 --- a/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/github/add-github-provider.tsx @@ -10,7 +10,7 @@ import { } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { format } from "date-fns"; import { useEffect, useState } from "react"; diff --git a/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx index 2850332ef..79306bf18 100644 --- a/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx @@ -1,52 +1,131 @@ import { AlertDialog, - AlertDialogAction, - AlertDialogCancel, AlertDialogContent, AlertDialogDescription, - AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Button } from "@/components/ui/button"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const PasswordSchema = z.object({ + password: z.string().min(8, { + message: "Password is required", + }), +}); + +type PasswordForm = z.infer; export const Disable2FA = () => { const utils = api.useUtils(); - const { mutateAsync, isLoading } = api.auth.disable2FA.useMutation(); + const [isLoading, setIsLoading] = useState(false); + + const form = useForm({ + resolver: zodResolver(PasswordSchema), + defaultValues: { + password: "", + }, + }); + + const handleSubmit = async (formData: PasswordForm) => { + setIsLoading(true); + try { + const result = await authClient.twoFactor.disable({ + password: formData.password, + }); + + if (result.error) { + form.setError("password", { + message: result.error.message, + }); + toast.error(result.error.message); + return; + } + + toast.success("2FA disabled successfully"); + utils.auth.get.invalidate(); + } catch (error) { + form.setError("password", { + message: "Connection error. Please try again.", + }); + toast.error("Connection error. Please try again."); + } finally { + setIsLoading(false); + } + }; + return ( - + Are you absolutely sure? - This action cannot be undone. This will permanently delete the 2FA + This action cannot be undone. This will permanently disable + Two-Factor Authentication for your account. - - Cancel - { - await mutateAsync() - .then(() => { - utils.auth.get.invalidate(); - toast.success("2FA Disabled"); - }) - .catch(() => { - toast.error("Error disabling 2FA"); - }); - }} + + + - Confirm - - + ( + + Password + + + + + Enter your password to disable 2FA + + + + )} + /> +
+ + +
+ +
); diff --git a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx index cf5910b8a..b1da3ec1a 100644 --- a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx @@ -17,144 +17,315 @@ import { FormLabel, FormMessage, } from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; import { InputOTP, InputOTPGroup, InputOTPSlot, } from "@/components/ui/input-otp"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, Fingerprint } from "lucide-react"; -import { useEffect } from "react"; +import { Fingerprint, QrCode } from "lucide-react"; +import QRCode from "qrcode"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -const Enable2FASchema = z.object({ +const PasswordSchema = z.object({ + password: z.string().min(8, { + message: "Password is required", + }), +}); + +const PinSchema = z.object({ pin: z.string().min(6, { message: "Pin is required", }), }); -type Enable2FA = z.infer; +type PasswordForm = z.infer; +type PinForm = z.infer; + +type TwoFactorEnableResponse = { + totpURI: string; + backupCodes: string[]; +}; + +type TwoFactorSetupData = { + qrCodeUrl: string; + secret: string; + totpURI: string; +}; export const Enable2FA = () => { const utils = api.useUtils(); + const { data: session } = authClient.useSession(); + const [data, setData] = useState(null); + const [backupCodes, setBackupCodes] = useState([]); + const [isDialogOpen, setIsDialogOpen] = useState(false); + const [step, setStep] = useState<"password" | "verify">("password"); + const [isPasswordLoading, setIsPasswordLoading] = useState(false); - const { data } = api.auth.generate2FASecret.useQuery(undefined, { - refetchOnWindowFocus: false, + const handlePasswordSubmit = async (formData: PasswordForm) => { + setIsPasswordLoading(true); + try { + const { data: enableData } = await authClient.twoFactor.enable({ + password: formData.password, + }); + + if (!enableData) { + throw new Error("No data received from server"); + } + + if (enableData.backupCodes) { + setBackupCodes(enableData.backupCodes); + } + + if (enableData.totpURI) { + const qrCodeUrl = await QRCode.toDataURL(enableData.totpURI); + + setData({ + qrCodeUrl, + secret: enableData.totpURI.split("secret=")[1]?.split("&")[0] || "", + totpURI: enableData.totpURI, + }); + + setStep("verify"); + toast.success("Scan the QR code with your authenticator app"); + } else { + throw new Error("No TOTP URI received from server"); + } + } catch (error) { + toast.error( + error instanceof Error ? error.message : "Error setting up 2FA", + ); + passwordForm.setError("password", { + message: "Error verifying password", + }); + } finally { + setIsPasswordLoading(false); + } + }; + + const handleVerifySubmit = async (formData: PinForm) => { + try { + const result = await authClient.twoFactor.verifyTotp({ + code: formData.pin, + }); + + if (result.error) { + if (result.error.code === "INVALID_TWO_FACTOR_AUTHENTICATION") { + pinForm.setError("pin", { + message: "Invalid code. Please try again.", + }); + toast.error("Invalid verification code"); + return; + } + + throw result.error; + } + + if (!result.data) { + throw new Error("No response received from server"); + } + + toast.success("2FA configured successfully"); + utils.auth.get.invalidate(); + setIsDialogOpen(false); + } catch (error) { + if (error instanceof Error) { + const errorMessage = + error.message === "Failed to fetch" + ? "Connection error. Please check your internet connection." + : error.message; + + pinForm.setError("pin", { + message: errorMessage, + }); + toast.error(errorMessage); + } else { + pinForm.setError("pin", { + message: "Error verifying code", + }); + toast.error("Error verifying 2FA code"); + } + } + }; + + const passwordForm = useForm({ + resolver: zodResolver(PasswordSchema), + defaultValues: { + password: "", + }, }); - const { mutateAsync, isLoading, error, isError } = - api.auth.verify2FASetup.useMutation(); - - const form = useForm({ + const pinForm = useForm({ + resolver: zodResolver(PinSchema), defaultValues: { pin: "", }, - resolver: zodResolver(Enable2FASchema), }); useEffect(() => { - form.reset({ - pin: "", - }); - }, [form, form.reset, form.formState.isSubmitSuccessful]); + if (!isDialogOpen) { + setStep("password"); + setData(null); + setBackupCodes([]); + passwordForm.reset(); + pinForm.reset(); + } + }, [isDialogOpen, passwordForm, pinForm]); - const onSubmit = async (formData: Enable2FA) => { - await mutateAsync({ - pin: formData.pin, - secret: data?.secret || "", - }) - .then(async () => { - toast.success("2FA Verified"); - utils.auth.get.invalidate(); - }) - .catch(() => { - toast.error("Error verifying the 2FA"); - }); - }; return ( - + - + 2FA Setup - Add a 2FA to your account + + {step === "password" + ? "Enter your password to begin 2FA setup" + : "Scan the QR code and verify with your authenticator app"} + - {isError && ( -
- - - {error?.message} - -
- )} -
- -
- - {data?.qrCodeUrl ? "Scan the QR code to add 2FA" : ""} - - qrCode -
- - {data?.secret ? `Secret: ${data?.secret}` : ""} - -
-
- ( - - Pin - - - - - - - - - - - - - - Please enter the 6 digits code provided by your - authenticator app. - - - - )} - /> - - - - - - + ( + + Password + + + + + Enter your password to enable 2FA + + + + )} + /> + + + + ) : ( +
+ +
+ {data?.qrCodeUrl ? ( + <> +
+ + + Scan this QR code with your authenticator app + + 2FA QR Code +
+ + Can't scan the QR code? + + + {data.secret} + +
+
+ + {backupCodes && backupCodes.length > 0 && ( +
+

Backup Codes

+
+ {backupCodes.map((code, index) => ( + + {code} + + ))} +
+

+ Save these backup codes in a secure place. You can use + them to access your account if you lose access to your + authenticator device. +

+
+ )} + + ) : ( +
+ +
+ )} +
+ + ( + + Verification Code + + + + + + + + + + + + + + Enter the 6-digit code from your authenticator app + + + + )} + /> + + + + + )}
); diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 4da97d18b..944b2ff49 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -1,4 +1,5 @@ import { AlertBlock } from "@/components/shared/alert-block"; +import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; import { Card, @@ -17,6 +18,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; +import { authClient } from "@/lib/auth-client"; import { generateSHA256Hash } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -54,6 +56,9 @@ const randomImages = [ ]; export const ProfileForm = () => { + const utils = api.useUtils(); + const { mutateAsync: disable2FA, isLoading: isDisabling } = + api.auth.disable2FA.useMutation(); const { data, refetch, isLoading } = api.auth.get.useQuery(); const { mutateAsync, @@ -130,7 +135,7 @@ export const ProfileForm = () => { {t("settings.profile.description")}
- {!data?.is2FAEnabled ? : } + {!data?.user.twoFactorEnabled ? : } diff --git a/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx b/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx new file mode 100644 index 000000000..d05409fb7 --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/users/add-invitation.tsx @@ -0,0 +1,166 @@ +import { AlertBlock } from "@/components/shared/alert-block"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { authClient } from "@/lib/auth-client"; +import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { PlusIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; + +const addInvitation = z.object({ + email: z + .string() + .min(1, "Email is required") + .email({ message: "Invalid email" }), + role: z.enum(["member", "admin"]), +}); + +type AddInvitation = z.infer; + +export const AddInvitation = () => { + const [open, setOpen] = useState(false); + const utils = api.useUtils(); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + const { data: activeOrganization } = authClient.useActiveOrganization(); + + const form = useForm({ + defaultValues: { + email: "", + role: "member", + }, + resolver: zodResolver(addInvitation), + }); + useEffect(() => { + form.reset(); + }, [form, form.formState.isSubmitSuccessful, form.reset]); + + const onSubmit = async (data: AddInvitation) => { + setIsLoading(true); + const result = await authClient.organization.inviteMember({ + email: data.email.toLowerCase(), + role: data.role, + organizationId: activeOrganization?.id, + }); + + if (result.error) { + setError(result.error.message || ""); + } else { + toast.success("Invitation created"); + setError(null); + setOpen(false); + } + + utils.organization.allInvitations.invalidate(); + setIsLoading(false); + }; + return ( + + + + + + + Add Invitation + Invite a new user + + {error && {error}} + +
+ + { + return ( + + Email + + + + + This will be the email of the new user + + + + ); + }} + /> + + { + return ( + + Role + + + Select the role for the new user + + + + ); + }} + /> + + + + + +
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/settings/users/add-user.tsx b/apps/dokploy/components/dashboard/settings/users/add-user.tsx index fa1c8bf95..78c8ebdb4 100644 --- a/apps/dokploy/components/dashboard/settings/users/add-user.tsx +++ b/apps/dokploy/components/dashboard/settings/users/add-user.tsx @@ -19,7 +19,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon } from "lucide-react"; diff --git a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx new file mode 100644 index 000000000..e6067e7b3 --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx @@ -0,0 +1,191 @@ +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { + Table, + TableBody, + TableCaption, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { authClient } from "@/lib/auth-client"; +import { api } from "@/utils/api"; +import { format } from "date-fns"; +import { Mail, MoreHorizontal, Users } from "lucide-react"; +import { Loader2 } from "lucide-react"; +import { toast } from "sonner"; +import { AddInvitation } from "./add-invitation"; + +export const ShowInvitations = () => { + const { data, isLoading, refetch } = + api.organization.allInvitations.useQuery(); + const { mutateAsync, isLoading: isRemoving } = + api.admin.removeUser.useMutation(); + + return ( +
+ +
+ + + + Invitations + + + Create invitations to your organization. + + + + {isLoading ? ( +
+ Loading... + +
+ ) : ( + <> + {data?.length === 0 ? ( +
+ + + Invite users to your organization + + +
+ ) : ( +
+ + See all invitations + + + Email + Role + Status + + Expires At + + Actions + + + + {data?.map((invitation) => { + return ( + + + {invitation.email} + + + + {invitation.role} + + + + + {invitation.status} + + + + {format(new Date(invitation.expiresAt), "PPpp")} + + + + + + + + + + Actions + + + {/* { + copy( + `${origin}/invitation?token=${user.user.token}`, + ); + toast.success( + "Invitation Copied to clipboard", + ); + }} + > + Copy Invitation + */} + {invitation.status === "pending" && ( + { + const result = + await authClient.organization.cancelInvitation( + { + invitationId: invitation.id, + }, + ); + + if (result.error) { + toast.error(result.error.message); + } else { + toast.success("Invitation deleted"); + refetch(); + } + }} + > + Cancel Invitation + + )} + + + + + ); + })} + +
+ +
+ +
+
+ )} + + )} +
+
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 7e3ed6f15..40fbea0d3 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -153,7 +153,7 @@ export const ShowUsers = () => {
)} - {user.user.isRegistered && ( + {user.role !== "owner" && ( diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index cc27dc389..bd10352f5 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -495,7 +495,7 @@ import { DropdownMenuShortcut, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { toast } from "sonner"; import { AddOrganization } from "../dashboard/organization/handle-organization"; import { DialogAction } from "../shared/dialog-action"; diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index d4de2019c..49fc92e59 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -15,7 +15,7 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { Languages } from "@/lib/languages"; import { api } from "@/utils/api"; import useLocale from "@/utils/hooks/use-locale"; diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql index 4b860a32f..279acbfb9 100644 --- a/apps/dokploy/drizzle/0067_migrate-data.sql +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -26,7 +26,8 @@ WITH inserted_users AS ( "serversQuantity", "expirationDate", "createdAt", - "two_factor_enabled" + "two_factor_enabled", + "isRegistered" ) SELECT a."adminId", @@ -52,7 +53,8 @@ WITH inserted_users AS ( a."serversQuantity", NOW() + INTERVAL '1 year', NOW(), - COALESCE(auth."is2FAEnabled", false) + COALESCE(auth."is2FAEnabled", false), + true FROM admin a JOIN auth ON auth.id = a."authId" RETURNING * @@ -141,7 +143,8 @@ inserted_members AS ( "accesedProjects", "accesedServices", "expirationDate", - "two_factor_enabled" + "two_factor_enabled", + "isRegistered" ) SELECT u."userId", @@ -163,7 +166,8 @@ inserted_members AS ( COALESCE(u."accesedProjects", '{}'), COALESCE(u."accesedServices", '{}'), NOW() + INTERVAL '1 year', - COALESCE(auth."is2FAEnabled", false) + COALESCE(auth."is2FAEnabled", false), + COALESCE(u."isRegistered", false) FROM "user" u JOIN admin a ON u."adminId" = a."adminId" JOIN auth ON auth.id = u."authId" diff --git a/apps/dokploy/lib/auth.ts b/apps/dokploy/lib/auth-client.ts similarity index 67% rename from apps/dokploy/lib/auth.ts rename to apps/dokploy/lib/auth-client.ts index 12c3cc3e7..9a184959b 100644 --- a/apps/dokploy/lib/auth.ts +++ b/apps/dokploy/lib/auth-client.ts @@ -1,7 +1,8 @@ import { organizationClient } from "better-auth/client/plugins"; +import { twoFactorClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient({ // baseURL: "http://localhost:3000", // the base url of your auth server - plugins: [organizationClient()], + plugins: [organizationClient(), twoFactorClient()], }); diff --git a/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx b/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx index 6936a802d..bc60d970f 100644 --- a/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx +++ b/apps/dokploy/pages/accept-invitation/[accept-invitation].tsx @@ -1,5 +1,5 @@ import { Button } from "@/components/ui/button"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { useRouter } from "next/router"; export const AcceptInvitation = () => { diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index 1c53c82b1..7945bf86d 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -1,3 +1,4 @@ +import { ShowInvitations } from "@/components/dashboard/settings/users/show-invitations"; import { ShowUsers } from "@/components/dashboard/settings/users/show-users"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; @@ -12,6 +13,7 @@ const Page = () => { return (
+
); }; diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index b85b1c7e1..8013c6319 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -3,17 +3,24 @@ import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; import { Button, buttonVariants } from "@/components/ui/button"; -import { CardContent } from "@/components/ui/card"; +import { CardContent, CardDescription } from "@/components/ui/card"; import { Form, FormControl, + FormDescription, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { authClient } from "@/lib/auth"; +import { + InputOTP, + InputOTPGroup, + InputOTPSlot, +} from "@/components/ui/input-otp"; +import { Label } from "@/components/ui/label"; +import { authClient } from "@/lib/auth-client"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { IS_CLOUD, auth, isAdminPresent } from "@dokploy/server"; @@ -21,110 +28,118 @@ import { validateRequest } from "@dokploy/server/lib/auth"; import { zodResolver } from "@hookform/resolvers/zod"; import { Session, getSessionCookie } from "better-auth"; import { betterFetch } from "better-auth/react"; +import base32 from "hi-base32"; +import { REGEXP_ONLY_DIGITS } from "input-otp"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; +import { TOTP } from "otpauth"; import { type ReactElement, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; -const loginSchema = z.object({ - email: z - .string() - .min(1, { - message: "Email is required", - }) - .email({ - message: "Email must be a valid email", - }), - - password: z - .string() - .min(1, { - message: "Password is required", - }) - .min(8, { - message: "Password must be at least 8 characters", - }), +const LoginSchema = z.object({ + email: z.string().email(), + password: z.string().min(8), }); -type Login = z.infer; +const TwoFactorSchema = z.object({ + code: z.string().min(6), +}); -type AuthResponse = { - is2FAEnabled: boolean; - authId: string; -}; +type LoginForm = z.infer; +type TwoFactorForm = z.infer; interface Props { IS_CLOUD: boolean; } export default function Home({ IS_CLOUD }: Props) { - const [isLoading, setIsLoading] = useState(false); - const [isError, setIsError] = useState(false); - const [error, setError] = useState(null); - const [temp, setTemp] = useState({ - is2FAEnabled: false, - authId: "", - }); const router = useRouter(); - const form = useForm({ + const [isLoginLoading, setIsLoginLoading] = useState(false); + const [isTwoFactorLoading, setIsTwoFactorLoading] = useState(false); + const [isTwoFactor, setIsTwoFactor] = useState(false); + const [error, setError] = useState(null); + const [twoFactorCode, setTwoFactorCode] = useState(""); + + const loginForm = useForm({ + resolver: zodResolver(LoginSchema), defaultValues: { email: "siumauricio@hotmail.com", password: "Password123", }, - resolver: zodResolver(loginSchema), }); - useEffect(() => { - form.reset(); - }, [form, form.reset, form.formState.isSubmitSuccessful]); - - const onSubmit = async (values: Login) => { - setIsLoading(true); - const { data, error } = await authClient.signIn.email({ - email: values.email, - password: values.password, - }); - - if (!error) { - // if (data) { - // setTemp(data); - // } else { - toast.success("Successfully signed in", { - duration: 2000, + const onSubmit = async (values: LoginForm) => { + setIsLoginLoading(true); + try { + const { data, error } = await authClient.signIn.email({ + email: values.email, + password: values.password, }); + + if (error) { + toast.error(error.message); + setError(error.message || "An error occurred while logging in"); + return; + } + + if (data?.twoFactorRedirect as boolean) { + setTwoFactorCode(""); + setIsTwoFactor(true); + toast.info("Please enter your 2FA code"); + return; + } + + toast.success("Logged in successfully"); router.push("/dashboard/projects"); - // } - } else { - setIsError(true); - setError(error.message ?? "Error to signup"); - toast.error("Error to sign up", { - description: error.message, - }); + } catch (error) { + toast.error("An error occurred while logging in"); + } finally { + setIsLoginLoading(false); + } + }; + + const onTwoFactorSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (twoFactorCode.length !== 6) { + toast.error("Please enter a valid 6-digit code"); + return; } - setIsLoading(false); - // await mutateAsync({ - // email: values.email.toLowerCase(), - // password: values.password, - // }) - // .then((data) => { - // if (data.is2FAEnabled) { - // setTemp(data); - // } else { - // toast.success("Successfully signed in", { - // duration: 2000, - // }); - // router.push("/dashboard/projects"); - // } - // }) - // .catch(() => { - // toast.error("Signin failed", { - // duration: 2000, - // }); - // }); + setIsTwoFactorLoading(true); + try { + const { data, error } = await authClient.twoFactor.verifyTotp({ + code: twoFactorCode.replace(/\s/g, ""), + }); + + if (error) { + toast.error(error.message); + setError(error.message || "An error occurred while verifying 2FA code"); + return; + } + + toast.success("Logged in successfully"); + router.push("/dashboard/projects"); + } catch (error) { + toast.error("An error occurred while verifying 2FA code"); + } finally { + setIsTwoFactorLoading(false); + } }; + + const convertBase32ToHex = (base32Secret: string) => { + try { + // Usar asBytes() para obtener los bytes directamente + const bytes = base32.decode.asBytes(base32Secret.toUpperCase()); + // Convertir bytes a hex + return Buffer.from(bytes).toString("hex"); + } catch (error) { + console.error("Error converting base32 to hex:", error); + return base32Secret; // Fallback al valor original si hay error + } + }; + return ( <>
@@ -138,55 +153,109 @@ export default function Home({ IS_CLOUD }: Props) { Enter your email and password to sign in

- {isError && ( + {error && ( {error} )} - {!temp.is2FAEnabled ? ( -
- -
- ( - - Email - - - - - - )} - /> - ( - - Password - - - - - - )} - /> - - -
+ {!isTwoFactor ? ( + + + ( + + Email + + + + + + )} + /> + ( + + Password + + + + + + )} + /> + ) : ( - +
+
+ + + + + + + + + + + + + Enter the 6-digit code from your authenticator app + +
+ +
+ + +
+
)}
diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx index e8bfc3fc3..0dd8dbe4d 100644 --- a/apps/dokploy/pages/invitation.tsx +++ b/apps/dokploy/pages/invitation.tsx @@ -26,8 +26,8 @@ import { useRouter } from "next/router"; import { type ReactElement, useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; -import { z } from "zod"; import superjson from "superjson"; +import { z } from "zod"; const registerSchema = z .object({ diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index 73dce5e77..e8fd15cf5 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -17,7 +17,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { authClient } from "@/lib/auth"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index db0c64385..444a80ba8 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -1,5 +1,5 @@ import { db } from "@/server/db"; -import { member, organization } from "@/server/db/schema"; +import { invitation, member, organization } from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; import { desc, eq } from "drizzle-orm"; import { nanoid } from "nanoid"; @@ -83,4 +83,10 @@ export const organizationRouter = createTRPCRouter({ .where(eq(organization.id, input.organizationId)); return result; }), + allInvitations: adminProcedure.query(async ({ ctx }) => { + return await db.query.invitation.findMany({ + where: eq(invitation.organizationId, ctx.session.activeOrganizationId), + orderBy: [desc(invitation.status)], + }); + }), }); diff --git a/packages/server/package.json b/packages/server/package.json index d8f72a868..dbc24375e 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -28,6 +28,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@better-auth/utils":"0.2.3", "@oslojs/encoding":"1.1.0", "@oslojs/crypto":"1.0.1", "drizzle-dbml-generator":"0.10.0", diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index cc144345b..a4b9a4f1f 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -16,6 +16,7 @@ export const auth = betterAuth({ provider: "pg", schema: schema, }), + appName: "Dokploy", socialProviders: { github: { clientId: process.env.GITHUB_CLIENT_ID as string, diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 8f3564be9..0a9f4e945 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -1,4 +1,5 @@ import { randomBytes } from "node:crypto"; +import { createOTP } from "@better-auth/utils/otp"; import { db } from "@dokploy/server/db"; import { users_temp } from "@dokploy/server/db/schema"; import { getPublicIpWithFallback } from "@dokploy/server/wss/utils"; @@ -29,26 +30,41 @@ export const findAuthById = async (authId: string) => { return result; }; -export const generate2FASecret = async (userId: string) => { - const user = await findUserById(userId); +const generateBase32Secret = () => { + // Generamos 32 bytes (256 bits) para asegurar que tengamos suficiente longitud + const buffer = randomBytes(32); + // Convertimos directamente a hex para Better Auth + const hex = buffer.toString("hex"); + // También necesitamos la versión base32 para el QR code + const base32 = encode.encode(buffer).replace(/=/g, "").substring(0, 32); + return { + hex, + base32, + }; +}; - const base32_secret = generateBase32Secret(); +export const generate2FASecret = () => { + const secret = "46JMUCG4NJ3CIU6LQAIVFWUW"; const totp = new TOTP({ issuer: "Dokploy", - label: `${user?.email}`, + label: "siumauricio@hotmail.com", algorithm: "SHA1", digits: 6, - secret: base32_secret, + secret: secret, }); - const otpauth_url = totp.toString(); + // Convertir los bytes del secreto a hex + const secretBytes = totp.secret.bytes; + const hexSecret = Buffer.from(secretBytes).toString("hex"); - const qrUrl = await QRCode.toDataURL(otpauth_url); + console.log("Secret bytes:", secretBytes); + console.log("Hex secret:", hexSecret); return { - qrCodeUrl: qrUrl, - secret: base32_secret, + secret, + hexSecret, + totp, }; }; @@ -59,6 +75,7 @@ export const verify2FA = async (auth: User, secret: string, pin: string) => { algorithm: "SHA1", digits: 6, secret: secret, + period: 30, }); const delta = totp.validate({ token: pin }); @@ -72,8 +89,124 @@ export const verify2FA = async (auth: User, secret: string, pin: string) => { return auth; }; -const generateBase32Secret = () => { - const buffer = randomBytes(15); - const base32 = encode.encode(buffer).replace(/=/g, "").substring(0, 24); - return base32; +const convertBase32ToHex = (base32Secret: string) => { + try { + // Asegurarnos de que la longitud sea múltiplo de 8 agregando padding + let paddedSecret = base32Secret; + while (paddedSecret.length % 8 !== 0) { + paddedSecret += "="; + } + + const bytes = encode.decode.asBytes(paddedSecret.toUpperCase()); + let hex = Buffer.from(bytes).toString("hex"); + + // Asegurarnos de que el hex tenga al menos 32 caracteres (16 bytes) + while (hex.length < 32) { + hex += "0"; + } + + return hex; + } catch (error) { + console.error("Error converting base32 to hex:", error); + return base32Secret; + } }; + +// Para probar +// const testSecret = "46JMUCG4NJ3CIU6LQAIVFWUW"; +// console.log("Original:", testSecret); +// console.log("Converted:", convertBase32ToHex(testSecret)); +// console.log( +// "Length in bytes:", +// Buffer.from(convertBase32ToHex(testSecret), "hex").length, +// ); +// console.log(generate2FASecret().secret.secret); + +// // Para probar +// const testResult = generate2FASecret(); +// console.log("\nResultados:"); +// console.log("Original base32:", testResult.secret); +// console.log("Hex convertido:", testResult.hexSecret); +// console.log( +// "Longitud en bytes:", +// Buffer.from(testResult.hexSecret, "hex").length, +// ); +export const symmetricDecrypt = async ({ key, data }) => { + const keyAsBytes = await createHash("SHA-256").digest(key); + const dataAsBytes = hexToBytes(data); + const chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)); + return new TextDecoder().decode(chacha.decrypt(dataAsBytes)); +}; +export const migrateExistingSecret = async ( + existingBase32Secret: string, + encryptionKey: string, +) => { + try { + // 1. Primero asegurarnos que el secreto base32 tenga el padding correcto + let paddedSecret = existingBase32Secret; + while (paddedSecret.length % 8 !== 0) { + paddedSecret += "="; + } + + // 2. Decodificar el base32 a bytes usando hi-base32 + const bytes = encode.decode.asBytes(paddedSecret.toUpperCase()); + + // 3. Convertir los bytes a hex + const hexSecret = Buffer.from(bytes).toString("hex"); + + // 4. Encriptar el secreto hex usando Better Auth + const encryptedSecret = await symmetricEncrypt({ + key: encryptionKey, + data: hexSecret, + }); + + // 5. Crear TOTP con el secreto original para validación + const originalTotp = new TOTP({ + issuer: "Dokploy", + label: "migration-test", + algorithm: "SHA1", + digits: 6, + secret: existingBase32Secret, + }); + + // 6. Generar un código de prueba con el secreto original + const testCode = originalTotp.generate(); + + // 7. Validar que el código funcione con el secreto original + const isValid = originalTotp.validate({ token: testCode }) !== null; + + return { + originalSecret: existingBase32Secret, + hexSecret, + encryptedSecret, // Este es el valor que debes guardar en la base de datos + isValid, + testCode, + secretLength: hexSecret.length, + }; + } catch (error: unknown) { + const errorMessage = + error instanceof Error ? error.message : "Unknown error"; + console.error("Error durante la migración:", errorMessage); + throw new Error(`Error al migrar el secreto: ${errorMessage}`); + } +}; + +// // Ejemplo de uso con el secreto de prueba +// const testMigration = await migrateExistingSecret( +// "46JMUCG4NJ3CIU6LQAIVFWUW", +// process.env.BETTER_AUTH_SECRET || "your-encryption-key", +// ); +// console.log("\nPrueba de migración:"); +// console.log("Secreto original (base32):", testMigration.originalSecret); +// console.log("Secreto convertido (hex):", testMigration.hexSecret); +// console.log("Secreto encriptado:", testMigration.encryptedSecret); +// console.log("Longitud del secreto hex:", testMigration.secretLength); +// console.log("¿Conversión válida?:", testMigration.isValid); +// console.log("Código de prueba:", testMigration.testCode); +const secret = "46JMUCG4NJ3CIU6LQAIVFWUW"; +const isValid = createOTP(secret, { + digits: 6, + period: 30, +}).verify("123456"); + +console.log(isValid.then((isValid) => console.log(isValid))); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09f7885b4..ee68b3995 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -549,6 +549,9 @@ importers: packages/server: dependencies: + '@better-auth/utils': + specifier: 0.2.3 + version: 0.2.3 '@faker-js/faker': specifier: ^8.4.1 version: 8.4.1 From 8c282233436c86ccbdb7a94d90b20df76d1ae111 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:10:34 -0600 Subject: [PATCH 063/126] refactor: remove 2fa migration --- apps/dokploy/drizzle/0067_migrate-data.sql | 45 ---------------------- 1 file changed, 45 deletions(-) diff --git a/apps/dokploy/drizzle/0067_migrate-data.sql b/apps/dokploy/drizzle/0067_migrate-data.sql index 279acbfb9..8f26fc13e 100644 --- a/apps/dokploy/drizzle/0067_migrate-data.sql +++ b/apps/dokploy/drizzle/0067_migrate-data.sql @@ -26,7 +26,6 @@ WITH inserted_users AS ( "serversQuantity", "expirationDate", "createdAt", - "two_factor_enabled", "isRegistered" ) SELECT @@ -53,30 +52,11 @@ WITH inserted_users AS ( a."serversQuantity", NOW() + INTERVAL '1 year', NOW(), - COALESCE(auth."is2FAEnabled", false), true FROM admin a JOIN auth ON auth.id = a."authId" RETURNING * ), -inserted_two_factor_admin AS ( - -- Insertar registros en two_factor para admins con 2FA habilitado - INSERT INTO two_factor ( - id, - secret, - backup_codes, - user_id - ) - SELECT - gen_random_uuid(), - auth.secret, - gen_random_uuid()::text, - a."adminId" - FROM admin a - JOIN auth ON auth.id = a."authId" - WHERE auth."is2FAEnabled" = true - RETURNING * -), inserted_accounts AS ( -- Insertar cuentas para los admins INSERT INTO account ( @@ -85,7 +65,6 @@ inserted_accounts AS ( "provider_id", "user_id", password, - "is2FAEnabled", "created_at", "updated_at" ) @@ -95,7 +74,6 @@ inserted_accounts AS ( 'credential', a."adminId", auth.password, - COALESCE(auth."is2FAEnabled", false), NOW(), NOW() FROM admin a @@ -143,7 +121,6 @@ inserted_members AS ( "accesedProjects", "accesedServices", "expirationDate", - "two_factor_enabled", "isRegistered" ) SELECT @@ -166,7 +143,6 @@ inserted_members AS ( COALESCE(u."accesedProjects", '{}'), COALESCE(u."accesedServices", '{}'), NOW() + INTERVAL '1 year', - COALESCE(auth."is2FAEnabled", false), COALESCE(u."isRegistered", false) FROM "user" u JOIN admin a ON u."adminId" = a."adminId" @@ -181,7 +157,6 @@ inserted_member_accounts AS ( "provider_id", "user_id", password, - "is2FAEnabled", "created_at", "updated_at" ) @@ -191,7 +166,6 @@ inserted_member_accounts AS ( 'credential', u."userId", auth.password, - COALESCE(auth."is2FAEnabled", false), NOW(), NOW() FROM "user" u @@ -199,25 +173,6 @@ inserted_member_accounts AS ( JOIN auth ON auth.id = u."authId" RETURNING * ), -inserted_two_factor_members AS ( - -- Insertar registros en two_factor para miembros con 2FA habilitado - INSERT INTO two_factor ( - id, - secret, - backup_codes, - user_id - ) - SELECT - gen_random_uuid(), - auth.secret, - gen_random_uuid()::text, - u."userId" - FROM "user" u - JOIN admin a ON u."adminId" = a."adminId" - JOIN auth ON auth.id = u."authId" - WHERE auth."is2FAEnabled" = true - RETURNING * -), inserted_admin_members AS ( -- Insertar miembros en las organizaciones (admins como owners) INSERT INTO member ( From c7d47a60038b2c5717f5eb3338e990881ad831ff Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 17 Feb 2025 00:30:15 -0600 Subject: [PATCH 064/126] refactor: update database foreign key constraints and user management --- .../dashboard/settings/users/add-user.tsx | 129 - .../dashboard/settings/users/show-users.tsx | 79 +- .../dokploy/drizzle/0074_lowly_jack_power.sql | 18 + apps/dokploy/drizzle/meta/0074_snapshot.json | 4878 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/server/api/routers/user.ts | 22 +- packages/server/src/db/schema/account.ts | 12 +- 7 files changed, 4967 insertions(+), 178 deletions(-) delete mode 100644 apps/dokploy/components/dashboard/settings/users/add-user.tsx create mode 100644 apps/dokploy/drizzle/0074_lowly_jack_power.sql create mode 100644 apps/dokploy/drizzle/meta/0074_snapshot.json diff --git a/apps/dokploy/components/dashboard/settings/users/add-user.tsx b/apps/dokploy/components/dashboard/settings/users/add-user.tsx deleted file mode 100644 index 78c8ebdb4..000000000 --- a/apps/dokploy/components/dashboard/settings/users/add-user.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import { AlertBlock } from "@/components/shared/alert-block"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { authClient } from "@/lib/auth-client"; -import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { PlusIcon } from "lucide-react"; -import { useEffect, useState } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; - -const addUser = z.object({ - email: z - .string() - .min(1, "Email is required") - .email({ message: "Invalid email" }), -}); - -type AddUser = z.infer; - -export const AddUser = () => { - const [open, setOpen] = useState(false); - const utils = api.useUtils(); - - const { data: activeOrganization } = authClient.useActiveOrganization(); - const { mutateAsync, isError, error, isLoading } = - api.admin.createUserInvitation.useMutation(); - - const form = useForm({ - defaultValues: { - email: "", - }, - resolver: zodResolver(addUser), - }); - useEffect(() => { - form.reset(); - }, [form, form.formState.isSubmitSuccessful, form.reset]); - - const onSubmit = async (data: AddUser) => { - const result = await authClient.organization.inviteMember({ - email: data.email.toLowerCase(), - role: "member", - organizationId: activeOrganization?.id, - }); - console.log(result); - // await mutateAsync({ - // email: data.email.toLowerCase(), - // }) - // .then(async () => { - // toast.success("Invitation created"); - // await utils.user.all.invalidate(); - // setOpen(false); - // }) - // .catch(() => { - // toast.error("Error creating the invitation"); - // }); - }; - return ( - - - - - - - Add User - Invite a new user - - {isError && {error?.message}} - -
- - { - return ( - - Email - - - - - This will be the email of the new user - - - - ); - }} - /> - - - - - -
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 40fbea0d3..c09ebf9f8 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -27,18 +27,16 @@ import { api } from "@/utils/api"; import copy from "copy-to-clipboard"; import { format } from "date-fns"; import { MoreHorizontal, Users } from "lucide-react"; -import { useEffect, useState } from "react"; import { toast } from "sonner"; import { AddUserPermissions } from "./add-permissions"; -import { AddUser } from "./add-user"; - import { DialogAction } from "@/components/shared/dialog-action"; import { Loader2 } from "lucide-react"; +import { authClient } from "@/lib/auth-client"; export const ShowUsers = () => { + const { data: isCloud } = api.settings.isCloud.useQuery(); const { data, isLoading, refetch } = api.user.all.useQuery(); - const { mutateAsync, isLoading: isRemoving } = - api.admin.removeUser.useMutation(); + const { mutateAsync, isLoading: isRemoving } = api.user.remove.useMutation(); return (
@@ -67,7 +65,6 @@ export const ShowUsers = () => { Invite users to your Dokploy account -
) : (
@@ -88,36 +85,37 @@ export const ShowUsers = () => { - {data?.map((user) => { + {data?.map((member) => { return ( - + - {user.user.email} + {member.user.email} - {user.role} + {member.role} - {user.user.twoFactorEnabled + {member.user.twoFactorEnabled ? "Enabled" : "Disabled"} - {user.user.isRegistered || user.role === "owner" + {member.user.isRegistered || + member.role === "owner" ? "Registered" : "Not Registered"} - {format(new Date(user.createdAt), "PPpp")} + {format(new Date(member.createdAt), "PPpp")} @@ -136,13 +134,13 @@ export const ShowUsers = () => { Actions - {!user.user.isRegistered && - user.role !== "owner" && ( + {!member.user.isRegistered && + member.role !== "owner" && ( { copy( - `${origin}/invitation?token=${user.user.token}`, + `${origin}/invitation?token=${member.user.token}`, ); toast.success( "Invitation Copied to clipboard", @@ -153,32 +151,53 @@ export const ShowUsers = () => { )} - {user.role !== "owner" && ( + {member.role !== "owner" && ( )} - {user.role !== "owner" && ( + {member.role !== "owner" && ( { - await mutateAsync({ - userId: user.userId, - }) - .then(() => { + if (isCloud) { + const { error } = + await authClient.organization.removeMember( + { + memberIdOrEmail: + member.user.id, + }, + ); + + if (!error) { toast.success( "User deleted successfully", ); refetch(); - }) - .catch(() => { + } else { toast.error( - "Error deleting destination", + "Error deleting user", ); - }); + } + } else { + await mutateAsync({ + userId: member.user.id, + }) + .then(() => { + toast.success( + "User deleted successfully", + ); + refetch(); + }) + .catch(() => { + toast.error( + "Error deleting destination", + ); + }); + } }} > { })} - -
- -
)} diff --git a/apps/dokploy/drizzle/0074_lowly_jack_power.sql b/apps/dokploy/drizzle/0074_lowly_jack_power.sql new file mode 100644 index 000000000..4f09535f4 --- /dev/null +++ b/apps/dokploy/drizzle/0074_lowly_jack_power.sql @@ -0,0 +1,18 @@ +ALTER TABLE "account" DROP CONSTRAINT "account_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "invitation" DROP CONSTRAINT "invitation_organization_id_organization_id_fk"; +--> statement-breakpoint +ALTER TABLE "invitation" DROP CONSTRAINT "invitation_inviter_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "member" DROP CONSTRAINT "member_organization_id_organization_id_fk"; +--> statement-breakpoint +ALTER TABLE "member" DROP CONSTRAINT "member_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "organization" DROP CONSTRAINT "organization_owner_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "account" ADD CONSTRAINT "account_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "invitation" ADD CONSTRAINT "invitation_inviter_id_user_temp_id_fk" FOREIGN KEY ("inviter_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" FOREIGN KEY ("organization_id") REFERENCES "public"."organization"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0074_snapshot.json b/apps/dokploy/drizzle/meta/0074_snapshot.json new file mode 100644 index 000000000..7c3df01b3 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0074_snapshot.json @@ -0,0 +1,4878 @@ +{ + "id": "9cb79f1e-14c2-4deb-b1ab-a1d038f72356", + "prevId": "e357a19a-dd1e-4843-b567-0c0243ade7a8", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 39903f65a..6f38bc1bb 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -519,6 +519,13 @@ "when": 1739740193879, "tag": "0073_polite_miss_america", "breakpoints": true + }, + { + "idx": 74, + "version": "7", + "when": 1739773539709, + "tag": "0074_lowly_jack_power", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index addbdb23c..c5382e9a9 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -2,11 +2,13 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { findUserByAuthId, findUserById, + IS_CLOUD, + removeUserById, updateUser, verify2FA, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; -import { apiUpdateUser, member } from "@dokploy/server/db/schema"; +import { account, apiUpdateUser, member } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { z } from "zod"; @@ -44,19 +46,17 @@ export const userRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { return await updateUser(ctx.user.id, input); }), - verify2FASetup: protectedProcedure + + remove: protectedProcedure .input( z.object({ - secret: z.string(), - pin: z.string(), + userId: z.string(), }), ) - .mutation(async ({ ctx, input }) => { - const user = await findUserById(ctx.user.id); - await verify2FA(user, input.secret, input.pin); - await updateUser(user.id, { - secret: input.secret, - }); - return user; + .mutation(async ({ input, ctx }) => { + if (IS_CLOUD) { + return true; + } + return await removeUserById(input.userId); }), }); diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 1c0b10e9a..185de1368 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -15,7 +15,7 @@ export const account = pgTable("account", { providerId: text("provider_id").notNull(), userId: text("user_id") .notNull() - .references(() => users_temp.id), + .references(() => users_temp.id, { onDelete: "cascade" }), accessToken: text("access_token"), refreshToken: text("refresh_token"), idToken: text("id_token"), @@ -59,7 +59,7 @@ export const organization = pgTable("organization", { metadata: text("metadata"), ownerId: text("owner_id") .notNull() - .references(() => users_temp.id), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const organizationRelations = relations( @@ -80,10 +80,10 @@ export const member = pgTable("member", { .$defaultFn(() => nanoid()), organizationId: text("organization_id") .notNull() - .references(() => organization.id), + .references(() => organization.id, { onDelete: "cascade" }), userId: text("user_id") .notNull() - .references(() => users_temp.id), + .references(() => users_temp.id, { onDelete: "cascade" }), role: text("role").notNull().$type<"owner" | "member" | "admin">(), createdAt: timestamp("created_at").notNull(), }); @@ -103,14 +103,14 @@ export const invitation = pgTable("invitation", { id: text("id").primaryKey(), organizationId: text("organization_id") .notNull() - .references(() => organization.id), + .references(() => organization.id, { onDelete: "cascade" }), email: text("email").notNull(), role: text("role").$type<"owner" | "member" | "admin">(), status: text("status").notNull(), expiresAt: timestamp("expires_at").notNull(), inviterId: text("inviter_id") .notNull() - .references(() => users_temp.id), + .references(() => users_temp.id, { onDelete: "cascade" }), }); export const invitationRelations = relations(invitation, ({ one }) => ({ From b73e4102ddf0d50d686830556d21e0f5943de47b Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Mon, 17 Feb 2025 02:48:42 -0600 Subject: [PATCH 065/126] feat: add organizations and members --- .../settings/users/show-invitations.tsx | 30 +- .../dashboard/settings/users/show-users.tsx | 9 +- apps/dokploy/components/layouts/side.tsx | 149 +- apps/dokploy/components/layouts/user-nav.tsx | 14 +- .../drizzle/0075_heavy_metal_master.sql | 3 + apps/dokploy/drizzle/meta/0075_snapshot.json | 4878 +++++++++++++++++ apps/dokploy/drizzle/meta/_journal.json | 7 + .../pages/dashboard/settings/users.tsx | 2 + apps/dokploy/pages/invitation.tsx | 306 +- apps/dokploy/server/api/routers/admin.ts | 15 +- .../server/api/routers/organization.ts | 89 +- apps/dokploy/server/api/routers/user.ts | 2 +- packages/server/src/db/schema/account.ts | 1 + packages/server/src/db/schema/session.ts | 2 +- packages/server/src/lib/auth.ts | 49 +- packages/server/src/services/admin.ts | 30 +- packages/server/src/services/auth.ts | 128 +- 17 files changed, 5385 insertions(+), 329 deletions(-) create mode 100644 apps/dokploy/drizzle/0075_heavy_metal_master.sql create mode 100644 apps/dokploy/drizzle/meta/0075_snapshot.json diff --git a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx index e6067e7b3..3a36b780e 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx @@ -14,6 +14,7 @@ import { DropdownMenuLabel, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import copy from "copy-to-clipboard"; import { Table, TableBody, @@ -132,19 +133,22 @@ export const ShowInvitations = () => { Actions - {/* { - copy( - `${origin}/invitation?token=${user.user.token}`, - ); - toast.success( - "Invitation Copied to clipboard", - ); - }} - > - Copy Invitation - */} + {invitation.status === "pending" && ( + { + copy( + `${origin}/invitation?token=${invitation.id}`, + ); + toast.success( + "Invitation Copied to clipboard", + ); + }} + > + Copy Invitation + + )} + {invitation.status === "pending" && ( { const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -167,8 +167,7 @@ export const ShowUsers = () => { const { error } = await authClient.organization.removeMember( { - memberIdOrEmail: - member.user.id, + memberIdOrEmail: member.id, }, ); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index bd10352f5..f8c24d360 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -78,7 +78,6 @@ import { UserNav } from "./user-nav"; // The types of the queries we are going to use type AuthQueryOutput = inferRouterOutputs["auth"]["get"]; -type UserQueryOutput = inferRouterOutputs["user"]["byAuthId"]; type SingleNavItem = { isSingle?: true; @@ -87,7 +86,6 @@ type SingleNavItem = { icon?: LucideIcon; isEnabled?: (opts: { auth?: AuthQueryOutput; - user?: UserQueryOutput; isCloud: boolean; }) => boolean; }; @@ -105,7 +103,6 @@ type NavItem = items: SingleNavItem[]; isEnabled?: (opts: { auth?: AuthQueryOutput; - user?: UserQueryOutput; isCloud: boolean; }) => boolean; }; @@ -118,7 +115,6 @@ type ExternalLink = { icon: React.ComponentType<{ className?: string }>; isEnabled?: (opts: { auth?: AuthQueryOutput; - user?: UserQueryOutput; isCloud: boolean; }) => boolean; }; @@ -149,7 +145,7 @@ const MENU: Menu = { url: "/dashboard/monitoring", icon: BarChartHorizontalBigIcon, // Only enabled in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => !isCloud, + isEnabled: ({ auth, isCloud }) => !isCloud, }, { isSingle: true, @@ -157,9 +153,9 @@ const MENU: Menu = { url: "/dashboard/traefik", icon: GalleryVerticalEnd, // Only enabled for admins and users with access to Traefik files in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => + isEnabled: ({ auth, isCloud }) => !!( - (auth?.role === "owner" || user?.canAccessToTraefikFiles) && + (auth?.role === "owner" || auth?.user?.canAccessToTraefikFiles) && !isCloud ), }, @@ -169,8 +165,11 @@ const MENU: Menu = { url: "/dashboard/docker", icon: BlocksIcon, // Only enabled for admins and users with access to Docker in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => - !!((auth?.role === "owner" || user?.canAccessToDocker) && !isCloud), + isEnabled: ({ auth, isCloud }) => + !!( + (auth?.role === "owner" || auth?.user?.canAccessToDocker) && + !isCloud + ), }, { isSingle: true, @@ -178,8 +177,11 @@ const MENU: Menu = { url: "/dashboard/swarm", icon: PieChart, // Only enabled for admins and users with access to Docker in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => - !!((auth?.role === "owner" || user?.canAccessToDocker) && !isCloud), + isEnabled: ({ auth, isCloud }) => + !!( + (auth?.role === "owner" || auth?.user?.canAccessToDocker) && + !isCloud + ), }, { isSingle: true, @@ -187,8 +189,11 @@ const MENU: Menu = { url: "/dashboard/requests", icon: Forward, // Only enabled for admins and users with access to Docker in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => - !!((auth?.role === "owner" || user?.canAccessToDocker) && !isCloud), + isEnabled: ({ auth, isCloud }) => + !!( + (auth?.role === "owner" || auth?.user?.canAccessToDocker) && + !isCloud + ), }, // Legacy unused menu, adjusted to the new structure @@ -255,8 +260,7 @@ const MENU: Menu = { url: "/dashboard/settings/server", icon: Activity, // Only enabled for admins in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => - !!(auth?.role === "owner" && !isCloud), + isEnabled: ({ auth, isCloud }) => !!(auth?.role === "owner" && !isCloud), }, { isSingle: true, @@ -270,7 +274,7 @@ const MENU: Menu = { url: "/dashboard/settings/servers", icon: Server, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth, isCloud }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -278,7 +282,7 @@ const MENU: Menu = { icon: Users, url: "/dashboard/settings/users", // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -286,8 +290,8 @@ const MENU: Menu = { icon: KeyRound, url: "/dashboard/settings/ssh-keys", // Only enabled for admins and users with access to SSH keys - isEnabled: ({ auth, user }) => - !!(auth?.role === "owner" || user?.canAccessToSSHKeys), + isEnabled: ({ auth }) => + !!(auth?.role === "owner" || auth?.user?.canAccessToSSHKeys), }, { isSingle: true, @@ -295,8 +299,8 @@ const MENU: Menu = { url: "/dashboard/settings/git-providers", icon: GitBranch, // Only enabled for admins and users with access to Git providers - isEnabled: ({ auth, user }) => - !!(auth?.role === "owner" || user?.canAccessToGitProviders), + isEnabled: ({ auth }) => + !!(auth?.role === "owner" || auth?.user?.canAccessToGitProviders), }, { isSingle: true, @@ -304,7 +308,7 @@ const MENU: Menu = { url: "/dashboard/settings/registry", icon: Package, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -312,7 +316,7 @@ const MENU: Menu = { url: "/dashboard/settings/destinations", icon: Database, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { @@ -321,7 +325,7 @@ const MENU: Menu = { url: "/dashboard/settings/certificates", icon: ShieldCheck, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -329,8 +333,7 @@ const MENU: Menu = { url: "/dashboard/settings/cluster", icon: Boxes, // Only enabled for admins in non-cloud environments - isEnabled: ({ auth, user, isCloud }) => - !!(auth?.role === "owner" && !isCloud), + isEnabled: ({ auth, isCloud }) => !!(auth?.role === "owner" && !isCloud), }, { isSingle: true, @@ -338,7 +341,7 @@ const MENU: Menu = { url: "/dashboard/settings/notifications", icon: Bell, // Only enabled for admins - isEnabled: ({ auth, user, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -346,8 +349,7 @@ const MENU: Menu = { url: "/dashboard/settings/billing", icon: CreditCard, // Only enabled for admins in cloud environments - isEnabled: ({ auth, user, isCloud }) => - !!(auth?.role === "owner" && isCloud), + isEnabled: ({ auth, isCloud }) => !!(auth?.role === "owner" && isCloud), }, ], @@ -383,7 +385,6 @@ const MENU: Menu = { */ function createMenuForAuthUser(opts: { auth?: AuthQueryOutput; - user?: UserQueryOutput; isCloud: boolean; }): Menu { return { @@ -394,7 +395,6 @@ function createMenuForAuthUser(opts: { ? true : item.isEnabled({ auth: opts.auth, - user: opts.user, isCloud: opts.isCloud, }), ), @@ -405,7 +405,6 @@ function createMenuForAuthUser(opts: { ? true : item.isEnabled({ auth: opts.auth, - user: opts.user, isCloud: opts.isCloud, }), ), @@ -416,7 +415,6 @@ function createMenuForAuthUser(opts: { ? true : item.isEnabled({ auth: opts.auth, - user: opts.user, isCloud: opts.isCloud, }), ), @@ -525,10 +523,12 @@ const data = { ], }; -const teams = data.teams; function SidebarLogo() { const { state } = useSidebar(); + const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: user } = api.auth.get.useQuery(); const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); + const { data: session } = authClient.useSession(); const { data: organizations, refetch, @@ -617,42 +617,51 @@ function SidebarLogo() { />
{org.name} - {/* ⌘{index + 1} */} - {/* */} -
- - { - await deleteOrganization({ - organizationId: org.id, - }) - .then(() => { - refetch(); - toast.success("Port deleted successfully"); + {(org.ownerId === session?.user?.id || isCloud) && ( +
+ + { + await deleteOrganization({ + organizationId: org.id, }) - .catch(() => { - toast.error("Error deleting port"); - }); - }} - > - - -
+ +
+
+ )}
))} - - + {!isCloud && user?.role === "owner" && ( + <> + + + + )} @@ -706,14 +715,6 @@ export default function Page({ children }: Props) { const pathname = usePathname(); const currentPath = router.pathname; const { data: auth } = api.auth.get.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: auth?.id || "", - }, - { - enabled: !!auth?.id && auth?.role === "member", - }, - ); const includesProjects = pathname?.includes("/dashboard/project"); const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); @@ -722,7 +723,7 @@ export default function Page({ children }: Props) { home: filteredHome, settings: filteredSettings, help, - } = createMenuForAuthUser({ auth, user, isCloud: !!isCloud }); + } = createMenuForAuthUser({ auth, isCloud: !!isCloud }); const activeItem = findActiveNavItem( [...filteredHome, ...filteredSettings], diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 49fc92e59..809f9c178 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -32,14 +32,7 @@ export const UserNav = () => { const router = useRouter(); const { data } = api.auth.get.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); - const { data: user } = api.user.byAuthId.useQuery( - { - authId: data?.id || "", - }, - { - enabled: !!data?.id && data?.role === "member", - }, - ); + const { locale, setLocale } = useLocale(); // const { mutateAsync } = api.auth.logout.useMutation(); @@ -99,7 +92,8 @@ export const UserNav = () => { > Monitoring - {(data?.role === "owner" || user?.canAccessToTraefikFiles) && ( + {(data?.role === "owner" || + data?.user?.canAccessToTraefikFiles) && ( { @@ -109,7 +103,7 @@ export const UserNav = () => { Traefik )} - {(data?.role === "owner" || user?.canAccessToDocker) && ( + {(data?.role === "owner" || data?.user?.canAccessToDocker) && ( { diff --git a/apps/dokploy/drizzle/0075_heavy_metal_master.sql b/apps/dokploy/drizzle/0075_heavy_metal_master.sql new file mode 100644 index 000000000..850f532d0 --- /dev/null +++ b/apps/dokploy/drizzle/0075_heavy_metal_master.sql @@ -0,0 +1,3 @@ +ALTER TABLE "session_temp" DROP CONSTRAINT "session_temp_user_id_user_temp_id_fk"; +--> statement-breakpoint +ALTER TABLE "session_temp" ADD CONSTRAINT "session_temp_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/0075_snapshot.json b/apps/dokploy/drizzle/meta/0075_snapshot.json new file mode 100644 index 000000000..fb28e29c9 --- /dev/null +++ b/apps/dokploy/drizzle/meta/0075_snapshot.json @@ -0,0 +1,4878 @@ +{ + "id": "c5e17a87-0aa3-4178-be24-cfa7cde0f75d", + "prevId": "9cb79f1e-14c2-4deb-b1ab-a1d038f72356", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.application": { + "name": "application", + "schema": "", + "columns": { + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewEnv": { + "name": "previewEnv", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewBuildArgs": { + "name": "previewBuildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewWildcard": { + "name": "previewWildcard", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewPort": { + "name": "previewPort", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "previewHttps": { + "name": "previewHttps", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "previewPath": { + "name": "previewPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "previewLimit": { + "name": "previewLimit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3 + }, + "isPreviewDeploymentsActive": { + "name": "isPreviewDeploymentsActive", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "buildArgs": { + "name": "buildArgs", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "subtitle": { + "name": "subtitle", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "buildPath": { + "name": "buildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBuildPath": { + "name": "gitlabBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBuildPath": { + "name": "bitbucketBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBuildPath": { + "name": "customGitBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerfile": { + "name": "dockerfile", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerContextPath": { + "name": "dockerContextPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerBuildStage": { + "name": "dockerBuildStage", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dropBuildPath": { + "name": "dropBuildPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "healthCheckSwarm": { + "name": "healthCheckSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "restartPolicySwarm": { + "name": "restartPolicySwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "placementSwarm": { + "name": "placementSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "updateConfigSwarm": { + "name": "updateConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "rollbackConfigSwarm": { + "name": "rollbackConfigSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "modeSwarm": { + "name": "modeSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "labelsSwarm": { + "name": "labelsSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "networkSwarm": { + "name": "networkSwarm", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "replicas": { + "name": "replicas", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 1 + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "buildType": { + "name": "buildType", + "type": "buildType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'nixpacks'" + }, + "herokuVersion": { + "name": "herokuVersion", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'24'" + }, + "publishDirectory": { + "name": "publishDirectory", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "application_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "application_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "application", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_registryId_registry_registryId_fk": { + "name": "application_registryId_registry_registryId_fk", + "tableFrom": "application", + "tableTo": "registry", + "columnsFrom": [ + "registryId" + ], + "columnsTo": [ + "registryId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_projectId_project_projectId_fk": { + "name": "application_projectId_project_projectId_fk", + "tableFrom": "application", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "application_githubId_github_githubId_fk": { + "name": "application_githubId_github_githubId_fk", + "tableFrom": "application", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_gitlabId_gitlab_gitlabId_fk": { + "name": "application_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "application", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "application_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "application", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "application_serverId_server_serverId_fk": { + "name": "application_serverId_server_serverId_fk", + "tableFrom": "application", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "application_appName_unique": { + "name": "application_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.postgres": { + "name": "postgres", + "schema": "", + "columns": { + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "postgres_projectId_project_projectId_fk": { + "name": "postgres_projectId_project_projectId_fk", + "tableFrom": "postgres", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "postgres_serverId_server_serverId_fk": { + "name": "postgres_serverId_server_serverId_fk", + "tableFrom": "postgres", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "postgres_appName_unique": { + "name": "postgres_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_temp": { + "name": "user_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "isRegistered": { + "name": "isRegistered", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "expirationDate": { + "name": "expirationDate", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false, + "default": "now()" + }, + "canCreateProjects": { + "name": "canCreateProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToSSHKeys": { + "name": "canAccessToSSHKeys", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canCreateServices": { + "name": "canCreateServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteProjects": { + "name": "canDeleteProjects", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canDeleteServices": { + "name": "canDeleteServices", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToDocker": { + "name": "canAccessToDocker", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToAPI": { + "name": "canAccessToAPI", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToGitProviders": { + "name": "canAccessToGitProviders", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "canAccessToTraefikFiles": { + "name": "canAccessToTraefikFiles", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "accesedProjects": { + "name": "accesedProjects", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "accesedServices": { + "name": "accesedServices", + "type": "text[]", + "primaryKey": false, + "notNull": true, + "default": "ARRAY[]::text[]" + }, + "two_factor_enabled": { + "name": "two_factor_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "banned": { + "name": "banned", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "serverIp": { + "name": "serverIp", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "letsEncryptEmail": { + "name": "letsEncryptEmail", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sshPrivateKey": { + "name": "sshPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enableLogRotation": { + "name": "enableLogRotation", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "enablePaidFeatures": { + "name": "enablePaidFeatures", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Dokploy\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"retentionDays\":2,\"cronJob\":\"\",\"urlCallback\":\"\",\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + }, + "cleanupCacheApplications": { + "name": "cleanupCacheApplications", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnPreviews": { + "name": "cleanupCacheOnPreviews", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "cleanupCacheOnCompose": { + "name": "cleanupCacheOnCompose", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "stripeCustomerId": { + "name": "stripeCustomerId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "stripeSubscriptionId": { + "name": "stripeSubscriptionId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serversQuantity": { + "name": "serversQuantity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_temp_email_unique": { + "name": "user_temp_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.project": { + "name": "project", + "schema": "", + "columns": { + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": { + "project_organizationId_organization_id_fk": { + "name": "project_organizationId_organization_id_fk", + "tableFrom": "project", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.domain": { + "name": "domain", + "schema": "", + "columns": { + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "host": { + "name": "host", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "https": { + "name": "https", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": false, + "default": 3000 + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'/'" + }, + "serviceName": { + "name": "serviceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "domainType": { + "name": "domainType", + "type": "domainType", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'application'" + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "certificateType": { + "name": "certificateType", + "type": "certificateType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'none'" + } + }, + "indexes": {}, + "foreignKeys": { + "domain_composeId_compose_composeId_fk": { + "name": "domain_composeId_compose_composeId_fk", + "tableFrom": "domain", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_applicationId_application_applicationId_fk": { + "name": "domain_applicationId_application_applicationId_fk", + "tableFrom": "domain", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "domain_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "domain", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mariadb": { + "name": "mariadb", + "schema": "", + "columns": { + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mariadb_projectId_project_projectId_fk": { + "name": "mariadb_projectId_project_projectId_fk", + "tableFrom": "mariadb", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mariadb_serverId_server_serverId_fk": { + "name": "mariadb_serverId_server_serverId_fk", + "tableFrom": "mariadb", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mariadb_appName_unique": { + "name": "mariadb_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mongo": { + "name": "mongo", + "schema": "", + "columns": { + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "replicaSets": { + "name": "replicaSets", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + } + }, + "indexes": {}, + "foreignKeys": { + "mongo_projectId_project_projectId_fk": { + "name": "mongo_projectId_project_projectId_fk", + "tableFrom": "mongo", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mongo_serverId_server_serverId_fk": { + "name": "mongo_serverId_server_serverId_fk", + "tableFrom": "mongo", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mongo_appName_unique": { + "name": "mongo_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mysql": { + "name": "mysql", + "schema": "", + "columns": { + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "databaseName": { + "name": "databaseName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseUser": { + "name": "databaseUser", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databasePassword": { + "name": "databasePassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "rootPassword": { + "name": "rootPassword", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mysql_projectId_project_projectId_fk": { + "name": "mysql_projectId_project_projectId_fk", + "tableFrom": "mysql", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mysql_serverId_server_serverId_fk": { + "name": "mysql_serverId_server_serverId_fk", + "tableFrom": "mysql", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "mysql_appName_unique": { + "name": "mysql_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.backup": { + "name": "backup", + "schema": "", + "columns": { + "backupId": { + "name": "backupId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "schedule": { + "name": "schedule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "database": { + "name": "database", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "databaseType": { + "name": "databaseType", + "type": "databaseType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "backup_destinationId_destination_destinationId_fk": { + "name": "backup_destinationId_destination_destinationId_fk", + "tableFrom": "backup", + "tableTo": "destination", + "columnsFrom": [ + "destinationId" + ], + "columnsTo": [ + "destinationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_postgresId_postgres_postgresId_fk": { + "name": "backup_postgresId_postgres_postgresId_fk", + "tableFrom": "backup", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mariadbId_mariadb_mariadbId_fk": { + "name": "backup_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "backup", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mysqlId_mysql_mysqlId_fk": { + "name": "backup_mysqlId_mysql_mysqlId_fk", + "tableFrom": "backup", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "backup_mongoId_mongo_mongoId_fk": { + "name": "backup_mongoId_mongo_mongoId_fk", + "tableFrom": "backup", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.destination": { + "name": "destination", + "schema": "", + "columns": { + "destinationId": { + "name": "destinationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessKey": { + "name": "accessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "secretAccessKey": { + "name": "secretAccessKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "destination_organizationId_organization_id_fk": { + "name": "destination_organizationId_organization_id_fk", + "tableFrom": "destination", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "deploymentId": { + "name": "deploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "deploymentStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": false, + "default": "'running'" + }, + "logPath": { + "name": "logPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "isPreviewDeployment": { + "name": "isPreviewDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "errorMessage": { + "name": "errorMessage", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_applicationId_application_applicationId_fk": { + "name": "deployment_applicationId_application_applicationId_fk", + "tableFrom": "deployment", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_composeId_compose_composeId_fk": { + "name": "deployment_composeId_compose_composeId_fk", + "tableFrom": "deployment", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_serverId_server_serverId_fk": { + "name": "deployment_serverId_server_serverId_fk", + "tableFrom": "deployment", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk": { + "name": "deployment_previewDeploymentId_preview_deployments_previewDeploymentId_fk", + "tableFrom": "deployment", + "tableTo": "preview_deployments", + "columnsFrom": [ + "previewDeploymentId" + ], + "columnsTo": [ + "previewDeploymentId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.mount": { + "name": "mount", + "schema": "", + "columns": { + "mountId": { + "name": "mountId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "type": { + "name": "type", + "type": "mountType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "hostPath": { + "name": "hostPath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "volumeName": { + "name": "volumeName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "filePath": { + "name": "filePath", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "content": { + "name": "content", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serviceType": { + "name": "serviceType", + "type": "serviceType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'application'" + }, + "mountPath": { + "name": "mountPath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "postgresId": { + "name": "postgresId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mariadbId": { + "name": "mariadbId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mongoId": { + "name": "mongoId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "mysqlId": { + "name": "mysqlId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "mount_applicationId_application_applicationId_fk": { + "name": "mount_applicationId_application_applicationId_fk", + "tableFrom": "mount", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_postgresId_postgres_postgresId_fk": { + "name": "mount_postgresId_postgres_postgresId_fk", + "tableFrom": "mount", + "tableTo": "postgres", + "columnsFrom": [ + "postgresId" + ], + "columnsTo": [ + "postgresId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mariadbId_mariadb_mariadbId_fk": { + "name": "mount_mariadbId_mariadb_mariadbId_fk", + "tableFrom": "mount", + "tableTo": "mariadb", + "columnsFrom": [ + "mariadbId" + ], + "columnsTo": [ + "mariadbId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mongoId_mongo_mongoId_fk": { + "name": "mount_mongoId_mongo_mongoId_fk", + "tableFrom": "mount", + "tableTo": "mongo", + "columnsFrom": [ + "mongoId" + ], + "columnsTo": [ + "mongoId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_mysqlId_mysql_mysqlId_fk": { + "name": "mount_mysqlId_mysql_mysqlId_fk", + "tableFrom": "mount", + "tableTo": "mysql", + "columnsFrom": [ + "mysqlId" + ], + "columnsTo": [ + "mysqlId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_redisId_redis_redisId_fk": { + "name": "mount_redisId_redis_redisId_fk", + "tableFrom": "mount", + "tableTo": "redis", + "columnsFrom": [ + "redisId" + ], + "columnsTo": [ + "redisId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "mount_composeId_compose_composeId_fk": { + "name": "mount_composeId_compose_composeId_fk", + "tableFrom": "mount", + "tableTo": "compose", + "columnsFrom": [ + "composeId" + ], + "columnsTo": [ + "composeId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.certificate": { + "name": "certificate", + "schema": "", + "columns": { + "certificateId": { + "name": "certificateId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificateData": { + "name": "certificateData", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "certificatePath": { + "name": "certificatePath", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "autoRenew": { + "name": "autoRenew", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "certificate_organizationId_organization_id_fk": { + "name": "certificate_organizationId_organization_id_fk", + "tableFrom": "certificate", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "certificate_serverId_server_serverId_fk": { + "name": "certificate_serverId_server_serverId_fk", + "tableFrom": "certificate", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "certificate_certificatePath_unique": { + "name": "certificate_certificatePath_unique", + "nullsNotDistinct": false, + "columns": [ + "certificatePath" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session_temp": { + "name": "session_temp", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "session_temp_user_id_user_temp_id_fk": { + "name": "session_temp_user_id_user_temp_id_fk", + "tableFrom": "session_temp", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_temp_token_unique": { + "name": "session_temp_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redirect": { + "name": "redirect", + "schema": "", + "columns": { + "redirectId": { + "name": "redirectId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "regex": { + "name": "regex", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "replacement": { + "name": "replacement", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permanent": { + "name": "permanent", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "uniqueConfigKey": { + "name": "uniqueConfigKey", + "type": "serial", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "redirect_applicationId_application_applicationId_fk": { + "name": "redirect_applicationId_application_applicationId_fk", + "tableFrom": "redirect", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.security": { + "name": "security", + "schema": "", + "columns": { + "securityId": { + "name": "securityId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "security_applicationId_application_applicationId_fk": { + "name": "security_applicationId_application_applicationId_fk", + "tableFrom": "security", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "security_username_applicationId_unique": { + "name": "security_username_applicationId_unique", + "nullsNotDistinct": false, + "columns": [ + "username", + "applicationId" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.port": { + "name": "port", + "schema": "", + "columns": { + "portId": { + "name": "portId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "publishedPort": { + "name": "publishedPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "targetPort": { + "name": "targetPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "protocol": { + "name": "protocol", + "type": "protocolType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "port_applicationId_application_applicationId_fk": { + "name": "port_applicationId_application_applicationId_fk", + "tableFrom": "port", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.redis": { + "name": "redis", + "schema": "", + "columns": { + "redisId": { + "name": "redisId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dockerImage": { + "name": "dockerImage", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryReservation": { + "name": "memoryReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "memoryLimit": { + "name": "memoryLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuReservation": { + "name": "cpuReservation", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "cpuLimit": { + "name": "cpuLimit", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "externalPort": { + "name": "externalPort", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationStatus": { + "name": "applicationStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "redis_projectId_project_projectId_fk": { + "name": "redis_projectId_project_projectId_fk", + "tableFrom": "redis", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "redis_serverId_server_serverId_fk": { + "name": "redis_serverId_server_serverId_fk", + "tableFrom": "redis", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "redis_appName_unique": { + "name": "redis_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.compose": { + "name": "compose", + "schema": "", + "columns": { + "composeId": { + "name": "composeId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "env": { + "name": "env", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "composeFile": { + "name": "composeFile", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "sourceType": { + "name": "sourceType", + "type": "sourceTypeCompose", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "composeType": { + "name": "composeType", + "type": "composeType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'docker-compose'" + }, + "repository": { + "name": "repository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "autoDeploy": { + "name": "autoDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "gitlabProjectId": { + "name": "gitlabProjectId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitlabRepository": { + "name": "gitlabRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabOwner": { + "name": "gitlabOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabBranch": { + "name": "gitlabBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabPathNamespace": { + "name": "gitlabPathNamespace", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketRepository": { + "name": "bitbucketRepository", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketOwner": { + "name": "bitbucketOwner", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketBranch": { + "name": "bitbucketBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitUrl": { + "name": "customGitUrl", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitBranch": { + "name": "customGitBranch", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "customGitSSHKeyId": { + "name": "customGitSSHKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "composePath": { + "name": "composePath", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'./docker-compose.yml'" + }, + "suffix": { + "name": "suffix", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "randomize": { + "name": "randomize", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "isolatedDeployment": { + "name": "isolatedDeployment", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "composeStatus": { + "name": "composeStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "projectId": { + "name": "projectId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk": { + "name": "compose_customGitSSHKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "compose", + "tableTo": "ssh-key", + "columnsFrom": [ + "customGitSSHKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_projectId_project_projectId_fk": { + "name": "compose_projectId_project_projectId_fk", + "tableFrom": "compose", + "tableTo": "project", + "columnsFrom": [ + "projectId" + ], + "columnsTo": [ + "projectId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "compose_githubId_github_githubId_fk": { + "name": "compose_githubId_github_githubId_fk", + "tableFrom": "compose", + "tableTo": "github", + "columnsFrom": [ + "githubId" + ], + "columnsTo": [ + "githubId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_gitlabId_gitlab_gitlabId_fk": { + "name": "compose_gitlabId_gitlab_gitlabId_fk", + "tableFrom": "compose", + "tableTo": "gitlab", + "columnsFrom": [ + "gitlabId" + ], + "columnsTo": [ + "gitlabId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_bitbucketId_bitbucket_bitbucketId_fk": { + "name": "compose_bitbucketId_bitbucket_bitbucketId_fk", + "tableFrom": "compose", + "tableTo": "bitbucket", + "columnsFrom": [ + "bitbucketId" + ], + "columnsTo": [ + "bitbucketId" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "compose_serverId_server_serverId_fk": { + "name": "compose_serverId_server_serverId_fk", + "tableFrom": "compose", + "tableTo": "server", + "columnsFrom": [ + "serverId" + ], + "columnsTo": [ + "serverId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.registry": { + "name": "registry", + "schema": "", + "columns": { + "registryId": { + "name": "registryId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "registryName": { + "name": "registryName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "imagePrefix": { + "name": "imagePrefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "registryUrl": { + "name": "registryUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "selfHosted": { + "name": "selfHosted", + "type": "RegistryType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'cloud'" + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "registry_organizationId_organization_id_fk": { + "name": "registry_organizationId_organization_id_fk", + "tableFrom": "registry", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.discord": { + "name": "discord", + "schema": "", + "columns": { + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.email": { + "name": "email", + "schema": "", + "columns": { + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "smtpServer": { + "name": "smtpServer", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "smtpPort": { + "name": "smtpPort", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "fromAddress": { + "name": "fromAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "toAddress": { + "name": "toAddress", + "type": "text[]", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gotify": { + "name": "gotify", + "schema": "", + "columns": { + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "serverUrl": { + "name": "serverUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appToken": { + "name": "appToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 5 + }, + "decoration": { + "name": "decoration", + "type": "boolean", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.notification": { + "name": "notification", + "schema": "", + "columns": { + "notificationId": { + "name": "notificationId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "appDeploy": { + "name": "appDeploy", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "appBuildError": { + "name": "appBuildError", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "databaseBackup": { + "name": "databaseBackup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dokployRestart": { + "name": "dokployRestart", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dockerCleanup": { + "name": "dockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "serverThreshold": { + "name": "serverThreshold", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "notificationType": { + "name": "notificationType", + "type": "notificationType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "discordId": { + "name": "discordId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "emailId": { + "name": "emailId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gotifyId": { + "name": "gotifyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "notification_slackId_slack_slackId_fk": { + "name": "notification_slackId_slack_slackId_fk", + "tableFrom": "notification", + "tableTo": "slack", + "columnsFrom": [ + "slackId" + ], + "columnsTo": [ + "slackId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_telegramId_telegram_telegramId_fk": { + "name": "notification_telegramId_telegram_telegramId_fk", + "tableFrom": "notification", + "tableTo": "telegram", + "columnsFrom": [ + "telegramId" + ], + "columnsTo": [ + "telegramId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_discordId_discord_discordId_fk": { + "name": "notification_discordId_discord_discordId_fk", + "tableFrom": "notification", + "tableTo": "discord", + "columnsFrom": [ + "discordId" + ], + "columnsTo": [ + "discordId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_emailId_email_emailId_fk": { + "name": "notification_emailId_email_emailId_fk", + "tableFrom": "notification", + "tableTo": "email", + "columnsFrom": [ + "emailId" + ], + "columnsTo": [ + "emailId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_gotifyId_gotify_gotifyId_fk": { + "name": "notification_gotifyId_gotify_gotifyId_fk", + "tableFrom": "notification", + "tableTo": "gotify", + "columnsFrom": [ + "gotifyId" + ], + "columnsTo": [ + "gotifyId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_organizationId_organization_id_fk": { + "name": "notification_organizationId_organization_id_fk", + "tableFrom": "notification", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.slack": { + "name": "slack", + "schema": "", + "columns": { + "slackId": { + "name": "slackId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "webhookUrl": { + "name": "webhookUrl", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "channel": { + "name": "channel", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.telegram": { + "name": "telegram", + "schema": "", + "columns": { + "telegramId": { + "name": "telegramId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "botToken": { + "name": "botToken", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "chatId": { + "name": "chatId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.ssh-key": { + "name": "ssh-key", + "schema": "", + "columns": { + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "privateKey": { + "name": "privateKey", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "publicKey": { + "name": "publicKey", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "lastUsedAt": { + "name": "lastUsedAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "ssh-key_organizationId_organization_id_fk": { + "name": "ssh-key_organizationId_organization_id_fk", + "tableFrom": "ssh-key", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.git_provider": { + "name": "git_provider", + "schema": "", + "columns": { + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerType": { + "name": "providerType", + "type": "gitProviderType", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'github'" + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "git_provider_organizationId_organization_id_fk": { + "name": "git_provider_organizationId_organization_id_fk", + "tableFrom": "git_provider", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.bitbucket": { + "name": "bitbucket", + "schema": "", + "columns": { + "bitbucketId": { + "name": "bitbucketId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "bitbucketUsername": { + "name": "bitbucketUsername", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "appPassword": { + "name": "appPassword", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "bitbucketWorkspaceName": { + "name": "bitbucketWorkspaceName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "bitbucket_gitProviderId_git_provider_gitProviderId_fk": { + "name": "bitbucket_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "bitbucket", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.github": { + "name": "github", + "schema": "", + "columns": { + "githubId": { + "name": "githubId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "githubAppName": { + "name": "githubAppName", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubAppId": { + "name": "githubAppId", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "githubClientId": { + "name": "githubClientId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubClientSecret": { + "name": "githubClientSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubInstallationId": { + "name": "githubInstallationId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubPrivateKey": { + "name": "githubPrivateKey", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "githubWebhookSecret": { + "name": "githubWebhookSecret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "github_gitProviderId_git_provider_gitProviderId_fk": { + "name": "github_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "github", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.gitlab": { + "name": "gitlab", + "schema": "", + "columns": { + "gitlabId": { + "name": "gitlabId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "gitlabUrl": { + "name": "gitlabUrl", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'https://gitlab.com'" + }, + "application_id": { + "name": "application_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "gitProviderId": { + "name": "gitProviderId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "gitlab_gitProviderId_git_provider_gitProviderId_fk": { + "name": "gitlab_gitProviderId_git_provider_gitProviderId_fk", + "tableFrom": "gitlab", + "tableTo": "git_provider", + "columnsFrom": [ + "gitProviderId" + ], + "columnsTo": [ + "gitProviderId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.server": { + "name": "server", + "schema": "", + "columns": { + "serverId": { + "name": "serverId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "port": { + "name": "port", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'root'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "enableDockerCleanup": { + "name": "enableDockerCleanup", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "organizationId": { + "name": "organizationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "serverStatus": { + "name": "serverStatus", + "type": "serverStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'active'" + }, + "command": { + "name": "command", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "sshKeyId": { + "name": "sshKeyId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metricsConfig": { + "name": "metricsConfig", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{\"server\":{\"type\":\"Remote\",\"refreshRate\":60,\"port\":4500,\"token\":\"\",\"urlCallback\":\"\",\"cronJob\":\"\",\"retentionDays\":2,\"thresholds\":{\"cpu\":0,\"memory\":0}},\"containers\":{\"refreshRate\":60,\"services\":{\"include\":[],\"exclude\":[]}}}'::jsonb" + } + }, + "indexes": {}, + "foreignKeys": { + "server_organizationId_organization_id_fk": { + "name": "server_organizationId_organization_id_fk", + "tableFrom": "server", + "tableTo": "organization", + "columnsFrom": [ + "organizationId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "server_sshKeyId_ssh-key_sshKeyId_fk": { + "name": "server_sshKeyId_ssh-key_sshKeyId_fk", + "tableFrom": "server", + "tableTo": "ssh-key", + "columnsFrom": [ + "sshKeyId" + ], + "columnsTo": [ + "sshKeyId" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.preview_deployments": { + "name": "preview_deployments", + "schema": "", + "columns": { + "previewDeploymentId": { + "name": "previewDeploymentId", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "branch": { + "name": "branch", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestId": { + "name": "pullRequestId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestNumber": { + "name": "pullRequestNumber", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestURL": { + "name": "pullRequestURL", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestTitle": { + "name": "pullRequestTitle", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "pullRequestCommentId": { + "name": "pullRequestCommentId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "previewStatus": { + "name": "previewStatus", + "type": "applicationStatus", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'idle'" + }, + "appName": { + "name": "appName", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "applicationId": { + "name": "applicationId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "domainId": { + "name": "domainId", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "preview_deployments_applicationId_application_applicationId_fk": { + "name": "preview_deployments_applicationId_application_applicationId_fk", + "tableFrom": "preview_deployments", + "tableTo": "application", + "columnsFrom": [ + "applicationId" + ], + "columnsTo": [ + "applicationId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "preview_deployments_domainId_domain_domainId_fk": { + "name": "preview_deployments_domainId_domain_domainId_fk", + "tableFrom": "preview_deployments", + "tableTo": "domain", + "columnsFrom": [ + "domainId" + ], + "columnsTo": [ + "domainId" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "preview_deployments_appName_unique": { + "name": "preview_deployments_appName_unique", + "nullsNotDistinct": false, + "columns": [ + "appName" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is2FAEnabled": { + "name": "is2FAEnabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "resetPasswordToken": { + "name": "resetPasswordToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "resetPasswordExpiresAt": { + "name": "resetPasswordExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationToken": { + "name": "confirmationToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "confirmationExpiresAt": { + "name": "confirmationExpiresAt", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_temp_id_fk": { + "name": "account_user_id_user_temp_id_fk", + "tableFrom": "account", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.invitation": { + "name": "invitation", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_temp_id_fk": { + "name": "invitation_inviter_id_user_temp_id_fk", + "tableFrom": "invitation", + "tableTo": "user_temp", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.member": { + "name": "member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_temp_id_fk": { + "name": "member_user_id_user_temp_id_fk", + "tableFrom": "member", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.organization": { + "name": "organization", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "owner_id": { + "name": "owner_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "organization_owner_id_user_temp_id_fk": { + "name": "organization_owner_id_user_temp_id_fk", + "tableFrom": "organization", + "tableTo": "user_temp", + "columnsFrom": [ + "owner_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "nullsNotDistinct": false, + "columns": [ + "slug" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.two_factor": { + "name": "two_factor", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "secret": { + "name": "secret", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "backup_codes": { + "name": "backup_codes", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "two_factor_user_id_user_temp_id_fk": { + "name": "two_factor_user_id_user_temp_id_fk", + "tableFrom": "two_factor", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.buildType": { + "name": "buildType", + "schema": "public", + "values": [ + "dockerfile", + "heroku_buildpacks", + "paketo_buildpacks", + "nixpacks", + "static" + ] + }, + "public.sourceType": { + "name": "sourceType", + "schema": "public", + "values": [ + "docker", + "git", + "github", + "gitlab", + "bitbucket", + "drop" + ] + }, + "public.domainType": { + "name": "domainType", + "schema": "public", + "values": [ + "compose", + "application", + "preview" + ] + }, + "public.databaseType": { + "name": "databaseType", + "schema": "public", + "values": [ + "postgres", + "mariadb", + "mysql", + "mongo" + ] + }, + "public.deploymentStatus": { + "name": "deploymentStatus", + "schema": "public", + "values": [ + "running", + "done", + "error" + ] + }, + "public.mountType": { + "name": "mountType", + "schema": "public", + "values": [ + "bind", + "volume", + "file" + ] + }, + "public.serviceType": { + "name": "serviceType", + "schema": "public", + "values": [ + "application", + "postgres", + "mysql", + "mariadb", + "mongo", + "redis", + "compose" + ] + }, + "public.protocolType": { + "name": "protocolType", + "schema": "public", + "values": [ + "tcp", + "udp" + ] + }, + "public.applicationStatus": { + "name": "applicationStatus", + "schema": "public", + "values": [ + "idle", + "running", + "done", + "error" + ] + }, + "public.certificateType": { + "name": "certificateType", + "schema": "public", + "values": [ + "letsencrypt", + "none" + ] + }, + "public.composeType": { + "name": "composeType", + "schema": "public", + "values": [ + "docker-compose", + "stack" + ] + }, + "public.sourceTypeCompose": { + "name": "sourceTypeCompose", + "schema": "public", + "values": [ + "git", + "github", + "gitlab", + "bitbucket", + "raw" + ] + }, + "public.RegistryType": { + "name": "RegistryType", + "schema": "public", + "values": [ + "selfHosted", + "cloud" + ] + }, + "public.notificationType": { + "name": "notificationType", + "schema": "public", + "values": [ + "slack", + "telegram", + "discord", + "email", + "gotify" + ] + }, + "public.gitProviderType": { + "name": "gitProviderType", + "schema": "public", + "values": [ + "github", + "gitlab", + "bitbucket" + ] + }, + "public.serverStatus": { + "name": "serverStatus", + "schema": "public", + "values": [ + "active", + "inactive" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 6f38bc1bb..674994c1d 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -526,6 +526,13 @@ "when": 1739773539709, "tag": "0074_lowly_jack_power", "breakpoints": true + }, + { + "idx": 75, + "version": "7", + "when": 1739781534192, + "tag": "0075_heavy_metal_master", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index 7945bf86d..e9fb65608 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -28,6 +28,8 @@ export async function getServerSideProps( ) { const { req, res } = ctx; const { user, session } = await validateRequest(req); + + console.log("user", user, session); if (!user || user.role === "member") { return { redirect: { diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx index 0dd8dbe4d..60cdfa854 100644 --- a/apps/dokploy/pages/invitation.tsx +++ b/apps/dokploy/pages/invitation.tsx @@ -1,4 +1,5 @@ import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; +import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; import { Button } from "@/components/ui/button"; import { @@ -16,21 +17,24 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { IS_CLOUD, getUserByToken } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle } from "lucide-react"; +import { AlertCircle, AlertTriangle } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; -import superjson from "superjson"; import { z } from "zod"; const registerSchema = z .object({ + name: z.string().min(1, { + message: "Name is required", + }), email: z .string() .min(1, { @@ -39,7 +43,6 @@ const registerSchema = z .email({ message: "Email must be a valid email", }), - password: z .string() .min(1, { @@ -72,9 +75,15 @@ interface Props { token: string; invitation: Awaited>; isCloud: boolean; + userAlreadyExists: boolean; } -const Invitation = ({ token, invitation, isCloud }: Props) => { +const Invitation = ({ + token, + invitation, + isCloud, + userAlreadyExists, +}: Props) => { const router = useRouter(); const { data } = api.admin.getUserByToken.useQuery( { @@ -91,6 +100,7 @@ const Invitation = ({ token, invitation, isCloud }: Props) => { const form = useForm({ defaultValues: { + name: "", email: "", password: "", confirmPassword: "", @@ -109,20 +119,32 @@ const Invitation = ({ token, invitation, isCloud }: Props) => { }, [form, form.reset, form.formState.isSubmitSuccessful, data]); const onSubmit = async (values: Register) => { - await mutateAsync({ - id: data?.id, - password: values.password, - token: token, - }) - .then(() => { - toast.success("User registered successfuly", { - description: - "Please check your inbox or spam folder to confirm your account.", - duration: 100000, - }); - router.push("/dashboard/projects"); - }) - .catch((e) => e); + try { + const { data, error } = await authClient.signUp.email({ + email: values.email, + password: values.password, + name: values.name, + fetchOptions: { + headers: { + "x-dokploy-token": token, + }, + }, + }); + + if (error) { + toast.error(error.message); + return; + } + + const result = await authClient.organization.acceptInvitation({ + invitationId: token, + }); + + toast.success("Account created successfully"); + router.push("/dashboard/projects"); + } catch (error) { + toast.error("An error occurred while creating your account"); + } }; return ( @@ -139,114 +161,155 @@ const Invitation = ({ token, invitation, isCloud }: Props) => { Invitation - - Fill the form below to create your account - -
-
+ {userAlreadyExists ? ( +
+ +
+ Valid Invitation! + + We detected that you already have an account with this + email. Please sign in to accept the invitation. + +
+
- {isError && ( -
- - - {error?.message} - -
- )} + +
+ ) : ( + <> + + Fill the form below to create your account + +
+
- -
- -
- ( - - Email - - - - - - )} - /> - ( - - Password - - - - - - )} - /> + {isError && ( +
+ + + {error?.message} + +
+ )} - ( - - Confirm Password - - - - - - )} - /> - - -
+
+ ( + + Name + + + + + + )} + /> + ( + + Email + + + + + + )} + /> + ( + + Password + + + + + + )} + /> -
- {isCloud && ( - <> - ( + + Confirm Password + + + + + + )} + /> + +
- - - -
+ Register + +
+ +
+ {isCloud && ( + <> + + Login + + + Lost your password? + + + )} +
+ + + +
+ + )}
); }; - +// http://localhost:3000/invitation?token=CZK4BLrUdMa32RVkAdZiLsPDdvnPiAgZ +// /f7af93acc1a99eae864972ab4c92fee089f0d83473d415ede8e821e5dbabe79c export default Invitation; Invitation.getLayout = (page: ReactElement) => { return {page}; @@ -268,7 +331,17 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) { try { const invitation = await getUserByToken(token); - console.log("invitation", invitation); + + if (invitation.userAlreadyExists) { + return { + props: { + isCloud: IS_CLOUD, + token: token, + invitation: invitation, + userAlreadyExists: true, + }, + }; + } if (invitation.isExpired) { return { @@ -287,6 +360,7 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) { }, }; } catch (error) { + console.log("error", error); return { redirect: { permanent: true, diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 97e3a1fa4..b15ed616e 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -9,7 +9,7 @@ import { import { IS_CLOUD, createInvitation, - findUserByAuthId, + findOrganizationById, findUserById, getUserByToken, removeUserById, @@ -98,21 +98,20 @@ export const adminRouter = createTRPCRouter({ try { const user = await findUserById(input.id); - if (user.id !== ctx.user.ownerId) { + const organization = await findOrganizationById( + ctx.session?.activeOrganizationId || "", + ); + + if (organization?.ownerId !== ctx.user.ownerId) { throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to assign permissions", }); } + await updateUser(user.id, { ...input, }); - // await db - // .update(users) - // .set({ - // ...input, - // }) - // .where(eq(users.userId, input.userId)); } catch (error) { throw error; } diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index 444a80ba8..7158a01ae 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -1,29 +1,43 @@ import { db } from "@/server/db"; -import { invitation, member, organization } from "@/server/db/schema"; +import { + invitation, + member, + organization, + users_temp, +} from "@/server/db/schema"; import { TRPCError } from "@trpc/server"; -import { desc, eq } from "drizzle-orm"; +import { and, desc, eq, exists } from "drizzle-orm"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { adminProcedure, createTRPCRouter } from "../trpc"; +import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; +import { auth, IS_CLOUD } from "@dokploy/server/index"; export const organizationRouter = createTRPCRouter({ - create: adminProcedure + create: protectedProcedure .input( z.object({ name: z.string(), }), ) .mutation(async ({ ctx, input }) => { + if (ctx.user.rol !== "owner" && !IS_CLOUD) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Only the organization owner can create an organization", + }); + } const result = await db .insert(organization) .values({ ...input, slug: nanoid(), createdAt: new Date(), - ownerId: ctx.user.ownerId, + ownerId: ctx.user.id, }) .returning() .then((res) => res[0]); + console.log("result", result); + if (!result) { throw new TRPCError({ code: "INTERNAL_SERVER_ERROR", @@ -39,13 +53,24 @@ export const organizationRouter = createTRPCRouter({ }); return result; }), - all: adminProcedure.query(async ({ ctx }) => { - return await db.query.organization.findMany({ - where: eq(organization.ownerId, ctx.user.ownerId), - orderBy: [desc(organization.createdAt)], + all: protectedProcedure.query(async ({ ctx }) => { + const memberResult = await db.query.organization.findMany({ + where: (organization) => + exists( + db + .select() + .from(member) + .where( + and( + eq(member.organizationId, organization.id), + eq(member.userId, ctx.user.id), + ), + ), + ), }); + return memberResult; }), - one: adminProcedure + one: protectedProcedure .input( z.object({ organizationId: z.string(), @@ -56,7 +81,7 @@ export const organizationRouter = createTRPCRouter({ where: eq(organization.id, input.organizationId), }); }), - update: adminProcedure + update: protectedProcedure .input( z.object({ organizationId: z.string(), @@ -64,6 +89,12 @@ export const organizationRouter = createTRPCRouter({ }), ) .mutation(async ({ ctx, input }) => { + if (ctx.user.rol !== "owner" && !IS_CLOUD) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Only the organization owner can update it", + }); + } const result = await db .update(organization) .set({ name: input.name }) @@ -71,16 +102,41 @@ export const organizationRouter = createTRPCRouter({ .returning(); return result[0]; }), - delete: adminProcedure + delete: protectedProcedure .input( z.object({ organizationId: z.string(), }), ) .mutation(async ({ ctx, input }) => { + if (ctx.user.rol !== "owner" && !IS_CLOUD) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Only the organization owner can delete it", + }); + } + const org = await db.query.organization.findFirst({ + where: eq(organization.id, input.organizationId), + }); + + if (!org) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Organization not found", + }); + } + + if (org.ownerId !== ctx.user.id) { + throw new TRPCError({ + code: "FORBIDDEN", + message: "Only the organization owner can delete it", + }); + } + const result = await db .delete(organization) .where(eq(organization.id, input.organizationId)); + return result; }), allInvitations: adminProcedure.query(async ({ ctx }) => { @@ -89,4 +145,13 @@ export const organizationRouter = createTRPCRouter({ orderBy: [desc(invitation.status)], }); }), + acceptInvitation: adminProcedure + .input(z.object({ invitationId: z.string() })) + .mutation(async ({ ctx, input }) => { + const result = await auth.api.acceptInvitation({ + invitationId: input.invitationId, + }); + + return result; + }), }); diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index c5382e9a9..2bfc06f78 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -1,8 +1,8 @@ import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { + IS_CLOUD, findUserByAuthId, findUserById, - IS_CLOUD, removeUserById, updateUser, verify2FA, diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 185de1368..969919030 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -71,6 +71,7 @@ export const organizationRelations = relations( }), servers: many(server), projects: many(projects), + members: many(member), }), ); diff --git a/packages/server/src/db/schema/session.ts b/packages/server/src/db/schema/session.ts index 99df9218e..f7c12dae0 100644 --- a/packages/server/src/db/schema/session.ts +++ b/packages/server/src/db/schema/session.ts @@ -12,7 +12,7 @@ export const session = pgTable("session_temp", { userAgent: text("user_agent"), userId: text("user_id") .notNull() - .references(() => users_temp.id), + .references(() => users_temp.id, { onDelete: "cascade" }), impersonatedBy: text("impersonated_by"), activeOrganizationId: text("active_organization_id"), }); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index a4b9a4f1f..a8d75637b 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -7,7 +7,7 @@ import { organization, twoFactor, } from "better-auth/plugins"; -import { desc, eq } from "drizzle-orm"; +import { and, desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; @@ -43,22 +43,25 @@ export const auth = betterAuth({ after: createAuthMiddleware(async (ctx) => { if (ctx.path.startsWith("/sign-up")) { const newSession = ctx.context.newSession; - const organization = await db - .insert(schema.organization) - .values({ - name: "My Organization", - ownerId: newSession?.user?.id || "", - createdAt: new Date(), - }) - .returning() - .then((res) => res[0]); + if (ctx.headers?.get("x-dokploy-token")) { + } else { + const organization = await db + .insert(schema.organization) + .values({ + name: "My Organization", + ownerId: newSession?.user?.id || "", + createdAt: new Date(), + }) + .returning() + .then((res) => res[0]); - await db.insert(schema.member).values({ - userId: newSession?.user?.id || "", - organizationId: organization?.id || "", - role: "owner", - createdAt: new Date(), - }); + await db.insert(schema.member).values({ + userId: newSession?.user?.id || "", + organizationId: organization?.id || "", + role: "owner", + createdAt: new Date(), + }); + } } }), }, @@ -89,11 +92,13 @@ export const auth = betterAuth({ additionalFields: { role: { type: "string", - required: true, + // required: true, + input: false, }, ownerId: { type: "string", - required: true, + // required: true, + input: false, }, }, }, @@ -133,7 +138,13 @@ export const validateRequest = async (request: IncomingMessage) => { if (session?.user) { const member = await db.query.member.findFirst({ - where: eq(schema.member.userId, session.user.id), + where: and( + eq(schema.member.userId, session.user.id), + eq( + schema.member.organizationId, + session.session.activeOrganizationId || "", + ), + ), with: { organization: true, }, diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index eee6bb37c..07c537dea 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -3,6 +3,7 @@ import { db } from "@dokploy/server/db"; import { account, type apiCreateUserInvitation, + invitation, member, organization, users_temp, @@ -64,6 +65,13 @@ export const findUserById = async (userId: string) => { return user; }; +export const findOrganizationById = async (organizationId: string) => { + const organizationResult = await db.query.organization.findFirst({ + where: eq(organization.id, organizationId), + }); + return organizationResult; +}; + export const updateUser = async (userId: string, userData: Partial) => { const user = await db .update(users_temp) @@ -106,24 +114,34 @@ export const isAdminPresent = async () => { }; export const getUserByToken = async (token: string) => { - const user = await db.query.users_temp.findFirst({ - where: eq(users_temp.token, token), + const user = await db.query.invitation.findFirst({ + where: eq(invitation.id, token), columns: { id: true, email: true, - token: true, - isRegistered: true, + status: true, + expiresAt: true, + role: true, + inviterId: true, }, }); + if (!user) { throw new TRPCError({ code: "NOT_FOUND", message: "Invitation not found", }); } + + const userAlreadyExists = await db.query.users_temp.findFirst({ + where: eq(users_temp.email, user?.email || ""), + }); + + const { expiresAt, ...rest } = user; return { - ...user, - isExpired: user.isRegistered, + ...rest, + isExpired: user.expiresAt < new Date(), + userAlreadyExists: !!userAlreadyExists, }; }; diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts index 0a9f4e945..5a7484eb9 100644 --- a/packages/server/src/services/auth.ts +++ b/packages/server/src/services/auth.ts @@ -137,76 +137,76 @@ export const symmetricDecrypt = async ({ key, data }) => { const chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)); return new TextDecoder().decode(chacha.decrypt(dataAsBytes)); }; -export const migrateExistingSecret = async ( - existingBase32Secret: string, - encryptionKey: string, -) => { - try { - // 1. Primero asegurarnos que el secreto base32 tenga el padding correcto - let paddedSecret = existingBase32Secret; - while (paddedSecret.length % 8 !== 0) { - paddedSecret += "="; - } +// export const migrateExistingSecret = async ( +// existingBase32Secret: string, +// encryptionKey: string, +// ) => { +// try { +// // 1. Primero asegurarnos que el secreto base32 tenga el padding correcto +// let paddedSecret = existingBase32Secret; +// while (paddedSecret.length % 8 !== 0) { +// paddedSecret += "="; +// } - // 2. Decodificar el base32 a bytes usando hi-base32 - const bytes = encode.decode.asBytes(paddedSecret.toUpperCase()); +// // 2. Decodificar el base32 a bytes usando hi-base32 +// const bytes = encode.decode.asBytes(paddedSecret.toUpperCase()); - // 3. Convertir los bytes a hex - const hexSecret = Buffer.from(bytes).toString("hex"); +// // 3. Convertir los bytes a hex +// const hexSecret = Buffer.from(bytes).toString("hex"); - // 4. Encriptar el secreto hex usando Better Auth - const encryptedSecret = await symmetricEncrypt({ - key: encryptionKey, - data: hexSecret, - }); +// // 4. Encriptar el secreto hex usando Better Auth +// const encryptedSecret = await symmetricEncrypt({ +// key: encryptionKey, +// data: hexSecret, +// }); - // 5. Crear TOTP con el secreto original para validación - const originalTotp = new TOTP({ - issuer: "Dokploy", - label: "migration-test", - algorithm: "SHA1", - digits: 6, - secret: existingBase32Secret, - }); +// // 5. Crear TOTP con el secreto original para validación +// const originalTotp = new TOTP({ +// issuer: "Dokploy", +// label: "migration-test", +// algorithm: "SHA1", +// digits: 6, +// secret: existingBase32Secret, +// }); - // 6. Generar un código de prueba con el secreto original - const testCode = originalTotp.generate(); +// // 6. Generar un código de prueba con el secreto original +// const testCode = originalTotp.generate(); - // 7. Validar que el código funcione con el secreto original - const isValid = originalTotp.validate({ token: testCode }) !== null; +// // 7. Validar que el código funcione con el secreto original +// const isValid = originalTotp.validate({ token: testCode }) !== null; - return { - originalSecret: existingBase32Secret, - hexSecret, - encryptedSecret, // Este es el valor que debes guardar en la base de datos - isValid, - testCode, - secretLength: hexSecret.length, - }; - } catch (error: unknown) { - const errorMessage = - error instanceof Error ? error.message : "Unknown error"; - console.error("Error durante la migración:", errorMessage); - throw new Error(`Error al migrar el secreto: ${errorMessage}`); - } -}; +// return { +// originalSecret: existingBase32Secret, +// hexSecret, +// encryptedSecret, // Este es el valor que debes guardar en la base de datos +// isValid, +// testCode, +// secretLength: hexSecret.length, +// }; +// } catch (error: unknown) { +// const errorMessage = +// error instanceof Error ? error.message : "Unknown error"; +// console.error("Error durante la migración:", errorMessage); +// throw new Error(`Error al migrar el secreto: ${errorMessage}`); +// } +// }; -// // Ejemplo de uso con el secreto de prueba -// const testMigration = await migrateExistingSecret( -// "46JMUCG4NJ3CIU6LQAIVFWUW", -// process.env.BETTER_AUTH_SECRET || "your-encryption-key", -// ); -// console.log("\nPrueba de migración:"); -// console.log("Secreto original (base32):", testMigration.originalSecret); -// console.log("Secreto convertido (hex):", testMigration.hexSecret); -// console.log("Secreto encriptado:", testMigration.encryptedSecret); -// console.log("Longitud del secreto hex:", testMigration.secretLength); -// console.log("¿Conversión válida?:", testMigration.isValid); -// console.log("Código de prueba:", testMigration.testCode); -const secret = "46JMUCG4NJ3CIU6LQAIVFWUW"; -const isValid = createOTP(secret, { - digits: 6, - period: 30, -}).verify("123456"); +// // // Ejemplo de uso con el secreto de prueba +// // const testMigration = await migrateExistingSecret( +// // "46JMUCG4NJ3CIU6LQAIVFWUW", +// // process.env.BETTER_AUTH_SECRET || "your-encryption-key", +// // ); +// // console.log("\nPrueba de migración:"); +// // console.log("Secreto original (base32):", testMigration.originalSecret); +// // console.log("Secreto convertido (hex):", testMigration.hexSecret); +// // console.log("Secreto encriptado:", testMigration.encryptedSecret); +// // console.log("Longitud del secreto hex:", testMigration.secretLength); +// // console.log("¿Conversión válida?:", testMigration.isValid); +// // console.log("Código de prueba:", testMigration.testCode); +// const secret = "46JMUCG4NJ3CIU6LQAIVFWUW"; +// const isValid = createOTP(secret, { +// digits: 6, +// period: 30, +// }).verify("123456"); -console.log(isValid.then((isValid) => console.log(isValid))); +// console.log(isValid.then((isValid) => console.log(isValid))); From 609fea7daa28604fe52a1f2f794611a10aca3549 Mon Sep 17 00:00:00 2001 From: vishalkadam47 Date: Thu, 20 Feb 2025 08:35:14 +0530 Subject: [PATCH 066/126] refactor: update glance template --- apps/dokploy/templates/glance/docker-compose.yml | 7 +++++-- apps/dokploy/templates/glance/index.ts | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/templates/glance/docker-compose.yml b/apps/dokploy/templates/glance/docker-compose.yml index e931d6e40..ace8bc940 100644 --- a/apps/dokploy/templates/glance/docker-compose.yml +++ b/apps/dokploy/templates/glance/docker-compose.yml @@ -2,7 +2,10 @@ services: glance: image: glanceapp/glance volumes: - - ../files/app/glance.yml:/app/glance.yml + - ../files/app/config/:/app/config + - ../files/app/assets:/app/assets + # Optionally, also mount docker socket if you want to use the docker containers widget + # - /var/run/docker.sock:/var/run/docker.sock:ro ports: - 8080 - restart: unless-stopped + env_file: .env \ No newline at end of file diff --git a/apps/dokploy/templates/glance/index.ts b/apps/dokploy/templates/glance/index.ts index 4b2297864..a0ab1b676 100644 --- a/apps/dokploy/templates/glance/index.ts +++ b/apps/dokploy/templates/glance/index.ts @@ -17,7 +17,7 @@ export function generate(schema: Schema): Template { const mounts: Template["mounts"] = [ { - filePath: "/app/glance.yml", + filePath: "/app/config/glance.yml", content: ` branding: hide-footer: true From c8b1fd36bd47fe53d1d1e6791abe991331963e05 Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Wed, 19 Feb 2025 22:27:36 -0500 Subject: [PATCH 067/126] feat: add Mailpit template --- apps/dokploy/public/templates/mailpit.svg | 6 ++++ .../templates/mailpit/docker-compose.yml | 25 +++++++++++++++ apps/dokploy/templates/mailpit/index.ts | 31 +++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 +++++++++ 4 files changed, 77 insertions(+) create mode 100644 apps/dokploy/public/templates/mailpit.svg create mode 100644 apps/dokploy/templates/mailpit/docker-compose.yml create mode 100644 apps/dokploy/templates/mailpit/index.ts diff --git a/apps/dokploy/public/templates/mailpit.svg b/apps/dokploy/public/templates/mailpit.svg new file mode 100644 index 000000000..58675a267 --- /dev/null +++ b/apps/dokploy/public/templates/mailpit.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/mailpit/docker-compose.yml b/apps/dokploy/templates/mailpit/docker-compose.yml new file mode 100644 index 000000000..d0dbdb8ec --- /dev/null +++ b/apps/dokploy/templates/mailpit/docker-compose.yml @@ -0,0 +1,25 @@ +services: + mailpit: + image: axllent/mailpit:v1.22.3 + restart: unless-stopped + ports: + - '1025:1025' + volumes: + - 'mailpit-data:/data' + environment: + - MP_SMTP_AUTH_ALLOW_INSECURE=true + - MP_MAX_MESSAGES=5000 + - MP_DATABASE=/data/mailpit.db + - MP_UI_AUTH=${MP_UI_AUTH} + - MP_SMTP_AUTH=${MP_SMTP_AUTH} + healthcheck: + test: + - CMD + - /mailpit + - readyz + interval: 5s + timeout: 20s + retries: 10 + +volumes: + mailpit-data: \ No newline at end of file diff --git a/apps/dokploy/templates/mailpit/index.ts b/apps/dokploy/templates/mailpit/index.ts new file mode 100644 index 000000000..25f18f7e6 --- /dev/null +++ b/apps/dokploy/templates/mailpit/index.ts @@ -0,0 +1,31 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 8025, + serviceName: "mailpit", + }, + ]; + + const defaultPassword = generatePassword(); + + const envs = [ + "# Uncomment below if you want basic auth on UI and SMTP", + `#MP_UI_AUTH=mailpit:${defaultPassword}`, + `#MP_SMTP_AUTH=mailpit:${defaultPassword}`, + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 17498e035..29a9b5751 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -393,6 +393,21 @@ export const templates: TemplateData[] = [ tags: ["chat"], load: () => import("./open-webui/index").then((m) => m.generate), }, + { + id: "mailpit", + name: "Mailpit", + version: "v1.22.3", + description: + "Mailpit is a tiny, self-contained, and secure email & SMTP testing tool with API for developers.", + logo: "mailpit.svg", + links: { + github: "https://github.com/axllent/mailpit", + website: "https://mailpit.axllent.org/", + docs: "https://mailpit.axllent.org/docs/", + }, + tags: ["email", "smtp"], + load: () => import("./mailpit/index").then((m) => m.generate), + }, { id: "listmonk", name: "Listmonk", From a9e12c2b18d6335ad9130812c8a5aa08e7219e96 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 20 Feb 2025 01:42:35 -0600 Subject: [PATCH 068/126] refactor: update organization context in API routers --- .../settings/users/show-invitations.tsx | 2 +- apps/dokploy/server/api/routers/bitbucket.ts | 2 +- .../dokploy/server/api/routers/destination.ts | 7 +++++-- apps/dokploy/server/api/routers/gitlab.ts | 2 +- .../server/api/routers/notification.ts | 20 +++++++++++++++---- .../server/api/routers/organization.ts | 11 +++++----- apps/dokploy/server/api/trpc.ts | 1 - 7 files changed, 29 insertions(+), 16 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx index 3a36b780e..b2ee81684 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx @@ -14,7 +14,6 @@ import { DropdownMenuLabel, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import copy from "copy-to-clipboard"; import { Table, TableBody, @@ -26,6 +25,7 @@ import { } from "@/components/ui/table"; import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; +import copy from "copy-to-clipboard"; import { format } from "date-fns"; import { Mail, MoreHorizontal, Users } from "lucide-react"; import { Loader2 } from "lucide-react"; diff --git a/apps/dokploy/server/api/routers/bitbucket.ts b/apps/dokploy/server/api/routers/bitbucket.ts index 7a8462641..fa02be8d6 100644 --- a/apps/dokploy/server/api/routers/bitbucket.ts +++ b/apps/dokploy/server/api/routers/bitbucket.ts @@ -22,7 +22,7 @@ export const bitbucketRouter = createTRPCRouter({ .input(apiCreateBitbucket) .mutation(async ({ input, ctx }) => { try { - return await createBitbucket(input, ctx.user.ownerId); + return await createBitbucket(input, ctx.session.activeOrganizationId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", diff --git a/apps/dokploy/server/api/routers/destination.ts b/apps/dokploy/server/api/routers/destination.ts index 770cb699f..f1d582c50 100644 --- a/apps/dokploy/server/api/routers/destination.ts +++ b/apps/dokploy/server/api/routers/destination.ts @@ -28,7 +28,10 @@ export const destinationRouter = createTRPCRouter({ .input(apiCreateDestination) .mutation(async ({ input, ctx }) => { try { - return await createDestintation(input, ctx.user.ownerId); + return await createDestintation( + input, + ctx.session.activeOrganizationId, + ); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -111,7 +114,7 @@ export const destinationRouter = createTRPCRouter({ } return await removeDestinationById( input.destinationId, - ctx.user.ownerId, + ctx.session.activeOrganizationId, ); } catch (error) { throw error; diff --git a/apps/dokploy/server/api/routers/gitlab.ts b/apps/dokploy/server/api/routers/gitlab.ts index b702bc2a4..daae68a5a 100644 --- a/apps/dokploy/server/api/routers/gitlab.ts +++ b/apps/dokploy/server/api/routers/gitlab.ts @@ -25,7 +25,7 @@ export const gitlabRouter = createTRPCRouter({ .input(apiCreateGitlab) .mutation(async ({ input, ctx }) => { try { - return await createGitlab(input, ctx.user.ownerId); + return await createGitlab(input, ctx.session.activeOrganizationId); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index bba6c7db9..6a893d363 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -110,7 +110,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateTelegram) .mutation(async ({ input, ctx }) => { try { - return await createTelegramNotification(input, ctx.user.ownerId); + return await createTelegramNotification( + input, + ctx.session.activeOrganizationId, + ); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -162,7 +165,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateDiscord) .mutation(async ({ input, ctx }) => { try { - return await createDiscordNotification(input, ctx.user.ownerId); + return await createDiscordNotification( + input, + ctx.session.activeOrganizationId, + ); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -223,7 +229,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateEmail) .mutation(async ({ input, ctx }) => { try { - return await createEmailNotification(input, ctx.user.ownerId); + return await createEmailNotification( + input, + ctx.session.activeOrganizationId, + ); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", @@ -388,7 +397,10 @@ export const notificationRouter = createTRPCRouter({ .input(apiCreateGotify) .mutation(async ({ input, ctx }) => { try { - return await createGotifyNotification(input, ctx.user.ownerId); + return await createGotifyNotification( + input, + ctx.session.activeOrganizationId, + ); } catch (error) { throw new TRPCError({ code: "BAD_REQUEST", diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index 7158a01ae..30c2630d6 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -5,12 +5,12 @@ import { organization, users_temp, } from "@/server/db/schema"; +import { IS_CLOUD, auth } from "@dokploy/server/index"; import { TRPCError } from "@trpc/server"; import { and, desc, eq, exists } from "drizzle-orm"; import { nanoid } from "nanoid"; import { z } from "zod"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; -import { auth, IS_CLOUD } from "@dokploy/server/index"; export const organizationRouter = createTRPCRouter({ create: protectedProcedure .input( @@ -148,10 +148,9 @@ export const organizationRouter = createTRPCRouter({ acceptInvitation: adminProcedure .input(z.object({ invitationId: z.string() })) .mutation(async ({ ctx, input }) => { - const result = await auth.api.acceptInvitation({ - invitationId: input.invitationId, - }); - - return result; + // const result = await auth.api.acceptInvitation({ + // invitationId: input.invitationId, + // }); + // return result; }), }); diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index c63839c5e..8e8206427 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -9,7 +9,6 @@ // import { getServerAuthSession } from "@/server/auth"; import { db } from "@/server/db"; -import { validateBearerToken } from "@dokploy/server"; import { validateRequest } from "@dokploy/server/lib/auth"; import type { OpenApiMeta } from "@dokploy/trpc-openapi"; import { TRPCError, initTRPC } from "@trpc/server"; From 5a1145996daf3abd9f1db0bbce5ad7a0879b59c9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 20 Feb 2025 01:50:01 -0600 Subject: [PATCH 069/126] feat: add backup code authentication for 2FA login --- .../settings/profile/disable-2fa.tsx | 4 +- apps/dokploy/pages/index.tsx | 212 +++++++++++++----- 2 files changed, 156 insertions(+), 60 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx index 79306bf18..f07aaed6a 100644 --- a/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx @@ -35,6 +35,7 @@ type PasswordForm = z.infer; export const Disable2FA = () => { const utils = api.useUtils(); + const [isOpen, setIsOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const form = useForm({ @@ -72,7 +73,7 @@ export const Disable2FA = () => { }; return ( - + @@ -116,6 +117,7 @@ export const Disable2FA = () => { variant="outline" onClick={() => { form.reset(); + setIsOpen(false); }} > Cancel diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 8013c6319..cb3684a5a 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -20,16 +20,23 @@ import { InputOTPSlot, } from "@/components/ui/input-otp"; import { Label } from "@/components/ui/label"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; import { authClient } from "@/lib/auth-client"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { IS_CLOUD, auth, isAdminPresent } from "@dokploy/server"; import { validateRequest } from "@dokploy/server/lib/auth"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Session, getSessionCookie } from "better-auth"; -import { betterFetch } from "better-auth/react"; import base32 from "hi-base32"; import { REGEXP_ONLY_DIGITS } from "input-otp"; +import { AlertTriangle } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; @@ -48,8 +55,14 @@ const TwoFactorSchema = z.object({ code: z.string().min(6), }); +const BackupCodeSchema = z.object({ + code: z.string().min(8, { + message: "Backup code must be at least 8 characters", + }), +}); + type LoginForm = z.infer; -type TwoFactorForm = z.infer; +type BackupCodeForm = z.infer; interface Props { IS_CLOUD: boolean; @@ -58,9 +71,12 @@ export default function Home({ IS_CLOUD }: Props) { const router = useRouter(); const [isLoginLoading, setIsLoginLoading] = useState(false); const [isTwoFactorLoading, setIsTwoFactorLoading] = useState(false); + const [isBackupCodeLoading, setIsBackupCodeLoading] = useState(false); const [isTwoFactor, setIsTwoFactor] = useState(false); const [error, setError] = useState(null); const [twoFactorCode, setTwoFactorCode] = useState(""); + const [isBackupCodeModalOpen, setIsBackupCodeModalOpen] = useState(false); + const [backupCode, setBackupCode] = useState(""); const loginForm = useForm({ resolver: zodResolver(LoginSchema), @@ -128,15 +144,33 @@ export default function Home({ IS_CLOUD }: Props) { } }; - const convertBase32ToHex = (base32Secret: string) => { + const onBackupCodeSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (backupCode.length < 8) { + toast.error("Please enter a valid backup code"); + return; + } + + setIsBackupCodeLoading(true); try { - // Usar asBytes() para obtener los bytes directamente - const bytes = base32.decode.asBytes(base32Secret.toUpperCase()); - // Convertir bytes a hex - return Buffer.from(bytes).toString("hex"); + const { data, error } = await authClient.twoFactor.verifyBackupCode({ + code: backupCode.trim(), + }); + + if (error) { + toast.error(error.message); + setError( + error.message || "An error occurred while verifying backup code", + ); + return; + } + + toast.success("Logged in successfully"); + router.push("/dashboard/projects"); } catch (error) { - console.error("Error converting base32 to hex:", error); - return base32Secret; // Fallback al valor original si hay error + toast.error("An error occurred while verifying backup code"); + } finally { + setIsBackupCodeLoading(false); } }; @@ -206,56 +240,116 @@ export default function Home({ IS_CLOUD }: Props) { ) : ( -
-
- - - - - - - - - - - - - Enter the 6-digit code from your authenticator app - -
+ <> + +
+ + + + + + + + + + + + + Enter the 6-digit code from your authenticator app + + +
-
- - -
-
+
+ + +
+ + + + + + Enter Backup Code + + Enter one of your backup codes to access your account + + + +
+
+ + setBackupCode(e.target.value)} + placeholder="Enter your backup code" + className="font-mono" + /> + + Enter one of the backup codes you received when setting up + 2FA + +
+ +
+ + +
+
+
+
+ )}
From 790894ab93589fbe63676b236fe3e39c516df9d7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Thu, 20 Feb 2025 23:02:02 -0600 Subject: [PATCH 070/126] refactor: migrate admin API calls to user router --- .../show-paid-container-monitoring.tsx | 2 +- .../paid/servers/show-paid-monitoring.tsx | 2 +- .../components/dashboard/projects/show.tsx | 2 +- .../settings/billing/show-billing.tsx | 12 +-- .../settings/billing/show-welcome-dokploy.tsx | 2 +- .../git/bitbucket/add-bitbucket-provider.tsx | 2 +- .../git/github/add-github-provider.tsx | 2 +- .../git/gitlab/add-gitlab-provider.tsx | 2 +- .../settings/profile/disable-2fa.tsx | 1 + .../settings/profile/generate-token.tsx | 2 +- .../settings/profile/profile-form.tsx | 2 +- .../settings/profile/remove-self-account.tsx | 2 +- .../servers/actions/toggle-docker-cleanup.tsx | 4 +- .../settings/servers/setup-monitoring.tsx | 2 +- .../settings/users/add-permissions.tsx | 2 +- .../settings/users/show-invitations.tsx | 97 +++++++++++-------- .../dashboard/settings/users/show-users.tsx | 26 +---- .../dashboard/settings/web-domain.tsx | 2 +- .../dashboard/settings/web-server.tsx | 4 +- .../settings/web-server/update-server-ip.tsx | 4 +- apps/dokploy/components/layouts/side.tsx | 4 +- apps/dokploy/components/layouts/user-nav.tsx | 2 +- apps/dokploy/pages/dashboard/monitoring.tsx | 2 +- .../pages/dashboard/project/[projectId].tsx | 2 +- .../services/application/[applicationId].tsx | 4 +- .../services/compose/[composeId].tsx | 4 +- .../services/mariadb/[mariadbId].tsx | 4 +- .../[projectId]/services/mongo/[mongoId].tsx | 4 +- .../[projectId]/services/mysql/[mysqlId].tsx | 4 +- .../services/postgres/[postgresId].tsx | 4 +- .../[projectId]/services/redis/[redisId].tsx | 4 +- .../pages/dashboard/settings/index.tsx | 10 +- .../pages/dashboard/settings/profile.tsx | 2 +- .../pages/dashboard/settings/server.tsx | 14 --- apps/dokploy/pages/index.tsx | 16 +-- apps/dokploy/pages/invitation.tsx | 2 +- apps/dokploy/server/api/routers/admin.ts | 5 +- apps/dokploy/server/api/routers/compose.ts | 1 - .../server/api/routers/organization.ts | 13 +-- apps/dokploy/server/api/routers/user.ts | 69 ++++++++++++- 40 files changed, 185 insertions(+), 159 deletions(-) diff --git a/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-container-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-container-monitoring.tsx index 3636a391a..3b189c2ac 100644 --- a/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-container-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/paid/container/show-paid-container-monitoring.tsx @@ -79,7 +79,7 @@ export const ContainerPaidMonitoring = ({ appName, baseUrl, token }: Props) => { data, isLoading, error: queryError, - } = api.admin.getContainerMetrics.useQuery( + } = api.user.getContainerMetrics.useQuery( { url: baseUrl, token, diff --git a/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx index 043b5c625..87c030570 100644 --- a/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx @@ -73,7 +73,7 @@ export const ShowPaidMonitoring = ({ data, isLoading, error: queryError, - } = api.admin.getServerMetrics.useQuery( + } = api.user.getServerMetrics.useQuery( { url: BASE_URL, token, diff --git a/apps/dokploy/components/dashboard/projects/show.tsx b/apps/dokploy/components/dashboard/projects/show.tsx index b9c96d314..a8c3ed5c1 100644 --- a/apps/dokploy/components/dashboard/projects/show.tsx +++ b/apps/dokploy/components/dashboard/projects/show.tsx @@ -51,7 +51,7 @@ import { ProjectEnvironment } from "./project-environment"; export const ShowProjects = () => { const utils = api.useUtils(); const { data, isLoading } = api.project.all.useQuery(); - const { data: auth } = api.auth.get.useQuery(); + const { data: auth } = api.user.get.useQuery(); const { mutateAsync } = api.project.remove.useMutation(); const [searchQuery, setSearchQuery] = useState(""); diff --git a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx index 9f3430de0..c76ec33e8 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx @@ -39,7 +39,7 @@ export const calculatePrice = (count: number, isAnnual = false) => { }; export const ShowBilling = () => { const { data: servers } = api.server.all.useQuery(undefined); - const { data: admin } = api.admin.one.useQuery(); + const { data: admin } = api.user.get.useQuery(); const { data, isLoading } = api.stripe.getProducts.useQuery(); const { mutateAsync: createCheckoutSession } = api.stripe.createCheckoutSession.useMutation(); @@ -70,7 +70,7 @@ export const ShowBilling = () => { return isAnnual ? interval === "year" : interval === "month"; }); - const maxServers = admin?.serversQuantity ?? 1; + const maxServers = admin?.user.serversQuantity ?? 1; const percentage = ((servers?.length ?? 0) / maxServers) * 100; const safePercentage = Math.min(percentage, 100); @@ -98,17 +98,17 @@ export const ShowBilling = () => { Annual - {admin?.stripeSubscriptionId && ( + {admin?.user.stripeSubscriptionId && (

Servers Plan

You have {servers?.length} server on your plan of{" "} - {admin?.serversQuantity} servers + {admin?.user.serversQuantity} servers

- {admin && admin.serversQuantity! <= servers?.length! && ( + {admin && admin.user.serversQuantity! <= servers?.length! && (
@@ -279,7 +279,7 @@ export const ShowBilling = () => { "flex flex-row items-center gap-2 mt-4", )} > - {admin?.stripeCustomerId && ( + {admin?.user.stripeCustomerId && ( diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index b78e750c3..7eebf7083 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -178,8 +178,7 @@ const Service = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index cceda8858..46c9864b4 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -173,8 +173,7 @@ const Service = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 788846e1d..6aa7677a3 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -147,8 +147,7 @@ const Mariadb = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index f03c4dfc4..2e3aae31f 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -148,8 +148,7 @@ const Mongo = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index 52e2cd072..3e75603dd 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -148,8 +148,7 @@ const MySql = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index 8ff2044b5..dd0c312d0 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -147,8 +147,7 @@ const Postgresql = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 9ad8d53c2..c7e5643a6 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -147,8 +147,7 @@ const Redis = (
- {(auth?.role === "owner" || - auth?.user?.canDeleteServices) && ( + {(auth?.role === "owner" || auth?.canDeleteServices) && ( )}
diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index cfded9915..7bacde246 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -54,7 +54,7 @@ export async function getServerSideProps( userId: user.id, }); - if (!userR.canAccessToGitProviders) { + if (!userR?.canAccessToGitProviders) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index da0dec728..79a3366d4 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -20,9 +20,7 @@ const Page = () => {
- {(data?.user?.canAccessToAPI || data?.role === "owner") && ( - - )} + {(data?.canAccessToAPI || data?.role === "owner") && } {isCloud && }
diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index c97df7ba1..8c5082e39 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -55,7 +55,7 @@ export async function getServerSideProps( userId: user.id, }); - if (!userR.canAccessToSSHKeys) { + if (!userR?.canAccessToSSHKeys) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index 3b59c47b0..c693fd8cf 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -58,7 +58,7 @@ export async function getServerSideProps( userId: user.id, }); - if (!userR.canAccessToDocker) { + if (!userR?.canAccessToDocker) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 8dcd3f084..3153e80d3 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -58,7 +58,7 @@ export async function getServerSideProps( userId: user.id, }); - if (!userR.canAccessToTraefikFiles) { + if (!userR?.canAccessToTraefikFiles) { return { redirect: { permanent: true, diff --git a/apps/dokploy/pages/swagger.tsx b/apps/dokploy/pages/swagger.tsx index e4a6fac8d..3d8cc01d9 100644 --- a/apps/dokploy/pages/swagger.tsx +++ b/apps/dokploy/pages/swagger.tsx @@ -63,7 +63,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { userId: user.id, }); - if (!userR.canAccessToAPI) { + if (!userR?.canAccessToAPI) { return { redirect: { permanent: true, diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 5fc79f437..e3c24e538 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -8,6 +8,7 @@ import { applications, compose, mariadb, + member, mongo, mysql, postgres, @@ -29,8 +30,8 @@ import { findUserByAuthId, findUserById, updateProjectById, + findMemberById, } from "@dokploy/server"; - export const projectRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateProject) @@ -71,7 +72,10 @@ export const projectRouter = createTRPCRouter({ .input(apiFindOneProject) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - const { accessedServices } = await findUserById(ctx.user.id); + const { accessedServices } = await findMemberById( + ctx.user.id, + ctx.session.activeOrganizationId, + ); await checkProjectAccess(ctx.user.id, "access", input.projectId); @@ -129,8 +133,9 @@ export const projectRouter = createTRPCRouter({ all: protectedProcedure.query(async ({ ctx }) => { // console.log(ctx.user); if (ctx.user.rol === "member") { - const { accessedProjects, accessedServices } = await findUserById( + const { accessedProjects, accessedServices } = await findMemberById( ctx.user.id, + ctx.session.activeOrganizationId, ); if (accessedProjects.length === 0) { diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 170af9084..9351a0031 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,7 +1,7 @@ import { db } from "@dokploy/server/db"; -import type { users_temp } from "@dokploy/server/db/schema"; +import { type users_temp, member } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; -import { eq } from "drizzle-orm"; +import { and, eq } from "drizzle-orm"; import { findUserById } from "./admin"; export type User = typeof users_temp.$inferSelect; @@ -191,3 +191,26 @@ export const checkProjectAccess = async ( }); } }; + +export const findMemberById = async ( + userId: string, + organizationId: string, +) => { + const result = await db.query.member.findFirst({ + where: and( + eq(member.userId, userId), + eq(member.organizationId, organizationId), + ), + with: { + user: true, + }, + }); + + if (!result) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "Permission denied", + }); + } + return result; +}; From 5ae103e779f03a0900d0e3e9b61cb0c159f1a461 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Fri, 21 Feb 2025 00:48:04 -0600 Subject: [PATCH 075/126] refactor: update permission checks to use organization context --- apps/dokploy/components/layouts/side.tsx | 21 ++-- apps/dokploy/server/api/routers/project.ts | 25 ++++- packages/server/src/services/user.ts | 111 +++++++++++++++------ 3 files changed, 107 insertions(+), 50 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 22f86fb08..f1296ce33 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -155,7 +155,7 @@ const MENU: Menu = { // Only enabled for admins and users with access to Traefik files in non-cloud environments isEnabled: ({ auth, isCloud }) => !!( - (auth?.role === "owner" || auth?.user?.canAccessToTraefikFiles) && + (auth?.role === "owner" || auth?.canAccessToTraefikFiles) && !isCloud ), }, @@ -166,10 +166,7 @@ const MENU: Menu = { icon: BlocksIcon, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, isCloud }) => - !!( - (auth?.role === "owner" || auth?.user?.canAccessToDocker) && - !isCloud - ), + !!((auth?.role === "owner" || auth?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -178,10 +175,7 @@ const MENU: Menu = { icon: PieChart, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, isCloud }) => - !!( - (auth?.role === "owner" || auth?.user?.canAccessToDocker) && - !isCloud - ), + !!((auth?.role === "owner" || auth?.canAccessToDocker) && !isCloud), }, { isSingle: true, @@ -190,10 +184,7 @@ const MENU: Menu = { icon: Forward, // Only enabled for admins and users with access to Docker in non-cloud environments isEnabled: ({ auth, isCloud }) => - !!( - (auth?.role === "owner" || auth?.user?.canAccessToDocker) && - !isCloud - ), + !!((auth?.role === "owner" || auth?.canAccessToDocker) && !isCloud), }, // Legacy unused menu, adjusted to the new structure @@ -291,7 +282,7 @@ const MENU: Menu = { url: "/dashboard/settings/ssh-keys", // Only enabled for admins and users with access to SSH keys isEnabled: ({ auth }) => - !!(auth?.role === "owner" || auth?.user?.canAccessToSSHKeys), + !!(auth?.role === "owner" || auth?.canAccessToSSHKeys), }, { isSingle: true, @@ -300,7 +291,7 @@ const MENU: Menu = { icon: GitBranch, // Only enabled for admins and users with access to Git providers isEnabled: ({ auth }) => - !!(auth?.role === "owner" || auth?.user?.canAccessToGitProviders), + !!(auth?.role === "owner" || auth?.canAccessToGitProviders), }, { isSingle: true, diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index e3c24e538..68b068bc3 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -38,7 +38,11 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { try { if (ctx.user.rol === "member") { - await checkProjectAccess(ctx.user.id, "create"); + await checkProjectAccess( + ctx.user.id, + "create", + ctx.session.activeOrganizationId, + ); } const admin = await findUserById(ctx.user.ownerId); @@ -55,7 +59,11 @@ export const projectRouter = createTRPCRouter({ ctx.session.activeOrganizationId, ); if (ctx.user.rol === "member") { - await addNewProject(ctx.user.id, project.projectId); + await addNewProject( + ctx.user.id, + project.projectId, + ctx.session.activeOrganizationId, + ); } return project; @@ -77,7 +85,12 @@ export const projectRouter = createTRPCRouter({ ctx.session.activeOrganizationId, ); - await checkProjectAccess(ctx.user.id, "access", input.projectId); + await checkProjectAccess( + ctx.user.id, + "access", + ctx.session.activeOrganizationId, + input.projectId, + ); const project = await db.query.projects.findFirst({ where: and( @@ -212,7 +225,11 @@ export const projectRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkProjectAccess(ctx.user.id, "delete"); + await checkProjectAccess( + ctx.user.id, + "delete", + ctx.session.activeOrganizationId, + ); } const currentProject = await findProjectById(input.projectId); if ( diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 9351a0031..9e924e9f1 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -33,32 +33,48 @@ export const findUserByAuthId = async (authId: string) => { // return userR; }; -export const addNewProject = async (userId: string, projectId: string) => { - const userR = await findUserById(userId); +export const addNewProject = async ( + userId: string, + projectId: string, + organizationId: string, +) => { + const userR = await findMemberById(userId, organizationId); - // await db - // .update(user) - // .set({ - // accessedProjects: [...userR.accessedProjects, projectId], - // }) - // .where(eq(user.authId, authId)); + await db + .update(member) + .set({ + accessedProjects: [...userR.accessedProjects, projectId], + }) + .where( + and(eq(member.id, userR.id), eq(member.organizationId, organizationId)), + ); }; -export const addNewService = async (userId: string, serviceId: string) => { - const userR = await findUserById(userId); - // await db - // .update(user) - // .set({ - // accessedServices: [...userR.accessedServices, serviceId], - // }) - // .where(eq(user.userId, userId)); +export const addNewService = async ( + userId: string, + serviceId: string, + organizationId: string, +) => { + const userR = await findMemberById(userId, organizationId); + await db + .update(member) + .set({ + accessedServices: [...userR.accessedServices, serviceId], + }) + .where( + and(eq(member.id, userR.id), eq(member.organizationId, organizationId)), + ); }; export const canPerformCreationService = async ( userId: string, projectId: string, + organizationId: string, ) => { - const { accessedProjects, canCreateServices } = await findUserById(userId); + const { accessedProjects, canCreateServices } = await findMemberById( + userId, + organizationId, + ); const haveAccessToProject = accessedProjects.includes(projectId); if (canCreateServices && haveAccessToProject) { @@ -71,8 +87,9 @@ export const canPerformCreationService = async ( export const canPerformAccessService = async ( userId: string, serviceId: string, + organizationId: string, ) => { - const { accessedServices } = await findUserById(userId); + const { accessedServices } = await findMemberById(userId, organizationId); const haveAccessToService = accessedServices.includes(serviceId); if (haveAccessToService) { @@ -85,8 +102,12 @@ export const canPerformAccessService = async ( export const canPeformDeleteService = async ( userId: string, serviceId: string, + organizationId: string, ) => { - const { accessedServices, canDeleteServices } = await findUserById(userId); + const { accessedServices, canDeleteServices } = await findMemberById( + userId, + organizationId, + ); const haveAccessToService = accessedServices.includes(serviceId); if (canDeleteServices && haveAccessToService) { @@ -96,8 +117,11 @@ export const canPeformDeleteService = async ( return false; }; -export const canPerformCreationProject = async (userId: string) => { - const { canCreateProjects } = await findUserById(userId); +export const canPerformCreationProject = async ( + userId: string, + organizationId: string, +) => { + const { canCreateProjects } = await findMemberById(userId, organizationId); if (canCreateProjects) { return true; @@ -106,8 +130,11 @@ export const canPerformCreationProject = async (userId: string) => { return false; }; -export const canPerformDeleteProject = async (userId: string) => { - const { canDeleteProjects } = await findUserById(userId); +export const canPerformDeleteProject = async ( + userId: string, + organizationId: string, +) => { + const { canDeleteProjects } = await findMemberById(userId, organizationId); if (canDeleteProjects) { return true; @@ -119,8 +146,9 @@ export const canPerformDeleteProject = async (userId: string) => { export const canPerformAccessProject = async ( userId: string, projectId: string, + organizationId: string, ) => { - const { accessedProjects } = await findUserById(userId); + const { accessedProjects } = await findMemberById(userId, organizationId); const haveAccessToProject = accessedProjects.includes(projectId); @@ -130,26 +158,45 @@ export const canPerformAccessProject = async ( return false; }; -export const canAccessToTraefikFiles = async (userId: string) => { - const { canAccessToTraefikFiles } = await findUserById(userId); +export const canAccessToTraefikFiles = async ( + userId: string, + organizationId: string, +) => { + const { canAccessToTraefikFiles } = await findMemberById( + userId, + organizationId, + ); return canAccessToTraefikFiles; }; export const checkServiceAccess = async ( userId: string, serviceId: string, + organizationId: string, action = "access" as "access" | "create" | "delete", ) => { let hasPermission = false; switch (action) { case "create": - hasPermission = await canPerformCreationService(userId, serviceId); + hasPermission = await canPerformCreationService( + userId, + serviceId, + organizationId, + ); break; case "access": - hasPermission = await canPerformAccessService(userId, serviceId); + hasPermission = await canPerformAccessService( + userId, + serviceId, + organizationId, + ); break; case "delete": - hasPermission = await canPeformDeleteService(userId, serviceId); + hasPermission = await canPeformDeleteService( + userId, + serviceId, + organizationId, + ); break; default: hasPermission = false; @@ -165,6 +212,7 @@ export const checkServiceAccess = async ( export const checkProjectAccess = async ( authId: string, action: "create" | "delete" | "access", + organizationId: string, projectId?: string, ) => { let hasPermission = false; @@ -173,13 +221,14 @@ export const checkProjectAccess = async ( hasPermission = await canPerformAccessProject( authId, projectId as string, + organizationId, ); break; case "create": - hasPermission = await canPerformCreationProject(authId); + hasPermission = await canPerformCreationProject(authId, organizationId); break; case "delete": - hasPermission = await canPerformDeleteProject(authId); + hasPermission = await canPerformDeleteProject(authId, organizationId); break; default: hasPermission = false; From b02195db17ac05369cfad80311f44c98a35be7f2 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 02:31:04 -0600 Subject: [PATCH 076/126] feat: add organization invitation system and update user profile management --- .../settings/profile/profile-form.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 190 ++++++++++-------- .../dokploy/server/api/routers/application.ts | 6 +- apps/dokploy/server/api/routers/compose.ts | 12 +- apps/dokploy/server/api/routers/mariadb.ts | 6 +- apps/dokploy/server/api/routers/mongo.ts | 6 +- apps/dokploy/server/api/routers/mysql.ts | 6 +- apps/dokploy/server/api/routers/postgres.ts | 6 +- apps/dokploy/server/api/routers/redis.ts | 6 +- apps/dokploy/server/api/routers/registry.ts | 2 +- apps/dokploy/server/api/routers/user.ts | 27 ++- 11 files changed, 169 insertions(+), 100 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index ba2009bd2..ca1bf3c21 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -65,7 +65,7 @@ export const ProfileForm = () => { isLoading: isUpdating, isError, error, - } = api.auth.update.useMutation(); + } = api.user.update.useMutation(); const { t } = useTranslation("settings"); const [gravatarHash, setGravatarHash] = useState(null); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index f1296ce33..cba10ca0f 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -27,6 +27,8 @@ import { Trash2, User, Users, + ChevronsUpDown, + Plus, } from "lucide-react"; import { usePathname } from "next/navigation"; import type * as React from "react"; @@ -75,6 +77,20 @@ import { useRouter } from "next/router"; import { Logo } from "../shared/logo"; import { UpdateServerButton } from "./update-server"; import { UserNav } from "./user-nav"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { authClient } from "@/lib/auth-client"; +import { toast } from "sonner"; +import { AddOrganization } from "../dashboard/organization/handle-organization"; +import { DialogAction } from "../shared/dialog-action"; +import { Button } from "../ui/button"; // The types of the queries we are going to use type AuthQueryOutput = inferRouterOutputs["auth"]["get"]; @@ -473,46 +489,6 @@ interface Props { function LogoWrapper() { return ; } -import { ChevronsUpDown, Plus } from "lucide-react"; - -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { authClient } from "@/lib/auth-client"; -import { toast } from "sonner"; -import { AddOrganization } from "../dashboard/organization/handle-organization"; -import { DialogAction } from "../shared/dialog-action"; -import { Button } from "../ui/button"; -const data = { - user: { - name: "shadcn", - email: "m@example.com", - avatar: "/avatars/shadcn.jpg", - }, - teams: [ - { - name: "Acme Inc", - logo: GalleryVerticalEnd, - plan: "Enterprise", - }, - { - name: "Acme Corp.", - logo: AudioWaveform, - plan: "Startup", - }, - { - name: "Evil Corp.", - logo: Command, - plan: "Free", - }, - ], -}; function SidebarLogo() { const { state } = useSidebar(); @@ -529,6 +505,10 @@ function SidebarLogo() { api.organization.delete.useMutation(); const { isMobile } = useSidebar(); const { data: activeOrganization } = authClient.useActiveOrganization(); + const utils = api.useUtils(); + + const { data: invitations, refetch: refetchInvitations } = + api.user.getInvitations.useQuery(); const [activeTeam, setActiveTeam] = useState< typeof activeOrganization | null @@ -549,31 +529,27 @@ function SidebarLogo() {
) : ( - + - {/*
*/} -
- -
-
- - {activeTeam?.name} - +
+
+ +
+
+

+ {activeOrganization?.name} +

+
@@ -587,14 +563,13 @@ function SidebarLogo() { Organizations - {organizations?.map((org, index) => ( + {organizations?.map((org) => (
{ await authClient.organization.setActive({ organizationId: org.id, }); - window.location.reload(); }} className="w-full gap-2 p-2" @@ -655,35 +630,76 @@ function SidebarLogo() { )} + + + + + + + Pending Invitations + {invitations && invitations.length > 0 ? ( + invitations.map((invitation) => ( +
+ e.preventDefault()} + > +
{invitation.email}
+
+ Expires:{" "} + {new Date(invitation.expiresAt).toLocaleDateString()} +
+
+ Role: {invitation.role} +
+
+ { + const { error } = + await authClient.organization.acceptInvitation({ + invitationId: invitation.id, + }); + + if (error) { + toast.error( + error.message || "Error accepting invitation", + ); + } else { + toast.success("Invitation accepted successfully"); + await refetchInvitations(); + } + }} + > + + +
+ )) + ) : ( + + No pending invitations + + )} +
+
)} - - {/* -
- -
- -
-

Dokploy

-

- {dokployVersion} -

-
- */} ); } diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 490da340a..269ac77b5 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -81,7 +81,11 @@ export const applicationRouter = createTRPCRouter({ const newApplication = await createApplication(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newApplication.applicationId); + await addNewService( + ctx.user.id, + newApplication.applicationId, + project.organizationId, + ); } return newApplication; } catch (error: unknown) { diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index d9cd46d24..258a03d4b 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -80,7 +80,11 @@ export const composeRouter = createTRPCRouter({ const newService = await createCompose(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newService.composeId); + await addNewService( + ctx.user.id, + newService.composeId, + project.organizationId, + ); } return newService; @@ -424,7 +428,11 @@ export const composeRouter = createTRPCRouter({ }); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, compose.composeId); + await addNewService( + ctx.user.id, + compose.composeId, + project.organizationId, + ); } if (mounts && mounts?.length > 0) { diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 4276560c6..5735620e7 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -57,7 +57,11 @@ export const mariadbRouter = createTRPCRouter({ } const newMariadb = await createMariadb(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newMariadb.mariadbId); + await addNewService( + ctx.user.id, + newMariadb.mariadbId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index d1d12bd0b..7f8716a59 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -56,7 +56,11 @@ export const mongoRouter = createTRPCRouter({ } const newMongo = await createMongo(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newMongo.mongoId); + await addNewService( + ctx.user.id, + newMongo.mongoId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index dc107bdba..96ea4846f 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -59,7 +59,11 @@ export const mysqlRouter = createTRPCRouter({ const newMysql = await createMysql(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newMysql.mysqlId); + await addNewService( + ctx.user.id, + newMysql.mysqlId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index b74bc0f68..aa3a0459d 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -64,7 +64,11 @@ export const postgresRouter = createTRPCRouter({ } const newPostgres = await createPostgres(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newPostgres.postgresId); + await addNewService( + ctx.user.id, + newPostgres.postgresId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index db76ee6ce..6d5a84d5e 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -56,7 +56,11 @@ export const redisRouter = createTRPCRouter({ } const newRedis = await createRedis(input); if (ctx.user.rol === "member") { - await addNewService(ctx.user.id, newRedis.redisId); + await addNewService( + ctx.user.id, + newRedis.redisId, + project.organizationId, + ); } await createMount({ diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 6ad7e2a94..62c8a9b65 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -18,7 +18,7 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; - +import { db } from "@/server/db"; export const registryRouter = createTRPCRouter({ create: adminProcedure .input(apiCreateRegistry) diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 6b4e8eded..872ee0744 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -15,10 +15,11 @@ import { apiAssignPermissions, apiFindOneToken, apiUpdateUser, + invitation, member, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; -import { and, asc, desc, eq } from "drizzle-orm"; +import { and, asc, desc, eq, gt } from "drizzle-orm"; import { z } from "zod"; import { adminProcedure, @@ -115,14 +116,34 @@ export const userRouter = createTRPCRouter({ }); } + const { id, ...rest } = input; + + console.log(rest); await db .update(member) .set({ - ...input, + ...rest, }) - .where(eq(member.userId, input.id)); + .where( + and( + eq(member.userId, input.id), + eq( + member.organizationId, + ctx.session?.activeOrganizationId || "", + ), + ), + ); } catch (error) { throw error; } }), + getInvitations: protectedProcedure.query(async ({ ctx }) => { + return await db.query.invitation.findMany({ + where: and( + eq(invitation.email, ctx.user.email), + gt(invitation.expiresAt, new Date()), + eq(invitation.status, "pending"), + ), + }); + }), }); From c52725420ef81cf066c006a4a59e6c6b5f6d0e61 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 02:35:44 -0600 Subject: [PATCH 077/126] refactor: use organization context for server creation --- apps/dokploy/server/api/routers/server.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 3662345d4..1d7fd40e8 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -50,7 +50,10 @@ export const serverRouter = createTRPCRouter({ message: "You cannot create more servers", }); } - const project = await createServer(input, ctx.user.ownerId); + const project = await createServer( + input, + ctx.session.activeOrganizationId, + ); return project; } catch (error) { throw new TRPCError({ From baf555af5249e0d5d3e193e6304dc0fe870358f0 Mon Sep 17 00:00:00 2001 From: Cohvir Date: Sat, 22 Feb 2025 14:16:14 +0100 Subject: [PATCH 078/126] feat(template): add Wiki.js --- apps/dokploy/public/templates/wikijs.svg | 119 ++++++++++++++++++ apps/dokploy/templates/templates.ts | 14 +++ .../templates/wikijs/docker-compose.yml | 38 ++++++ apps/dokploy/templates/wikijs/index.ts | 35 ++++++ 4 files changed, 206 insertions(+) create mode 100644 apps/dokploy/public/templates/wikijs.svg create mode 100644 apps/dokploy/templates/wikijs/docker-compose.yml create mode 100644 apps/dokploy/templates/wikijs/index.ts diff --git a/apps/dokploy/public/templates/wikijs.svg b/apps/dokploy/public/templates/wikijs.svg new file mode 100644 index 000000000..78073b234 --- /dev/null +++ b/apps/dokploy/public/templates/wikijs.svg @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f9..1e18f4acc 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1559,4 +1559,18 @@ export const templates: TemplateData[] = [ tags: ["backend", "database", "api"], load: () => import("./convex/index").then((m) => m.generate), }, + { + id: "wikijs", + name: "Wiki.js", + version: "2.5", + description: "The most powerful and extensible open source Wiki software.", + logo: "wikijs.svg", + links: { + github: "https://github.com/requarks/wiki", + website: "https://js.wiki/", + docs: "https://docs.requarks.io/", + }, + tags: ["knowledge-base", "self-hosted", "documentation"], + load: () => import("./wikijs/index").then((m) => m.generate), + }, ]; diff --git a/apps/dokploy/templates/wikijs/docker-compose.yml b/apps/dokploy/templates/wikijs/docker-compose.yml new file mode 100644 index 000000000..132774198 --- /dev/null +++ b/apps/dokploy/templates/wikijs/docker-compose.yml @@ -0,0 +1,38 @@ +version: '3.5' +services: + wiki: + image: ghcr.io/requarks/wiki:2.5 + restart: unless-stopped + ports: + # Change 5000 to the desired port + - "5000:3000" + environment: + - DB_TYPE + - DB_HOST + - DB_PORT + - DB_USER + - DB_PASS + - DB_NAME + depends_on: + - db + networks: + - dokploy-network + labels: + - traefik.enable=true + - traefik.constraint-label-stack=wikijs + db: + image: postgres:14 + restart: unless-stopped + environment: + - POSTGRES_USER + - POSTGRES_PASSWORD + - POSTGRES_DB + volumes: + - wiki-db-data:/var/lib/postgresql/data + networks: + - dokploy-network +networks: + dokploy-network: + external: true +volumes: + wiki-db-data: diff --git a/apps/dokploy/templates/wikijs/index.ts b/apps/dokploy/templates/wikijs/index.ts new file mode 100644 index 000000000..ff6c234de --- /dev/null +++ b/apps/dokploy/templates/wikijs/index.ts @@ -0,0 +1,35 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const domains: DomainSchema[] = [ + { + host: generateRandomDomain(schema), + port: 3000, + serviceName: "wiki", + }, + ]; + + const envs = [ + "# Database Setup", + "POSTGRES_USER=wikijs", + "POSTGRES_PASSWORD=wikijsrocks", + "POSTGRES_DB=wiki", + "# WikiJS Database Connection", + "DB_TYPE=postgres", + "DB_HOST=db", + "DB_PORT=5432", + "DB_USER=wikijs", + "DB_PASS=wikijsrocks", + "DB_NAME=wiki", + ]; + + return { + domains, + envs, + }; +} From 81a881b07e1548fd5e2062834c7e607a4d10a1cf Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 13:53:57 -0600 Subject: [PATCH 079/126] feat: enhance organization invitation UI and add organization details --- apps/dokploy/components/layouts/side.tsx | 103 ++++++++++++----------- apps/dokploy/server/api/routers/user.ts | 3 + packages/server/src/db/schema/user.ts | 7 +- 3 files changed, 63 insertions(+), 50 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index cba10ca0f..e0931b08f 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -555,7 +555,7 @@ function SidebarLogo() { - -
- )) - ) : ( - - No pending invitations - - )} + if (error) { + toast.error( + error.message || "Error accepting invitation", + ); + } else { + toast.success("Invitation accepted successfully"); + await refetchInvitations(); + await refetch(); + } + }} + > + + +
+ )) + ) : ( + + No pending invitations + + )} +
diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 872ee0744..5c4eb56d5 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -144,6 +144,9 @@ export const userRouter = createTRPCRouter({ gt(invitation.expiresAt, new Date()), eq(invitation.status, "pending"), ), + with: { + organization: true, + }, }); }), }); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 5860875f6..67a247414 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -140,7 +140,12 @@ export const apiRemoveUser = createSchema }) .required(); -export const apiFindOneToken = createSchema.pick({}).required(); +export const apiFindOneToken = createSchema + .pick({}) + .required() + .extend({ + token: z.string().min(1), + }); export const apiAssignPermissions = createSchema .pick({ From 1a415b96c90b428a903a6516de1cc94cbe04ec11 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 18:03:12 -0600 Subject: [PATCH 080/126] refactor: remove unused auth service and clean up server-side code --- apps/api/src/index.ts | 2 +- apps/api/src/utils.ts | 2 +- .../__test__/compose/config/config.test.ts | 2 +- .../compose/network/network-root.test.ts | 23 -- .../compose/secrets/secret-root.test.ts | 2 +- .../__test__/compose/volume/volume.test.ts | 6 +- .../server/update-server-config.test.ts | 2 - apps/dokploy/components/auth/login-2fa.tsx | 1 - .../cluster/show-cluster-settings.tsx | 1 - .../advanced/general/add-command.tsx | 1 - .../application/advanced/ports/show-port.tsx | 1 - .../advanced/redirects/show-redirects.tsx | 1 - .../advanced/security/show-security.tsx | 1 - .../application/advanced/show-resources.tsx | 2 +- .../advanced/traefik/show-traefik-config.tsx | 1 - .../advanced/volumes/show-volumes.tsx | 1 - .../advanced/volumes/update-volume.tsx | 2 +- .../application/deployments/refresh-token.tsx | 1 - .../environment/show-enviroment.tsx | 2 +- .../application/environment/show.tsx | 2 +- .../application/general/generic/show.tsx | 2 +- .../dashboard/application/general/show.tsx | 1 - .../show-preview-builds.tsx | 1 - .../show-preview-deployments.tsx | 1 - .../application/update-application.tsx | 2 +- .../compose/advanced/add-command.tsx | 1 - .../deployments/refresh-token-compose.tsx | 1 - .../compose/general/generic/show.tsx | 2 +- .../compose/general/randomize-compose.tsx | 2 +- .../dashboard/compose/general/show.tsx | 1 - .../dashboard/compose/logs/show.tsx | 2 +- .../database/backups/show-backups.tsx | 1 - .../database/backups/update-backup.tsx | 2 +- .../docker/logs/since-logs-filter.tsx | 1 - .../dashboard/docker/logs/terminal-line.tsx | 1 - .../dashboard/docker/show/colums.tsx | 1 - .../dashboard/docker/show/show-containers.tsx | 28 +-- .../file-system/show-traefik-system.tsx | 3 +- .../show-external-mariadb-credentials.tsx | 2 +- .../mariadb/general/show-general-mariadb.tsx | 2 +- .../show-internal-mariadb-credentials.tsx | 1 - .../dashboard/mariadb/update-mariadb.tsx | 2 +- .../show-external-mongo-credentials.tsx | 2 +- .../mongo/general/show-general-mongo.tsx | 2 +- .../show-internal-mongo-credentials.tsx | 1 - .../show-free-compose-monitoring.tsx | 1 - .../show-free-container-monitoring.tsx | 10 +- .../paid/servers/show-paid-monitoring.tsx | 1 - .../show-external-mysql-credentials.tsx | 2 +- .../mysql/general/show-general-mysql.tsx | 2 +- .../show-internal-mysql-credentials.tsx | 1 - .../dashboard/mysql/update-mysql.tsx | 2 +- .../organization/handle-organization.tsx | 2 +- .../postgres/advanced/show-custom-command.tsx | 2 +- .../show-external-postgres-credentials.tsx | 2 +- .../general/show-general-postgres.tsx | 2 +- .../show-internal-postgres-credentials.tsx | 1 - .../dashboard/postgres/update-postgres.tsx | 2 +- .../dashboard/project/add-database.tsx | 1 - .../dashboard/project/add-template.tsx | 1 - .../dashboard/projects/handle-project.tsx | 1 - .../show-external-redis-credentials.tsx | 2 +- .../redis/general/show-general-redis.tsx | 2 +- .../show-internal-redis-credentials.tsx | 1 - .../dashboard/redis/update-redis.tsx | 2 +- .../components/dashboard/requests/columns.tsx | 1 - .../dashboard/requests/show-requests.tsx | 1 - .../components/dashboard/search-command.tsx | 2 - .../settings/billing/show-billing.tsx | 2 +- .../settings/billing/show-welcome-dokploy.tsx | 1 - .../settings/cluster/nodes/show-nodes.tsx | 1 - .../git/bitbucket/add-bitbucket-provider.tsx | 6 +- .../git/bitbucket/edit-bitbucket-provider.tsx | 2 +- .../git/gitlab/edit-gitlab-provider.tsx | 2 +- .../dashboard/settings/profile/enable-2fa.tsx | 1 - .../settings/profile/profile-form.tsx | 2 - .../servers/actions/show-dokploy-actions.tsx | 1 - .../servers/actions/show-storage-actions.tsx | 1 - .../servers/actions/show-traefik-actions.tsx | 14 +- .../settings/servers/gpu-support.tsx | 1 - .../settings/servers/setup-monitoring.tsx | 2 - .../servers/show-docker-containers-modal.tsx | 8 +- .../servers/show-swarm-overview-modal.tsx | 9 +- .../dashboard/settings/users/show-users.tsx | 83 ++++--- .../dashboard/settings/web-domain.tsx | 2 +- .../dashboard/settings/web-server.tsx | 2 - .../web-server/local-server-config.tsx | 1 - .../settings/web-server/show-modal-logs.tsx | 1 - .../settings/web-server/update-server.tsx | 2 - .../dashboard/swarm/applications/columns.tsx | 1 - .../swarm/applications/show-applications.tsx | 1 - .../components/icons/data-tools-icons.tsx | 1 - apps/dokploy/components/layouts/side.tsx | 30 +-- apps/dokploy/components/layouts/user-nav.tsx | 2 - .../components/shared/dialog-action.tsx | 1 - .../dokploy/components/shared/drawer-logs.tsx | 9 +- apps/dokploy/components/shared/logo.tsx | 2 - apps/dokploy/components/ui/modeToggle.tsx | 1 - apps/dokploy/migrate.ts | 1 - apps/dokploy/pages/api/deploy/github.ts | 2 - .../pages/api/providers/github/setup.ts | 9 +- apps/dokploy/pages/api/stripe/webhook.ts | 2 +- apps/dokploy/pages/dashboard/docker.tsx | 2 +- apps/dokploy/pages/dashboard/monitoring.tsx | 4 - .../services/application/[applicationId].tsx | 5 +- .../services/compose/[composeId].tsx | 3 +- .../services/mariadb/[mariadbId].tsx | 6 +- .../[projectId]/services/mongo/[mongoId].tsx | 6 +- .../[projectId]/services/mysql/[mysqlId].tsx | 6 +- .../services/postgres/[postgresId].tsx | 6 +- .../[projectId]/services/redis/[redisId].tsx | 4 +- apps/dokploy/pages/dashboard/projects.tsx | 1 - apps/dokploy/pages/dashboard/requests.tsx | 1 - .../pages/dashboard/settings/billing.tsx | 2 +- .../pages/dashboard/settings/certificates.tsx | 2 +- .../pages/dashboard/settings/cluster.tsx | 2 +- .../pages/dashboard/settings/destinations.tsx | 2 +- .../dashboard/settings/git-providers.tsx | 2 +- .../pages/dashboard/settings/index.tsx | 3 +- .../dashboard/settings/notifications.tsx | 2 +- .../pages/dashboard/settings/profile.tsx | 2 +- .../pages/dashboard/settings/registry.tsx | 2 +- .../pages/dashboard/settings/server.tsx | 5 +- .../pages/dashboard/settings/servers.tsx | 2 +- .../pages/dashboard/settings/ssh-keys.tsx | 2 +- .../pages/dashboard/settings/users.tsx | 2 +- apps/dokploy/pages/dashboard/traefik.tsx | 2 +- apps/dokploy/pages/index.tsx | 14 +- apps/dokploy/pages/invitation.tsx | 9 +- apps/dokploy/pages/register.tsx | 8 +- apps/dokploy/pages/send-reset-password.tsx | 7 +- apps/dokploy/server/api/routers/admin.ts | 234 +----------------- .../dokploy/server/api/routers/application.ts | 25 +- apps/dokploy/server/api/routers/auth.ts | 39 --- apps/dokploy/server/api/routers/backup.ts | 33 +-- apps/dokploy/server/api/routers/cluster.ts | 4 +- apps/dokploy/server/api/routers/compose.ts | 32 ++- .../server/api/routers/git-provider.ts | 6 +- apps/dokploy/server/api/routers/mariadb.ts | 24 +- apps/dokploy/server/api/routers/mongo.ts | 23 +- apps/dokploy/server/api/routers/mysql.ts | 23 +- .../server/api/routers/notification.ts | 6 +- .../server/api/routers/organization.ts | 10 +- apps/dokploy/server/api/routers/port.ts | 8 +- apps/dokploy/server/api/routers/postgres.ts | 31 ++- apps/dokploy/server/api/routers/project.ts | 11 +- apps/dokploy/server/api/routers/redis.ts | 23 +- apps/dokploy/server/api/routers/registry.ts | 2 +- apps/dokploy/server/api/routers/server.ts | 4 +- apps/dokploy/server/api/routers/settings.ts | 22 +- apps/dokploy/server/api/routers/ssh-key.ts | 1 - apps/dokploy/server/api/routers/stripe.ts | 50 ++-- apps/dokploy/server/api/routers/user.ts | 10 +- apps/dokploy/server/db/seed.ts | 1 - apps/dokploy/server/utils/backup.ts | 1 - apps/dokploy/templates/excalidraw/index.ts | 1 - apps/dokploy/templates/ghost/index.ts | 1 - apps/dokploy/templates/penpot/index.ts | 2 - apps/dokploy/templates/photoprism/index.ts | 1 - apps/dokploy/templates/triggerdotdev/index.ts | 1 - apps/dokploy/templates/unsend/index.ts | 1 - biome.json | 5 +- packages/server/auth-schema.ts | 8 +- packages/server/src/db/schema/user.ts | 2 +- .../server/src/emails/emails/build-failed.tsx | 1 - .../src/emails/emails/build-success.tsx | 1 - .../src/emails/emails/database-backup.tsx | 1 - .../src/emails/emails/docker-cleanup.tsx | 2 - .../src/emails/emails/dokploy-restart.tsx | 1 - .../src/emails/emails/notion-magic-link.tsx | 1 - .../emails/emails/plaid-verify-identity.tsx | 1 - .../src/emails/emails/stripe-welcome.tsx | 1 - .../src/emails/emails/vercel-invite-user.tsx | 1 - packages/server/src/index.ts | 2 - packages/server/src/lib/auth.ts | 1 - packages/server/src/services/admin.ts | 3 - packages/server/src/services/application.ts | 5 +- packages/server/src/services/auth.ts | 212 ---------------- packages/server/src/services/backup.ts | 2 - packages/server/src/services/compose.ts | 3 +- packages/server/src/services/deployment.ts | 10 +- packages/server/src/services/domain.ts | 2 +- packages/server/src/services/gitlab.ts | 2 - packages/server/src/services/mariadb.ts | 2 +- packages/server/src/services/mongo.ts | 2 +- packages/server/src/services/mount.ts | 4 +- packages/server/src/services/postgres.ts | 2 +- .../server/src/services/preview-deployment.ts | 16 +- packages/server/src/services/redirect.ts | 6 +- packages/server/src/services/redis.ts | 2 +- packages/server/src/services/registry.ts | 4 +- packages/server/src/services/security.ts | 8 +- packages/server/src/services/server.ts | 2 +- packages/server/src/services/settings.ts | 1 - packages/server/src/services/user.ts | 30 +-- packages/server/src/setup/monitoring-setup.ts | 2 +- .../server/src/utils/access-log/handler.ts | 1 - packages/server/src/utils/backups/mysql.ts | 1 - packages/server/src/utils/builders/compose.ts | 1 - .../utils/notifications/database-backup.ts | 1 - .../server/src/utils/traefik/middleware.ts | 2 +- 201 files changed, 434 insertions(+), 1035 deletions(-) delete mode 100644 packages/server/src/services/auth.ts diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 4b405e9c7..0db565995 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -28,7 +28,7 @@ app.use(async (c, next) => { app.post("/deploy", zValidator("json", deployJobSchema), (c) => { const data = c.req.valid("json"); - const res = queue.add(data, { groupName: data.serverId }); + queue.add(data, { groupName: data.serverId }); return c.json( { message: "Deployment Added", diff --git a/apps/api/src/utils.ts b/apps/api/src/utils.ts index d919f29e9..3f3c9698b 100644 --- a/apps/api/src/utils.ts +++ b/apps/api/src/utils.ts @@ -64,7 +64,7 @@ export const deploy = async (job: DeployJob) => { } } } - } catch (error) { + } catch (_) { if (job.applicationType === "application") { await updateApplicationStatus(job.applicationId, "error"); } else if (job.applicationType === "compose") { diff --git a/apps/dokploy/__test__/compose/config/config.test.ts b/apps/dokploy/__test__/compose/config/config.test.ts index 3f98525a2..aed3350f5 100644 --- a/apps/dokploy/__test__/compose/config/config.test.ts +++ b/apps/dokploy/__test__/compose/config/config.test.ts @@ -1,5 +1,5 @@ import { generateRandomHash } from "@dokploy/server"; -import { addSuffixToAllConfigs, addSuffixToConfigsRoot } from "@dokploy/server"; +import { addSuffixToAllConfigs } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/compose/network/network-root.test.ts b/apps/dokploy/__test__/compose/network/network-root.test.ts index 7e06a9f0b..980502fff 100644 --- a/apps/dokploy/__test__/compose/network/network-root.test.ts +++ b/apps/dokploy/__test__/compose/network/network-root.test.ts @@ -293,29 +293,6 @@ networks: dokploy-network: `; -const expectedComposeFile7 = ` -version: "3.8" - -services: - web: - image: nginx:latest - networks: - - dokploy-network - -networks: - dokploy-network: - driver: bridge - driver_opts: - com.docker.network.driver.mtu: 1200 - - backend: - driver: bridge - attachable: true - - external_network: - external: true - name: dokploy-network -`; test("It shoudn't add suffix to dokploy-network", () => { const composeData = load(composeFile7) as ComposeSpecification; diff --git a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts index 2bd91b58a..1b1898c59 100644 --- a/apps/dokploy/__test__/compose/secrets/secret-root.test.ts +++ b/apps/dokploy/__test__/compose/secrets/secret-root.test.ts @@ -1,7 +1,7 @@ import { generateRandomHash } from "@dokploy/server"; import { addSuffixToSecretsRoot } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; -import { dump, load } from "js-yaml"; +import { load } from "js-yaml"; import { expect, test } from "vitest"; test("Generate random hash with 8 characters", () => { diff --git a/apps/dokploy/__test__/compose/volume/volume.test.ts b/apps/dokploy/__test__/compose/volume/volume.test.ts index d4623aeb1..6c4344762 100644 --- a/apps/dokploy/__test__/compose/volume/volume.test.ts +++ b/apps/dokploy/__test__/compose/volume/volume.test.ts @@ -1,8 +1,4 @@ -import { generateRandomHash } from "@dokploy/server"; -import { - addSuffixToAllVolumes, - addSuffixToVolumesInServices, -} from "@dokploy/server"; +import { addSuffixToAllVolumes } from "@dokploy/server"; import type { ComposeSpecification } from "@dokploy/server"; import { load } from "js-yaml"; import { expect, test } from "vitest"; diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index 49d71bc4c..7e4a3c82a 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -126,8 +126,6 @@ test("Should not touch config without host", () => { }); test("Should remove websecure if https rollback to http", () => { - const originalConfig: FileConfig = loadOrCreateConfig("dokploy"); - updateServerTraefik( { ...baseAdmin, certificateType: "letsencrypt" }, "example.com", diff --git a/apps/dokploy/components/auth/login-2fa.tsx b/apps/dokploy/components/auth/login-2fa.tsx index 6a11268e0..634f28146 100644 --- a/apps/dokploy/components/auth/login-2fa.tsx +++ b/apps/dokploy/components/auth/login-2fa.tsx @@ -13,7 +13,6 @@ import { CardTitle } from "@/components/ui/card"; import { InputOTP, InputOTPGroup, - InputOTPSeparator, InputOTPSlot, } from "@/components/ui/input-otp"; import { api } from "@/utils/api"; diff --git a/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx b/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx index cf7314cf6..1eadf8bab 100644 --- a/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/cluster/show-cluster-settings.tsx @@ -29,7 +29,6 @@ import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { Server } from "lucide-react"; import Link from "next/link"; -import React from "react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx b/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx index 4cd839a11..50e36ad76 100644 --- a/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/general/add-command.tsx @@ -17,7 +17,6 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React from "react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx b/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx index a2c6ddcf1..4cd29a36d 100644 --- a/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/ports/show-port.tsx @@ -10,7 +10,6 @@ import { } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Rss, Trash2 } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; import { HandlePorts } from "./handle-ports"; interface Props { diff --git a/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx b/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx index 4ee597917..5c2c5943c 100644 --- a/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/redirects/show-redirects.tsx @@ -9,7 +9,6 @@ import { } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Split, Trash2 } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; import { HandleRedirect } from "./handle-redirect"; diff --git a/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx b/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx index 33022c097..92439f511 100644 --- a/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/security/show-security.tsx @@ -9,7 +9,6 @@ import { } from "@/components/ui/card"; import { api } from "@/utils/api"; import { LockKeyhole, Trash2 } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; import { HandleSecurity } from "./handle-security"; diff --git a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx index 227bca559..3d26716fc 100644 --- a/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/show-resources.tsx @@ -25,7 +25,7 @@ import { import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { InfoIcon } from "lucide-react"; -import React, { useEffect } from "react"; +import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx b/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx index fb6fc0c15..58601fb49 100644 --- a/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/traefik/show-traefik-config.tsx @@ -8,7 +8,6 @@ import { } from "@/components/ui/card"; import { api } from "@/utils/api"; import { File, Loader2 } from "lucide-react"; -import React from "react"; import { UpdateTraefikConfig } from "./update-traefik-config"; interface Props { applicationId: string; diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx index e0f842ce3..2a2d2c032 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/show-volumes.tsx @@ -10,7 +10,6 @@ import { } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Package, Trash2 } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; import type { ServiceType } from "../show-resources"; import { AddVolumes } from "./add-volumes"; diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx index d8481d652..687d0f608 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx @@ -21,7 +21,7 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { PenBoxIcon, Pencil } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx b/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx index c268e6d51..b80450f9f 100644 --- a/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx +++ b/apps/dokploy/components/dashboard/application/deployments/refresh-token.tsx @@ -11,7 +11,6 @@ import { } from "@/components/ui/alert-dialog"; import { api } from "@/utils/api"; import { RefreshCcw } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; interface Props { diff --git a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx index b65a18161..ba20db315 100644 --- a/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show-enviroment.tsx @@ -18,7 +18,7 @@ import { Toggle } from "@/components/ui/toggle"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { EyeIcon, EyeOffIcon } from "lucide-react"; -import React, { type CSSProperties, useEffect, useState } from "react"; +import { type CSSProperties, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/application/environment/show.tsx b/apps/dokploy/components/dashboard/application/environment/show.tsx index 7200f2a71..d97c39e2f 100644 --- a/apps/dokploy/components/dashboard/application/environment/show.tsx +++ b/apps/dokploy/components/dashboard/application/environment/show.tsx @@ -1,5 +1,5 @@ import { Button } from "@/components/ui/button"; -import { Card, CardContent } from "@/components/ui/card"; +import { Card } from "@/components/ui/card"; import { Form } from "@/components/ui/form"; import { Secrets } from "@/components/ui/secrets"; import { api } from "@/utils/api"; diff --git a/apps/dokploy/components/dashboard/application/general/generic/show.tsx b/apps/dokploy/components/dashboard/application/general/generic/show.tsx index 73f5e8f8f..b00a34953 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/show.tsx @@ -11,7 +11,7 @@ import { import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { api } from "@/utils/api"; -import { GitBranch, LockIcon, UploadCloud } from "lucide-react"; +import { GitBranch, UploadCloud } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { SaveBitbucketProvider } from "./save-bitbucket-provider"; diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index 83e4b6f06..0ea331e94 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -7,7 +7,6 @@ import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { Ban, CheckCircle2, Hammer, RefreshCcw, Terminal } from "lucide-react"; import { useRouter } from "next/router"; -import React from "react"; import { toast } from "sonner"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; interface Props { diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx index 55b31f3f0..90800f757 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-builds.tsx @@ -5,7 +5,6 @@ import { Dialog, DialogContent, DialogDescription, - DialogFooter, DialogHeader, DialogTitle, DialogTrigger, diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx index 371276bdd..ec3680f10 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-deployments.tsx @@ -22,7 +22,6 @@ import { RocketIcon, Trash2, } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; import { ShowModalLogs } from "../../settings/web-server/show-modal-logs"; import { AddPreviewDomain } from "./add-preview-domain"; diff --git a/apps/dokploy/components/dashboard/application/update-application.tsx b/apps/dokploy/components/dashboard/application/update-application.tsx index a49fc5383..90b63f08e 100644 --- a/apps/dokploy/components/dashboard/application/update-application.tsx +++ b/apps/dokploy/components/dashboard/application/update-application.tsx @@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, PenBoxIcon, SquarePen } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx b/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx index 1bbd38205..c5a34b3c1 100644 --- a/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx +++ b/apps/dokploy/components/dashboard/compose/advanced/add-command.tsx @@ -19,7 +19,6 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React from "react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/compose/deployments/refresh-token-compose.tsx b/apps/dokploy/components/dashboard/compose/deployments/refresh-token-compose.tsx index 95fafaab1..b062b0994 100644 --- a/apps/dokploy/components/dashboard/compose/deployments/refresh-token-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/deployments/refresh-token-compose.tsx @@ -11,7 +11,6 @@ import { } from "@/components/ui/alert-dialog"; import { api } from "@/utils/api"; import { RefreshCcw } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; interface Props { diff --git a/apps/dokploy/components/dashboard/compose/general/generic/show.tsx b/apps/dokploy/components/dashboard/compose/general/generic/show.tsx index 1681039cc..347c134e3 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/show.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/show.tsx @@ -7,7 +7,7 @@ import { import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { api } from "@/utils/api"; -import { CodeIcon, GitBranch, LockIcon } from "lucide-react"; +import { CodeIcon, GitBranch } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { ComposeFileEditor } from "../compose-file-editor"; diff --git a/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx b/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx index b1a00985f..4462ef0eb 100644 --- a/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx @@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, Dices } from "lucide-react"; +import { AlertTriangle } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/compose/general/show.tsx b/apps/dokploy/components/dashboard/compose/general/show.tsx index d002b409c..71752525c 100644 --- a/apps/dokploy/components/dashboard/compose/general/show.tsx +++ b/apps/dokploy/components/dashboard/compose/general/show.tsx @@ -7,7 +7,6 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import React from "react"; import { ComposeActions } from "./actions"; import { ShowProviderFormCompose } from "./generic/show"; interface Props { diff --git a/apps/dokploy/components/dashboard/compose/logs/show.tsx b/apps/dokploy/components/dashboard/compose/logs/show.tsx index 4530e0ddd..571190549 100644 --- a/apps/dokploy/components/dashboard/compose/logs/show.tsx +++ b/apps/dokploy/components/dashboard/compose/logs/show.tsx @@ -18,7 +18,7 @@ import { SelectValue, } from "@/components/ui/select"; import { api } from "@/utils/api"; -import { Loader, Loader2 } from "lucide-react"; +import { Loader2 } from "lucide-react"; import dynamic from "next/dynamic"; import { useEffect, useState } from "react"; export const DockerLogs = dynamic( diff --git a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx index 21fe28d4a..6619ceae7 100644 --- a/apps/dokploy/components/dashboard/database/backups/show-backups.tsx +++ b/apps/dokploy/components/dashboard/database/backups/show-backups.tsx @@ -16,7 +16,6 @@ import { import { api } from "@/utils/api"; import { DatabaseBackup, Play, Trash2 } from "lucide-react"; import Link from "next/link"; -import React from "react"; import { toast } from "sonner"; import type { ServiceType } from "../../application/advanced/show-resources"; import { AddBackup } from "./add-backup"; diff --git a/apps/dokploy/components/dashboard/database/backups/update-backup.tsx b/apps/dokploy/components/dashboard/database/backups/update-backup.tsx index 0083bb1df..99f7692a9 100644 --- a/apps/dokploy/components/dashboard/database/backups/update-backup.tsx +++ b/apps/dokploy/components/dashboard/database/backups/update-backup.tsx @@ -35,7 +35,7 @@ import { Switch } from "@/components/ui/switch"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown, PenBoxIcon, Pencil } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, PenBoxIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/docker/logs/since-logs-filter.tsx b/apps/dokploy/components/dashboard/docker/logs/since-logs-filter.tsx index b7caafe71..44f2cdfc3 100644 --- a/apps/dokploy/components/dashboard/docker/logs/since-logs-filter.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/since-logs-filter.tsx @@ -15,7 +15,6 @@ import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; import { cn } from "@/lib/utils"; import { CheckIcon } from "lucide-react"; -import React from "react"; export type TimeFilter = "all" | "1h" | "6h" | "24h" | "168h" | "720h"; diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx index c25acc67f..ee674e0fe 100644 --- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx @@ -9,7 +9,6 @@ import { import { cn } from "@/lib/utils"; import { FancyAnsi } from "fancy-ansi"; import { escapeRegExp } from "lodash"; -import React from "react"; import { type LogLine, getLogType } from "./utils"; interface LogLineProps { diff --git a/apps/dokploy/components/dashboard/docker/show/colums.tsx b/apps/dokploy/components/dashboard/docker/show/colums.tsx index 3feae176a..1cf0200f2 100644 --- a/apps/dokploy/components/dashboard/docker/show/colums.tsx +++ b/apps/dokploy/components/dashboard/docker/show/colums.tsx @@ -1,6 +1,5 @@ import type { ColumnDef } from "@tanstack/react-table"; import { ArrowUpDown, MoreHorizontal } from "lucide-react"; -import * as React from "react"; import { Button } from "@/components/ui/button"; import { diff --git a/apps/dokploy/components/dashboard/docker/show/show-containers.tsx b/apps/dokploy/components/dashboard/docker/show/show-containers.tsx index c66c9b9ba..024b00618 100644 --- a/apps/dokploy/components/dashboard/docker/show/show-containers.tsx +++ b/apps/dokploy/components/dashboard/docker/show/show-containers.tsx @@ -1,18 +1,3 @@ -import { - type ColumnFiltersState, - type SortingState, - type VisibilityState, - flexRender, - getCoreRowModel, - getFilteredRowModel, - getPaginationRowModel, - getSortedRowModel, - useReactTable, -} from "@tanstack/react-table"; -import { ChevronDown, Container } from "lucide-react"; -import * as React from "react"; - -import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Card, @@ -37,6 +22,19 @@ import { TableRow, } from "@/components/ui/table"; import { type RouterOutputs, api } from "@/utils/api"; +import { + type ColumnFiltersState, + type SortingState, + type VisibilityState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { ChevronDown, Container } from "lucide-react"; +import * as React from "react"; import { columns } from "./colums"; export type Container = NonNullable< RouterOutputs["docker"]["getContainers"] diff --git a/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx b/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx index ed2ed1974..c9272f293 100644 --- a/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx +++ b/apps/dokploy/components/dashboard/file-system/show-traefik-system.tsx @@ -7,9 +7,8 @@ import { CardTitle, } from "@/components/ui/card"; import { Tree } from "@/components/ui/file-tree"; -import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; -import { FileIcon, Folder, Link, Loader2, Workflow } from "lucide-react"; +import { FileIcon, Folder, Loader2, Workflow } from "lucide-react"; import React from "react"; import { ShowTraefikFile } from "./show-traefik-file"; diff --git a/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx b/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx index f20449178..4a5c43a26 100644 --- a/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx +++ b/apps/dokploy/components/dashboard/mariadb/general/show-external-mariadb-credentials.tsx @@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx b/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx index 98773685f..ad6b1164b 100644 --- a/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx +++ b/apps/dokploy/components/dashboard/mariadb/general/show-general-mariadb.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react"; -import React, { useState } from "react"; +import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; diff --git a/apps/dokploy/components/dashboard/mariadb/general/show-internal-mariadb-credentials.tsx b/apps/dokploy/components/dashboard/mariadb/general/show-internal-mariadb-credentials.tsx index b409ac4d8..170269269 100644 --- a/apps/dokploy/components/dashboard/mariadb/general/show-internal-mariadb-credentials.tsx +++ b/apps/dokploy/components/dashboard/mariadb/general/show-internal-mariadb-credentials.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import React from "react"; interface Props { mariadbId: string; diff --git a/apps/dokploy/components/dashboard/mariadb/update-mariadb.tsx b/apps/dokploy/components/dashboard/mariadb/update-mariadb.tsx index 4c9be0903..48d944898 100644 --- a/apps/dokploy/components/dashboard/mariadb/update-mariadb.tsx +++ b/apps/dokploy/components/dashboard/mariadb/update-mariadb.tsx @@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, PenBoxIcon, SquarePen } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx b/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx index 6dd2e9198..9fe6e7137 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-external-mongo-credentials.tsx @@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx b/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx index df01e36d4..a20d46378 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-general-mongo.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react"; -import React, { useState } from "react"; +import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; diff --git a/apps/dokploy/components/dashboard/mongo/general/show-internal-mongo-credentials.tsx b/apps/dokploy/components/dashboard/mongo/general/show-internal-mongo-credentials.tsx index 6636688d9..e66ea8c36 100644 --- a/apps/dokploy/components/dashboard/mongo/general/show-internal-mongo-credentials.tsx +++ b/apps/dokploy/components/dashboard/mongo/general/show-internal-mongo-credentials.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import React from "react"; interface Props { mongoId: string; diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx index 99be6d9d9..2bf6c9005 100644 --- a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-compose-monitoring.tsx @@ -1,6 +1,5 @@ import { Button } from "@/components/ui/button"; import { - Card, CardContent, CardDescription, CardHeader, diff --git a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx index 64a46bdb2..278e09360 100644 --- a/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/free/container/show-free-container-monitoring.tsx @@ -1,13 +1,7 @@ -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Progress } from "@/components/ui/progress"; import { api } from "@/utils/api"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { DockerBlockChart } from "./docker-block-chart"; import { DockerCpuChart } from "./docker-cpu-chart"; import { DockerDiskChart } from "./docker-disk-chart"; diff --git a/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx index 87c030570..e299cfb38 100644 --- a/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx @@ -7,7 +7,6 @@ import { } from "@/components/ui/select"; import { api } from "@/utils/api"; import { Clock, Cpu, HardDrive, Loader2, MemoryStick } from "lucide-react"; -import type React from "react"; import { useEffect, useState } from "react"; import { CPUChart } from "./cpu-chart"; import { DiskChart } from "./disk-chart"; diff --git a/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx b/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx index dc1ca3a7d..7a0527b17 100644 --- a/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx +++ b/apps/dokploy/components/dashboard/mysql/general/show-external-mysql-credentials.tsx @@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx b/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx index 56a191ceb..13f46caee 100644 --- a/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx +++ b/apps/dokploy/components/dashboard/mysql/general/show-general-mysql.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react"; -import React, { useState } from "react"; +import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; diff --git a/apps/dokploy/components/dashboard/mysql/general/show-internal-mysql-credentials.tsx b/apps/dokploy/components/dashboard/mysql/general/show-internal-mysql-credentials.tsx index 2c09efb84..3f1872371 100644 --- a/apps/dokploy/components/dashboard/mysql/general/show-internal-mysql-credentials.tsx +++ b/apps/dokploy/components/dashboard/mysql/general/show-internal-mysql-credentials.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import React from "react"; interface Props { mysqlId: string; diff --git a/apps/dokploy/components/dashboard/mysql/update-mysql.tsx b/apps/dokploy/components/dashboard/mysql/update-mysql.tsx index 645575cdc..efe1eb11d 100644 --- a/apps/dokploy/components/dashboard/mysql/update-mysql.tsx +++ b/apps/dokploy/components/dashboard/mysql/update-mysql.tsx @@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, PenBoxIcon, SquarePen } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 24bb5c0eb..905a244cc 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -12,7 +12,7 @@ import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import { PenBoxIcon, Plus, SquarePen } from "lucide-react"; +import { PenBoxIcon, Plus } from "lucide-react"; import { useEffect, useState } from "react"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx index 6e912db95..2bae245ec 100644 --- a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx +++ b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx @@ -11,7 +11,7 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React, { useEffect } from "react"; +import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx b/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx index e8fff7dca..dbd57d0bf 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-external-postgres-credentials.tsx @@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx b/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx index 43c3f4322..89d275236 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-general-postgres.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react"; -import React, { useState } from "react"; +import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; diff --git a/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx b/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx index e01226101..545150f87 100644 --- a/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx +++ b/apps/dokploy/components/dashboard/postgres/general/show-internal-postgres-credentials.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import React from "react"; interface Props { postgresId: string; diff --git a/apps/dokploy/components/dashboard/postgres/update-postgres.tsx b/apps/dokploy/components/dashboard/postgres/update-postgres.tsx index 54ad5bce0..7be6908f1 100644 --- a/apps/dokploy/components/dashboard/postgres/update-postgres.tsx +++ b/apps/dokploy/components/dashboard/postgres/update-postgres.tsx @@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, PenBoxIcon, SquarePen } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/project/add-database.tsx b/apps/dokploy/components/dashboard/project/add-database.tsx index 1ca0d6a5e..cd75ba468 100644 --- a/apps/dokploy/components/dashboard/project/add-database.tsx +++ b/apps/dokploy/components/dashboard/project/add-database.tsx @@ -18,7 +18,6 @@ import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index cc6962aab..3965f8172 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -57,7 +57,6 @@ import { BookText, CheckIcon, ChevronsUpDown, - Code, Github, Globe, HelpCircle, diff --git a/apps/dokploy/components/dashboard/projects/handle-project.tsx b/apps/dokploy/components/dashboard/projects/handle-project.tsx index 492c03c93..f5d62dfc6 100644 --- a/apps/dokploy/components/dashboard/projects/handle-project.tsx +++ b/apps/dokploy/components/dashboard/projects/handle-project.tsx @@ -21,7 +21,6 @@ import { import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; import { PlusIcon, SquarePen } from "lucide-react"; diff --git a/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx b/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx index 40ae0574e..25b5f2ba7 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx @@ -19,7 +19,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import React, { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx b/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx index ec4aeb6cb..e309ef49a 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-general-redis.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { api } from "@/utils/api"; import { Ban, CheckCircle2, RefreshCcw, Terminal } from "lucide-react"; -import React, { useState } from "react"; +import { useState } from "react"; import { toast } from "sonner"; import { type LogLine, parseLogs } from "../../docker/logs/utils"; import { DockerTerminalModal } from "../../settings/web-server/docker-terminal-modal"; diff --git a/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx b/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx index 092006748..47ad0df0b 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-internal-redis-credentials.tsx @@ -3,7 +3,6 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; -import React from "react"; interface Props { redisId: string; diff --git a/apps/dokploy/components/dashboard/redis/update-redis.tsx b/apps/dokploy/components/dashboard/redis/update-redis.tsx index c3557bee8..193aec3b3 100644 --- a/apps/dokploy/components/dashboard/redis/update-redis.tsx +++ b/apps/dokploy/components/dashboard/redis/update-redis.tsx @@ -21,7 +21,7 @@ import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle, PenBoxIcon, SquarePen } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/requests/columns.tsx b/apps/dokploy/components/dashboard/requests/columns.tsx index 529d64a48..c1814190e 100644 --- a/apps/dokploy/components/dashboard/requests/columns.tsx +++ b/apps/dokploy/components/dashboard/requests/columns.tsx @@ -3,7 +3,6 @@ import { Button } from "@/components/ui/button"; import type { ColumnDef } from "@tanstack/react-table"; import { format } from "date-fns"; import { ArrowUpDown } from "lucide-react"; -import * as React from "react"; import type { LogEntry } from "./show-requests"; export const getStatusColor = (status: number) => { diff --git a/apps/dokploy/components/dashboard/requests/show-requests.tsx b/apps/dokploy/components/dashboard/requests/show-requests.tsx index 05ba6e51f..c3d92dd6d 100644 --- a/apps/dokploy/components/dashboard/requests/show-requests.tsx +++ b/apps/dokploy/components/dashboard/requests/show-requests.tsx @@ -11,7 +11,6 @@ import { import { type RouterOutputs, api } from "@/utils/api"; import { ArrowDownUp } from "lucide-react"; import Link from "next/link"; -import * as React from "react"; import { toast } from "sonner"; import { RequestDistributionChart } from "./request-distribution-chart"; import { RequestsTable } from "./requests-table"; diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 7ea53d72f..5726dc99a 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -7,9 +7,7 @@ import { PostgresqlIcon, RedisIcon, } from "@/components/icons/data-tools-icons"; -import { Badge } from "@/components/ui/badge"; import { - Command, CommandDialog, CommandEmpty, CommandGroup, diff --git a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx index c76ec33e8..029eaa90f 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx @@ -23,7 +23,7 @@ import { PlusIcon, } from "lucide-react"; import Link from "next/link"; -import React, { useState } from "react"; +import { useState } from "react"; const stripePromise = loadStripe( process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!, diff --git a/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx b/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx index 201aed445..64362b25c 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-welcome-dokploy.tsx @@ -6,7 +6,6 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { api } from "@/utils/api"; -import type React from "react"; import { useEffect, useState } from "react"; export const ShowWelcomeDokploy = () => { diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx index b55396bfe..ba3eefa56 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx @@ -33,7 +33,6 @@ import { } from "@/components/ui/tooltip"; import { api } from "@/utils/api"; import { Boxes, HelpCircle, LockIcon, MoreHorizontal } from "lucide-react"; -import React from "react"; import { toast } from "sonner"; import { AddNode } from "./add-node"; import { ShowNodeData } from "./show-node-data"; diff --git a/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx index 9cb605a64..2c0f30466 100644 --- a/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx @@ -1,8 +1,4 @@ -import { - BitbucketIcon, - GithubIcon, - GitlabIcon, -} from "@/components/icons/data-tools-icons"; +import { BitbucketIcon } from "@/components/icons/data-tools-icons"; import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { CardContent } from "@/components/ui/card"; diff --git a/apps/dokploy/components/dashboard/settings/git/bitbucket/edit-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/settings/git/bitbucket/edit-bitbucket-provider.tsx index 25c5e5daf..e5a7f7529 100644 --- a/apps/dokploy/components/dashboard/settings/git/bitbucket/edit-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/bitbucket/edit-bitbucket-provider.tsx @@ -20,7 +20,7 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Edit, PenBoxIcon } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/settings/git/gitlab/edit-gitlab-provider.tsx b/apps/dokploy/components/dashboard/settings/git/gitlab/edit-gitlab-provider.tsx index fb688b89d..6c5d488eb 100644 --- a/apps/dokploy/components/dashboard/settings/git/gitlab/edit-gitlab-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/gitlab/edit-gitlab-provider.tsx @@ -20,7 +20,7 @@ import { import { Input } from "@/components/ui/input"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { Edit, PenBoxIcon } from "lucide-react"; +import { PenBoxIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx index b1da3ec1a..918df4353 100644 --- a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx @@ -3,7 +3,6 @@ import { Dialog, DialogContent, DialogDescription, - DialogFooter, DialogHeader, DialogTitle, DialogTrigger, diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index ca1bf3c21..f4299709a 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -1,5 +1,4 @@ import { AlertBlock } from "@/components/shared/alert-block"; -import { DialogAction } from "@/components/shared/dialog-action"; import { Button } from "@/components/ui/button"; import { Card, @@ -18,7 +17,6 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import { authClient } from "@/lib/auth-client"; import { generateSHA256Hash } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-dokploy-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-dokploy-actions.tsx index 3a1af2063..f57dad3cd 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-dokploy-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-dokploy-actions.tsx @@ -1,5 +1,4 @@ import { Button } from "@/components/ui/button"; -import React from "react"; import { UpdateServerIp } from "@/components/dashboard/settings/web-server/update-server-ip"; import { diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx index c45c0c8b5..cb60effd1 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx @@ -1,5 +1,4 @@ import { Button } from "@/components/ui/button"; -import React from "react"; import { DropdownMenu, diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx index 17a6ae757..0968931d7 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-traefik-actions.tsx @@ -1,14 +1,4 @@ import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; -import React from "react"; import { DropdownMenu, @@ -20,10 +10,8 @@ import { DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { api } from "@/utils/api"; -import { toast } from "sonner"; - -import { cn } from "@/lib/utils"; import { useTranslation } from "next-i18next"; +import { toast } from "sonner"; import { EditTraefikEnv } from "../../web-server/edit-traefik-env"; import { ManageTraefikPorts } from "../../web-server/manage-traefik-ports"; import { ShowModalLogs } from "../../web-server/show-modal-logs"; diff --git a/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx b/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx index 3cda7e806..ec60fed6d 100644 --- a/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx @@ -9,7 +9,6 @@ import { CardTitle, } from "@/components/ui/card"; import { api } from "@/utils/api"; -import { TRPCClientError } from "@trpc/client"; import { CheckCircle2, Cpu, Loader2, RefreshCw, XCircle } from "lucide-react"; import { useEffect, useState } from "react"; import { toast } from "sonner"; diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx index db9545a64..23173047e 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx @@ -2,7 +2,6 @@ import { AlertBlock } from "@/components/shared/alert-block"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { - Card, CardContent, CardDescription, CardHeader, @@ -30,7 +29,6 @@ import { PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; -import { ScrollArea } from "@/components/ui/scroll-area"; import { extractServices } from "@/pages/dashboard/project/[projectId]"; import { api } from "@/utils/api"; import { useUrl } from "@/utils/hooks/use-url"; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx index 1cdab23bc..ad82085c7 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-docker-containers-modal.tsx @@ -1,10 +1,4 @@ -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { useState } from "react"; import { ShowContainers } from "../../docker/show/show-containers"; diff --git a/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx b/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx index a47005d07..b86311840 100644 --- a/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/show-swarm-overview-modal.tsx @@ -1,12 +1,5 @@ -import { - Dialog, - DialogContent, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; -import { ContainerIcon } from "lucide-react"; import { useState } from "react"; import SwarmMonitorCard from "../../swarm/monitoring-card"; diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 0ccb23e7d..ff56698e4 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -26,7 +26,6 @@ import { } from "@/components/ui/table"; import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; -import copy from "copy-to-clipboard"; import { format } from "date-fns"; import { MoreHorizontal, Users } from "lucide-react"; import { Loader2 } from "lucide-react"; @@ -134,12 +133,45 @@ export const ShowUsers = () => { )} {member.role !== "owner" && ( - { - if (isCloud) { + <> + {!isCloud && ( + { + await mutateAsync({ + userId: member.user.id, + }) + .then(() => { + toast.success( + "User deleted successfully", + ); + refetch(); + }) + .catch(() => { + toast.error( + "Error deleting destination", + ); + }); + }} + > + + e.preventDefault() + } + > + Delete User + + + )} + + { const { error } = await authClient.organization.removeMember( { @@ -149,39 +181,24 @@ export const ShowUsers = () => { if (!error) { toast.success( - "User deleted successfully", + "User unlinked successfully", ); refetch(); } else { toast.error( - "Error deleting user", + "Error unlinking user", ); } - } else { - await mutateAsync({ - userId: member.user.id, - }) - .then(() => { - toast.success( - "User deleted successfully", - ); - refetch(); - }) - .catch(() => { - toast.error( - "Error deleting destination", - ); - }); - } - }} - > - e.preventDefault()} + }} > - Delete User - - + e.preventDefault()} + > + Unlink User + + + )} diff --git a/apps/dokploy/components/dashboard/settings/web-domain.tsx b/apps/dokploy/components/dashboard/settings/web-domain.tsx index 13750e2aa..3b3f70ba5 100644 --- a/apps/dokploy/components/dashboard/settings/web-domain.tsx +++ b/apps/dokploy/components/dashboard/settings/web-domain.tsx @@ -24,7 +24,7 @@ import { } from "@/components/ui/select"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { GlobeIcon, ServerIcon, User } from "lucide-react"; +import { GlobeIcon } from "lucide-react"; import { useTranslation } from "next-i18next"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; diff --git a/apps/dokploy/components/dashboard/settings/web-server.tsx b/apps/dokploy/components/dashboard/settings/web-server.tsx index 0cc4772f2..326cb0eaf 100644 --- a/apps/dokploy/components/dashboard/settings/web-server.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server.tsx @@ -5,11 +5,9 @@ import { CardHeader, CardTitle, } from "@/components/ui/card"; -import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { ServerIcon } from "lucide-react"; import { useTranslation } from "next-i18next"; -import React from "react"; import { ShowDokployActions } from "./servers/actions/show-dokploy-actions"; import { ShowStorageActions } from "./servers/actions/show-storage-actions"; import { ShowTraefikActions } from "./servers/actions/show-traefik-actions"; diff --git a/apps/dokploy/components/dashboard/settings/web-server/local-server-config.tsx b/apps/dokploy/components/dashboard/settings/web-server/local-server-config.tsx index f68046dbb..e30408e6d 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/local-server-config.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/local-server-config.tsx @@ -18,7 +18,6 @@ import { cn } from "@/lib/utils"; import { zodResolver } from "@hookform/resolvers/zod"; import { Settings } from "lucide-react"; import { useTranslation } from "next-i18next"; -import React from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; diff --git a/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx b/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx index 12e7b6704..bedecf517 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/show-modal-logs.tsx @@ -6,7 +6,6 @@ import { DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; -import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; import { Label } from "@/components/ui/label"; import { Select, diff --git a/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx b/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx index 816687f58..2cfc459b6 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/update-server.tsx @@ -1,4 +1,3 @@ -import { AlertBlock } from "@/components/shared/alert-block"; import { Button } from "@/components/ui/button"; import { Dialog, @@ -14,7 +13,6 @@ import { Info, RefreshCcw, Server, - ServerCrash, Sparkles, Stars, } from "lucide-react"; diff --git a/apps/dokploy/components/dashboard/swarm/applications/columns.tsx b/apps/dokploy/components/dashboard/swarm/applications/columns.tsx index 69ccc41d6..ab058e851 100644 --- a/apps/dokploy/components/dashboard/swarm/applications/columns.tsx +++ b/apps/dokploy/components/dashboard/swarm/applications/columns.tsx @@ -1,6 +1,5 @@ import type { ColumnDef } from "@tanstack/react-table"; import { ArrowUpDown, MoreHorizontal } from "lucide-react"; -import * as React from "react"; import { Button } from "@/components/ui/button"; import { diff --git a/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx b/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx index 41d7b113b..681afd755 100644 --- a/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx +++ b/apps/dokploy/components/dashboard/swarm/applications/show-applications.tsx @@ -9,7 +9,6 @@ import { } from "@/components/ui/dialog"; import { api } from "@/utils/api"; import { Layers, Loader2 } from "lucide-react"; -import React from "react"; import { type ApplicationList, columns } from "./columns"; import { DataTable } from "./data-table"; diff --git a/apps/dokploy/components/icons/data-tools-icons.tsx b/apps/dokploy/components/icons/data-tools-icons.tsx index 93da14a06..e2c0922a8 100644 --- a/apps/dokploy/components/icons/data-tools-icons.tsx +++ b/apps/dokploy/components/icons/data-tools-icons.tsx @@ -1,5 +1,4 @@ import { cn } from "@/lib/utils"; -import React from "react"; // https://worldvectorlogo.com/downloaded/redis Ref diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index e0931b08f..939d10841 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -1,15 +1,14 @@ "use client"; import { Activity, - AudioWaveform, BarChartHorizontalBigIcon, Bell, BlocksIcon, BookIcon, Boxes, ChevronRight, + ChevronsUpDown, CircleHelp, - Command, CreditCard, Database, Folder, @@ -27,8 +26,6 @@ import { Trash2, User, Users, - ChevronsUpDown, - Plus, } from "lucide-react"; import { usePathname } from "next/navigation"; import type * as React from "react"; @@ -47,6 +44,14 @@ import { CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Separator } from "@/components/ui/separator"; import { SIDEBAR_COOKIE_NAME, @@ -68,29 +73,20 @@ import { SidebarTrigger, useSidebar, } from "@/components/ui/sidebar"; +import { authClient } from "@/lib/auth-client"; import { cn } from "@/lib/utils"; import type { AppRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import type { inferRouterOutputs } from "@trpc/server"; import Link from "next/link"; import { useRouter } from "next/router"; -import { Logo } from "../shared/logo"; -import { UpdateServerButton } from "./update-server"; -import { UserNav } from "./user-nav"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuShortcut, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { authClient } from "@/lib/auth-client"; import { toast } from "sonner"; import { AddOrganization } from "../dashboard/organization/handle-organization"; import { DialogAction } from "../shared/dialog-action"; +import { Logo } from "../shared/logo"; import { Button } from "../ui/button"; +import { UpdateServerButton } from "./update-server"; +import { UserNav } from "./user-nav"; // The types of the queries we are going to use type AuthQueryOutput = inferRouterOutputs["auth"]["get"]; diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 30579f300..196f6d77d 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -20,9 +20,7 @@ import { Languages } from "@/lib/languages"; import { api } from "@/utils/api"; import useLocale from "@/utils/hooks/use-locale"; import { ChevronsUpDown } from "lucide-react"; -import { useTranslation } from "next-i18next"; import { useRouter } from "next/router"; -import { useEffect, useRef, useState } from "react"; import { ModeToggle } from "../ui/modeToggle"; import { SidebarMenuButton } from "../ui/sidebar"; diff --git a/apps/dokploy/components/shared/dialog-action.tsx b/apps/dokploy/components/shared/dialog-action.tsx index 3724242d0..444440a2d 100644 --- a/apps/dokploy/components/shared/dialog-action.tsx +++ b/apps/dokploy/components/shared/dialog-action.tsx @@ -9,7 +9,6 @@ import { AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; -import { Button } from "../ui/button"; interface Props { title?: string | React.ReactNode; diff --git a/apps/dokploy/components/shared/drawer-logs.tsx b/apps/dokploy/components/shared/drawer-logs.tsx index f5a56cd6a..5e4ab554b 100644 --- a/apps/dokploy/components/shared/drawer-logs.tsx +++ b/apps/dokploy/components/shared/drawer-logs.tsx @@ -1,6 +1,3 @@ -import { DialogAction } from "@/components/shared/dialog-action"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Sheet, SheetContent, @@ -8,10 +5,8 @@ import { SheetHeader, SheetTitle, } from "@/components/ui/sheet"; -import { api } from "@/utils/api"; -import { Ban, CheckCircle2, Loader2, RefreshCcw, Terminal } from "lucide-react"; -import React, { useState, useEffect, useRef } from "react"; -import { toast } from "sonner"; +import { Loader2 } from "lucide-react"; +import { useEffect, useRef, useState } from "react"; import { TerminalLine } from "../dashboard/docker/logs/terminal-line"; import type { LogLine } from "../dashboard/docker/logs/utils"; diff --git a/apps/dokploy/components/shared/logo.tsx b/apps/dokploy/components/shared/logo.tsx index 5d192cfdc..086ef3b0c 100644 --- a/apps/dokploy/components/shared/logo.tsx +++ b/apps/dokploy/components/shared/logo.tsx @@ -1,5 +1,3 @@ -import React from "react"; - interface Props { className?: string; } diff --git a/apps/dokploy/components/ui/modeToggle.tsx b/apps/dokploy/components/ui/modeToggle.tsx index 7965a3395..9b6ba27b9 100644 --- a/apps/dokploy/components/ui/modeToggle.tsx +++ b/apps/dokploy/components/ui/modeToggle.tsx @@ -3,7 +3,6 @@ import { Button } from "@/components/ui/button"; import { Moon, Sun } from "lucide-react"; import { useTheme } from "next-themes"; -import * as React from "react"; export function ModeToggle() { const { theme, setTheme } = useTheme(); diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index 38f48784c..febd1c0e2 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -1,5 +1,4 @@ import { drizzle } from "drizzle-orm/postgres-js"; -import { migrate } from "drizzle-orm/postgres-js/migrator"; import { nanoid } from "nanoid"; import postgres from "postgres"; import * as schema from "./server/db/schema"; diff --git a/apps/dokploy/pages/api/deploy/github.ts b/apps/dokploy/pages/api/deploy/github.ts index 761c38663..5e64d8b26 100644 --- a/apps/dokploy/pages/api/deploy/github.ts +++ b/apps/dokploy/pages/api/deploy/github.ts @@ -3,9 +3,7 @@ import { applications, compose, github } from "@/server/db/schema"; import type { DeploymentJob } from "@/server/queues/queue-types"; import { myQueue } from "@/server/queues/queueSetup"; import { deploy } from "@/server/utils/deploy"; -import { generateRandomDomain } from "@/templates/utils"; import { - type Domain, IS_CLOUD, createPreviewDeployment, findPreviewDeploymentByApplicationId, diff --git a/apps/dokploy/pages/api/providers/github/setup.ts b/apps/dokploy/pages/api/providers/github/setup.ts index 9a03ed3d7..ac5e7a6b0 100644 --- a/apps/dokploy/pages/api/providers/github/setup.ts +++ b/apps/dokploy/pages/api/providers/github/setup.ts @@ -1,13 +1,6 @@ import { db } from "@/server/db"; import { github } from "@/server/db/schema"; -import { - auth, - createGithub, - findAdminByAuthId, - findAuthById, - findUserByAuthId, - findUserById, -} from "@dokploy/server"; +import { createGithub } from "@dokploy/server"; import { eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import { Octokit } from "octokit"; diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index d9cbedc8a..6200a79ec 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -1,7 +1,7 @@ import { buffer } from "node:stream/consumers"; import { db } from "@/server/db"; import { server, users_temp } from "@/server/db/schema"; -import { findAdminById, findUserById } from "@dokploy/server"; +import { findUserById } from "@dokploy/server"; import { asc, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import Stripe from "stripe"; diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index 5685dcfc5..a9d80353f 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -5,7 +5,7 @@ import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Dashboard = () => { diff --git a/apps/dokploy/pages/dashboard/monitoring.tsx b/apps/dokploy/pages/dashboard/monitoring.tsx index c5c617a7f..4d8b072f5 100644 --- a/apps/dokploy/pages/dashboard/monitoring.tsx +++ b/apps/dokploy/pages/dashboard/monitoring.tsx @@ -1,17 +1,13 @@ import { ContainerFreeMonitoring } from "@/components/dashboard/monitoring/free/container/show-free-container-monitoring"; import { ShowPaidMonitoring } from "@/components/dashboard/monitoring/paid/servers/show-paid-monitoring"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; -import { AlertBlock } from "@/components/shared/alert-block"; import { Card } from "@/components/ui/card"; -import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { useLocalStorage } from "@/hooks/useLocalStorage"; import { api } from "@/utils/api"; import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { Loader2 } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; -import type React from "react"; import type { ReactElement } from "react"; const BASE_URL = "http://localhost:3001/metrics"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 7eebf7083..59dba68ce 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -28,7 +28,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -42,7 +41,7 @@ import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import copy from "copy-to-clipboard"; -import { GlobeIcon, HelpCircle, ServerOff, Trash2 } from "lucide-react"; +import { GlobeIcon, HelpCircle, ServerOff } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -50,7 +49,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, useEffect, type ReactElement } from "react"; +import { type ReactElement, useEffect, useState } from "react"; import { toast } from "sonner"; import superjson from "superjson"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index 46c9864b4..c1331e23f 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -22,7 +22,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -45,7 +44,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, useEffect, type ReactElement } from "react"; +import { type ReactElement, useEffect, useState } from "react"; import { toast } from "sonner"; import superjson from "superjson"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 6aa7677a3..033b88a9d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -24,7 +24,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -37,7 +36,7 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; -import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; +import { HelpCircle, ServerOff } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -45,8 +44,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, type ReactElement } from "react"; -import { toast } from "sonner"; +import { type ReactElement, useState } from "react"; import superjson from "superjson"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index 2e3aae31f..dea8cd57b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -24,7 +24,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -37,7 +36,7 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; -import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; +import { HelpCircle, ServerOff } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -45,8 +44,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, type ReactElement } from "react"; -import { toast } from "sonner"; +import { type ReactElement, useState } from "react"; import superjson from "superjson"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index 3e75603dd..cc4eb4aa3 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -24,7 +24,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -37,7 +36,7 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; -import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; +import { HelpCircle, ServerOff } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -45,8 +44,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, type ReactElement } from "react"; -import { toast } from "sonner"; +import { type ReactElement, useState } from "react"; import superjson from "superjson"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index dd0c312d0..d0f1dc106 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -24,7 +24,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -37,7 +36,7 @@ import { appRouter } from "@/server/api/root"; import { api } from "@/utils/api"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; -import { HelpCircle, ServerOff, Trash2 } from "lucide-react"; +import { HelpCircle, ServerOff } from "lucide-react"; import type { GetServerSidePropsContext, InferGetServerSidePropsType, @@ -45,8 +44,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, type ReactElement } from "react"; -import { toast } from "sonner"; +import { type ReactElement, useState } from "react"; import superjson from "superjson"; type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index c7e5643a6..2b053df47 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -23,7 +23,6 @@ import { CardTitle, } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; -import { Switch } from "@/components/ui/switch"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Tooltip, @@ -44,8 +43,7 @@ import type { import Head from "next/head"; import Link from "next/link"; import { useRouter } from "next/router"; -import React, { useState, type ReactElement } from "react"; -import { toast } from "sonner"; +import { type ReactElement, useState } from "react"; import superjson from "superjson"; type TabState = "projects" | "monitoring" | "settings" | "advanced"; diff --git a/apps/dokploy/pages/dashboard/projects.tsx b/apps/dokploy/pages/dashboard/projects.tsx index abeee6695..49427c25b 100644 --- a/apps/dokploy/pages/dashboard/projects.tsx +++ b/apps/dokploy/pages/dashboard/projects.tsx @@ -6,7 +6,6 @@ import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; import dynamic from "next/dynamic"; -import type React from "react"; import type { ReactElement } from "react"; import superjson from "superjson"; diff --git a/apps/dokploy/pages/dashboard/requests.tsx b/apps/dokploy/pages/dashboard/requests.tsx index 580b936a2..cb4545875 100644 --- a/apps/dokploy/pages/dashboard/requests.tsx +++ b/apps/dokploy/pages/dashboard/requests.tsx @@ -4,7 +4,6 @@ import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import type { GetServerSidePropsContext } from "next"; import type { ReactElement } from "react"; -import * as React from "react"; export default function Requests() { return ; diff --git a/apps/dokploy/pages/dashboard/settings/billing.tsx b/apps/dokploy/pages/dashboard/settings/billing.tsx index 3acce0b01..ee1ecdbe5 100644 --- a/apps/dokploy/pages/dashboard/settings/billing.tsx +++ b/apps/dokploy/pages/dashboard/settings/billing.tsx @@ -6,7 +6,7 @@ import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/certificates.tsx b/apps/dokploy/pages/dashboard/settings/certificates.tsx index d045208e1..96bec90bf 100644 --- a/apps/dokploy/pages/dashboard/settings/certificates.tsx +++ b/apps/dokploy/pages/dashboard/settings/certificates.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { return ( diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index 8d70f8692..77ece29b8 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { IS_CLOUD, validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/destinations.tsx b/apps/dokploy/pages/dashboard/settings/destinations.tsx index d9d17d538..8605a7c18 100644 --- a/apps/dokploy/pages/dashboard/settings/destinations.tsx +++ b/apps/dokploy/pages/dashboard/settings/destinations.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index 7bacde246..4187a0ef7 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/index.tsx b/apps/dokploy/pages/dashboard/settings/index.tsx index 2bfce2f2e..713e51133 100644 --- a/apps/dokploy/pages/dashboard/settings/index.tsx +++ b/apps/dokploy/pages/dashboard/settings/index.tsx @@ -17,7 +17,6 @@ import { FormField, FormItem, FormLabel, - FormMessage, } from "@/components/ui/form"; import { Switch } from "@/components/ui/switch"; import { appRouter } from "@/server/api/root"; @@ -27,7 +26,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { createServerSideHelpers } from "@trpc/react-query/server"; import { Settings } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; -import React, { useEffect, type ReactElement } from "react"; +import { type ReactElement, useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import superjson from "superjson"; diff --git a/apps/dokploy/pages/dashboard/settings/notifications.tsx b/apps/dokploy/pages/dashboard/settings/notifications.tsx index d0e0c65fe..76566fdf2 100644 --- a/apps/dokploy/pages/dashboard/settings/notifications.tsx +++ b/apps/dokploy/pages/dashboard/settings/notifications.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 79a3366d4..446e6c875 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -9,7 +9,7 @@ import { getLocale, serverSideTranslations } from "@/utils/i18n"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/registry.tsx b/apps/dokploy/pages/dashboard/settings/registry.tsx index 0a01e255f..678e0da46 100644 --- a/apps/dokploy/pages/dashboard/settings/registry.tsx +++ b/apps/dokploy/pages/dashboard/settings/registry.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index dbd049579..4f88c794b 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -1,4 +1,3 @@ -import { SetupMonitoring } from "@/components/dashboard/settings/servers/setup-monitoring"; import { WebDomain } from "@/components/dashboard/settings/web-domain"; import { WebServer } from "@/components/dashboard/settings/web-server"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; @@ -6,10 +5,8 @@ import { appRouter } from "@/server/api/root"; import { getLocale, serverSideTranslations } from "@/utils/i18n"; import { IS_CLOUD, validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; -import { LayoutDashboardIcon } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; -import { toast } from "sonner"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/servers.tsx b/apps/dokploy/pages/dashboard/settings/servers.tsx index 85ca5bb31..08d4ab694 100644 --- a/apps/dokploy/pages/dashboard/settings/servers.tsx +++ b/apps/dokploy/pages/dashboard/settings/servers.tsx @@ -6,7 +6,7 @@ import { getLocale, serverSideTranslations } from "@/utils/i18n"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index 8c5082e39..738c647d4 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -5,7 +5,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index e9fb65608..ac5355218 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -6,7 +6,7 @@ import { appRouter } from "@/server/api/root"; import { validateRequest } from "@dokploy/server"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Page = () => { diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 3153e80d3..90359ccd6 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -5,7 +5,7 @@ import { IS_CLOUD } from "@dokploy/server/constants"; import { validateRequest } from "@dokploy/server/lib/auth"; import { createServerSideHelpers } from "@trpc/react-query/server"; import type { GetServerSidePropsContext } from "next"; -import React, { type ReactElement } from "react"; +import type { ReactElement } from "react"; import superjson from "superjson"; const Dashboard = () => { diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 795d7a862..c910e78ec 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -1,8 +1,7 @@ -import { Login2FA } from "@/components/auth/login-2fa"; import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; -import { Button, buttonVariants } from "@/components/ui/button"; +import { Button } from "@/components/ui/button"; import { CardContent, CardDescription } from "@/components/ui/card"; import { Dialog, @@ -10,12 +9,10 @@ import { DialogDescription, DialogHeader, DialogTitle, - DialogTrigger, } from "@/components/ui/dialog"; import { Form, FormControl, - FormDescription, FormField, FormItem, FormLabel, @@ -29,19 +26,14 @@ import { } from "@/components/ui/input-otp"; import { Label } from "@/components/ui/label"; import { authClient } from "@/lib/auth-client"; -import { cn } from "@/lib/utils"; -import { api } from "@/utils/api"; -import { IS_CLOUD, auth, isAdminPresent } from "@dokploy/server"; +import { IS_CLOUD, isAdminPresent } from "@dokploy/server"; import { validateRequest } from "@dokploy/server/lib/auth"; import { zodResolver } from "@hookform/resolvers/zod"; -import base32 from "hi-base32"; import { REGEXP_ONLY_DIGITS } from "input-otp"; -import { AlertTriangle } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; -import { TOTP } from "otpauth"; -import { type ReactElement, useEffect, useState } from "react"; +import { type ReactElement, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx index c11cc8225..91ca1d0d7 100644 --- a/apps/dokploy/pages/invitation.tsx +++ b/apps/dokploy/pages/invitation.tsx @@ -2,12 +2,7 @@ import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardTitle, -} from "@/components/ui/card"; +import { CardContent, CardDescription, CardTitle } from "@/components/ui/card"; import { Form, FormControl, @@ -21,7 +16,7 @@ import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { IS_CLOUD, getUserByToken } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertCircle, AlertTriangle } from "lucide-react"; +import { AlertTriangle } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index e8fd15cf5..701d8f5b5 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -2,12 +2,7 @@ import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardTitle, -} from "@/components/ui/card"; +import { CardContent, CardDescription, CardTitle } from "@/components/ui/card"; import { Form, FormControl, @@ -27,7 +22,6 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { type ReactElement, useEffect } from "react"; import { useForm } from "react-hook-form"; -import { toast } from "sonner"; import { z } from "zod"; const registerSchema = z diff --git a/apps/dokploy/pages/send-reset-password.tsx b/apps/dokploy/pages/send-reset-password.tsx index c4cd851ce..ce73fbb82 100644 --- a/apps/dokploy/pages/send-reset-password.tsx +++ b/apps/dokploy/pages/send-reset-password.tsx @@ -3,12 +3,7 @@ import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardTitle, -} from "@/components/ui/card"; +import { CardContent, CardDescription, CardTitle } from "@/components/ui/card"; import { Form, FormControl, diff --git a/apps/dokploy/server/api/routers/admin.ts b/apps/dokploy/server/api/routers/admin.ts index 293b7dfe5..47bd9cd9c 100644 --- a/apps/dokploy/server/api/routers/admin.ts +++ b/apps/dokploy/server/api/routers/admin.ts @@ -1,119 +1,14 @@ -import { - apiAssignPermissions, - apiCreateUserInvitation, - apiFindOneToken, - apiRemoveUser, - apiUpdateWebServerMonitoring, -} from "@/server/db/schema"; +import { apiUpdateWebServerMonitoring } from "@/server/db/schema"; import { IS_CLOUD, - createInvitation, - findOrganizationById, findUserById, - getUserByToken, - removeUserById, setupWebMonitoring, updateUser, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; -import { z } from "zod"; -import { - adminProcedure, - createTRPCRouter, - protectedProcedure, - publicProcedure, -} from "../trpc"; +import { adminProcedure, createTRPCRouter } from "../trpc"; export const adminRouter = createTRPCRouter({ - one: adminProcedure.query(async ({ ctx }) => { - const { sshPrivateKey, ...rest } = await findUserById(ctx.user.id); - return { - haveSSH: !!sshPrivateKey, - ...rest, - }; - }), - update: adminProcedure - .input( - z.object({ - enableDockerCleanup: z.boolean(), - }), - ) - .mutation(async ({ input, ctx }) => { - if (ctx.user.rol === "member") { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to update this admin", - }); - } - const user = await findUserById(ctx.user.ownerId); - return updateUser(user.id, {}); - }), - createUserInvitation: adminProcedure - .input(apiCreateUserInvitation) - .mutation(async ({ input, ctx }) => { - try { - await createInvitation(input, ctx.user.id); - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: - "Error creating this user\ncheck if the email is not registered", - cause: error, - }); - } - }), - removeUser: adminProcedure - .input(apiRemoveUser) - .mutation(async ({ input, ctx }) => { - try { - const user = await findUserById(input.id); - - if (user.id !== ctx.user.ownerId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to delete this user", - }); - } - - return await removeUserById(input.id); - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error deleting this user", - cause: error, - }); - } - }), - getUserByToken: publicProcedure - .input(apiFindOneToken) - .query(async ({ input }) => { - return await getUserByToken(input.token); - }), - assignPermissions: adminProcedure - .input(apiAssignPermissions) - .mutation(async ({ input, ctx }) => { - try { - const user = await findUserById(input.id); - - const organization = await findOrganizationById( - ctx.session?.activeOrganizationId || "", - ); - - if (organization?.ownerId !== ctx.user.ownerId) { - throw new TRPCError({ - code: "UNAUTHORIZED", - message: "You are not allowed to assign permissions", - }); - } - - await updateUser(user.id, { - ...input, - }); - } catch (error) { - throw error; - } - }), - setupMonitoring: adminProcedure .input(apiUpdateWebServerMonitoring) .mutation(async ({ input, ctx }) => { @@ -163,129 +58,4 @@ export const adminRouter = createTRPCRouter({ throw error; } }), - getMetricsToken: protectedProcedure.query(async ({ ctx }) => { - const user = await findUserById(ctx.user.ownerId); - return { - serverIp: user.serverIp, - enabledFeatures: user.enablePaidFeatures, - metricsConfig: user?.metricsConfig, - }; - }), - - getServerMetrics: protectedProcedure - .input( - z.object({ - url: z.string(), - token: z.string(), - dataPoints: z.string(), - }), - ) - .query(async ({ ctx, input }) => { - try { - const url = new URL(input.url); - url.searchParams.append("limit", input.dataPoints); - const response = await fetch(url.toString(), { - headers: { - Authorization: `Bearer ${input.token}`, - }, - }); - if (!response.ok) { - throw new Error( - `Error ${response.status}: ${response.statusText}. Ensure the container is running and this service is included in the monitoring configuration.`, - ); - } - - const data = await response.json(); - if (!Array.isArray(data) || data.length === 0) { - throw new Error( - [ - "No monitoring data available. This could be because:", - "", - "1. You don't have setup the monitoring service, you can do in web server section.", - "2. If you already have setup the monitoring service, wait a few minutes and refresh the page.", - ].join("\n"), - ); - } - return data as { - cpu: string; - cpuModel: string; - cpuCores: number; - cpuPhysicalCores: number; - cpuSpeed: number; - os: string; - distro: string; - kernel: string; - arch: string; - memUsed: string; - memUsedGB: string; - memTotal: string; - uptime: number; - diskUsed: string; - totalDisk: string; - networkIn: string; - networkOut: string; - timestamp: string; - }[]; - } catch (error) { - throw error; - } - }), - getContainerMetrics: protectedProcedure - .input( - z.object({ - url: z.string(), - token: z.string(), - appName: z.string(), - dataPoints: z.string(), - }), - ) - .query(async ({ ctx, input }) => { - try { - if (!input.appName) { - throw new Error( - [ - "No Application Selected:", - "", - "Make Sure to select an application to monitor.", - ].join("\n"), - ); - } - const url = new URL(`${input.url}/metrics/containers`); - url.searchParams.append("limit", input.dataPoints); - url.searchParams.append("appName", input.appName); - const response = await fetch(url.toString(), { - headers: { - Authorization: `Bearer ${input.token}`, - }, - }); - if (!response.ok) { - throw new Error( - `Error ${response.status}: ${response.statusText}. Please verify that the application "${input.appName}" is running and this service is included in the monitoring configuration.`, - ); - } - - const data = await response.json(); - if (!Array.isArray(data) || data.length === 0) { - throw new Error( - [ - `No monitoring data available for "${input.appName}". This could be because:`, - "", - "1. The container was recently started - wait a few minutes for data to be collected", - "2. The container is not running - verify its status", - "3. The service is not included in your monitoring configuration", - ].join("\n"), - ); - } - return data as { - containerId: string; - containerName: string; - containerImage: string; - containerLabels: string; - containerCommand: string; - containerCreated: string; - }[]; - } catch (error) { - throw error; - } - }), }); diff --git a/apps/dokploy/server/api/routers/application.ts b/apps/dokploy/server/api/routers/application.ts index 269ac77b5..e1629b4cb 100644 --- a/apps/dokploy/server/api/routers/application.ts +++ b/apps/dokploy/server/api/routers/application.ts @@ -61,7 +61,12 @@ export const applicationRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -103,7 +108,12 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.applicationId, "access"); + await checkServiceAccess( + ctx.user.id, + input.applicationId, + ctx.session.activeOrganizationId, + "access", + ); } const application = await findApplicationById(input.applicationId); if ( @@ -149,7 +159,12 @@ export const applicationRouter = createTRPCRouter({ .input(apiFindOneApplication) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.applicationId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.applicationId, + ctx.session.activeOrganizationId, + "delete", + ); } const application = await findApplicationById(input.applicationId); @@ -186,7 +201,7 @@ export const applicationRouter = createTRPCRouter({ for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_) {} } return result[0]; @@ -642,7 +657,7 @@ export const applicationRouter = createTRPCRouter({ }), readAppMonitoring: protectedProcedure .input(apiFindMonitoringStats) - .query(async ({ input, ctx }) => { + .query(async ({ input }) => { if (IS_CLOUD) { throw new TRPCError({ code: "UNAUTHORIZED", diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 4cfbe71a2..da3121b2e 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -12,14 +12,11 @@ import { import { WEBSITE_URL } from "@/server/utils/stripe"; import { IS_CLOUD, - findAuthById, findUserById, - generate2FASecret, getUserByToken, sendDiscordNotification, sendEmailNotification, validateRequest, - verify2FA, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; @@ -273,42 +270,6 @@ export const authRouter = createTRPCRouter({ const user = await findUserById(input.userId); return user; }), - - generate2FASecret: protectedProcedure.query(async ({ ctx }) => { - return await generate2FASecret(ctx.user.id); - }), - verify2FASetup: protectedProcedure.mutation(async ({ ctx, input }) => { - // const auth = await findAuthById(ctx.user.authId); - // await verify2FA(auth, input.secret, input.pin); - // await updateAuthById(auth.id, { - // is2FAEnabled: true, - // secret: input.secret, - // }); - // return auth; - }), - - verifyLogin2FA: publicProcedure.mutation(async ({ ctx, input }) => { - // const auth = await findAuthById(input.id); - - // await verify2FA(auth, auth.secret || "", input.pin); - - // const session = await lucia.createSession(auth.id, {}); - - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); - - return true; - }), - disable2FA: protectedProcedure.mutation(async ({ ctx }) => { - // const auth = await findAuthById(ctx.user.authId); - // await updateAuthById(auth.id, { - // is2FAEnabled: false, - // secret: null, - // }); - // return auth; - }), sendResetPasswordEmail: publicProcedure .input( z.object({ diff --git a/apps/dokploy/server/api/routers/backup.ts b/apps/dokploy/server/api/routers/backup.ts index 0b8d7ab15..8a7a5f22f 100644 --- a/apps/dokploy/server/api/routers/backup.ts +++ b/apps/dokploy/server/api/routers/backup.ts @@ -30,7 +30,7 @@ import { TRPCError } from "@trpc/server"; export const backupRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateBackup) - .mutation(async ({ input, ctx }) => { + .mutation(async ({ input }) => { try { const newBackup = await createBackup(input); @@ -74,16 +74,14 @@ export const backupRouter = createTRPCRouter({ }); } }), - one: protectedProcedure - .input(apiFindOneBackup) - .query(async ({ input, ctx }) => { - const backup = await findBackupById(input.backupId); + one: protectedProcedure.input(apiFindOneBackup).query(async ({ input }) => { + const backup = await findBackupById(input.backupId); - return backup; - }), + return backup; + }), update: protectedProcedure .input(apiUpdateBackup) - .mutation(async ({ input, ctx }) => { + .mutation(async ({ input }) => { try { await updateBackupById(input.backupId, input); const backup = await findBackupById(input.backupId); @@ -111,15 +109,17 @@ export const backupRouter = createTRPCRouter({ } } } catch (error) { + const message = + error instanceof Error ? error.message : "Error updating this Backup"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error updating this Backup", + message, }); } }), remove: protectedProcedure .input(apiRemoveBackup) - .mutation(async ({ input, ctx }) => { + .mutation(async ({ input }) => { try { const value = await removeBackupById(input.backupId); if (IS_CLOUD && value) { @@ -133,10 +133,11 @@ export const backupRouter = createTRPCRouter({ } return value; } catch (error) { + const message = + error instanceof Error ? error.message : "Error deleting this Backup"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error deleting this Backup", - cause: error, + message, }); } }), @@ -149,11 +150,13 @@ export const backupRouter = createTRPCRouter({ await runPostgresBackup(postgres, backup); return true; } catch (error) { - console.log(error); + const message = + error instanceof Error + ? error.message + : "Error running manual Postgres backup "; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error running manual Postgres backup ", - cause: error, + message, }); } }), diff --git a/apps/dokploy/server/api/routers/cluster.ts b/apps/dokploy/server/api/routers/cluster.ts index 7ded632c6..0d8407576 100644 --- a/apps/dokploy/server/api/routers/cluster.ts +++ b/apps/dokploy/server/api/routers/cluster.ts @@ -40,7 +40,7 @@ export const clusterRouter = createTRPCRouter({ }); } }), - addWorker: protectedProcedure.query(async ({ input }) => { + addWorker: protectedProcedure.query(async () => { if (IS_CLOUD) { return { command: "", @@ -57,7 +57,7 @@ export const clusterRouter = createTRPCRouter({ version: docker_version.Version, }; }), - addManager: protectedProcedure.query(async ({ input }) => { + addManager: protectedProcedure.query(async () => { if (IS_CLOUD) { return { command: "", diff --git a/apps/dokploy/server/api/routers/compose.ts b/apps/dokploy/server/api/routers/compose.ts index 258a03d4b..bae926d05 100644 --- a/apps/dokploy/server/api/routers/compose.ts +++ b/apps/dokploy/server/api/routers/compose.ts @@ -61,7 +61,12 @@ export const composeRouter = createTRPCRouter({ .mutation(async ({ ctx, input }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -97,7 +102,12 @@ export const composeRouter = createTRPCRouter({ .input(apiFindCompose) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.composeId, "access"); + await checkServiceAccess( + ctx.user.id, + input.composeId, + ctx.session.activeOrganizationId, + "access", + ); } const compose = await findComposeById(input.composeId); @@ -126,7 +136,12 @@ export const composeRouter = createTRPCRouter({ .input(apiDeleteCompose) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.composeId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.composeId, + ctx.session.activeOrganizationId, + "delete", + ); } const composeResult = await findComposeById(input.composeId); @@ -155,7 +170,7 @@ export const composeRouter = createTRPCRouter({ for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_) {} } return result[0]; @@ -385,7 +400,12 @@ export const composeRouter = createTRPCRouter({ .input(apiCreateComposeByTemplate) .mutation(async ({ ctx, input }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -476,7 +496,7 @@ export const composeRouter = createTRPCRouter({ return templatesData; }), - getTags: protectedProcedure.query(async ({ input }) => { + getTags: protectedProcedure.query(async () => { const allTags = templates.flatMap((template) => template.tags); const uniqueTags = _.uniq(allTags); return uniqueTags; diff --git a/apps/dokploy/server/api/routers/git-provider.ts b/apps/dokploy/server/api/routers/git-provider.ts index 39194ed31..ed37869d5 100644 --- a/apps/dokploy/server/api/routers/git-provider.ts +++ b/apps/dokploy/server/api/routers/git-provider.ts @@ -31,9 +31,13 @@ export const gitProviderRouter = createTRPCRouter({ } return await removeGitProvider(input.gitProviderId); } catch (error) { + const message = + error instanceof Error + ? error.message + : "Error deleting this Git provider"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error deleting this Git provider", + message, }); } }), diff --git a/apps/dokploy/server/api/routers/mariadb.ts b/apps/dokploy/server/api/routers/mariadb.ts index 5735620e7..be0ffd39a 100644 --- a/apps/dokploy/server/api/routers/mariadb.ts +++ b/apps/dokploy/server/api/routers/mariadb.ts @@ -20,7 +20,6 @@ import { findBackupsByDbId, findMariadbById, findProjectById, - findServerById, removeMariadbById, removeService, startService, @@ -38,7 +37,12 @@ export const mariadbRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -84,7 +88,12 @@ export const mariadbRouter = createTRPCRouter({ .input(apiFindOneMariaDB) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.mariadbId, "access"); + await checkServiceAccess( + ctx.user.id, + input.mariadbId, + ctx.session.activeOrganizationId, + "access", + ); } const mariadb = await findMariadbById(input.mariadbId); if (mariadb.project.organizationId !== ctx.session.activeOrganizationId) { @@ -206,7 +215,12 @@ export const mariadbRouter = createTRPCRouter({ .input(apiFindOneMariaDB) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.mariadbId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.mariadbId, + ctx.session.activeOrganizationId, + "delete", + ); } const mongo = await findMariadbById(input.mariadbId); @@ -227,7 +241,7 @@ export const mariadbRouter = createTRPCRouter({ for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_) {} } return mongo; diff --git a/apps/dokploy/server/api/routers/mongo.ts b/apps/dokploy/server/api/routers/mongo.ts index 7f8716a59..1c3ba6bb7 100644 --- a/apps/dokploy/server/api/routers/mongo.ts +++ b/apps/dokploy/server/api/routers/mongo.ts @@ -37,7 +37,12 @@ export const mongoRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -87,7 +92,12 @@ export const mongoRouter = createTRPCRouter({ .input(apiFindOneMongo) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.mongoId, "access"); + await checkServiceAccess( + ctx.user.id, + input.mongoId, + ctx.session.activeOrganizationId, + "access", + ); } const mongo = await findMongoById(input.mongoId); @@ -247,7 +257,12 @@ export const mongoRouter = createTRPCRouter({ .input(apiFindOneMongo) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.mongoId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.mongoId, + ctx.session.activeOrganizationId, + "delete", + ); } const mongo = await findMongoById(input.mongoId); @@ -269,7 +284,7 @@ export const mongoRouter = createTRPCRouter({ for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_) {} } return mongo; diff --git a/apps/dokploy/server/api/routers/mysql.ts b/apps/dokploy/server/api/routers/mysql.ts index 96ea4846f..594403f24 100644 --- a/apps/dokploy/server/api/routers/mysql.ts +++ b/apps/dokploy/server/api/routers/mysql.ts @@ -39,7 +39,12 @@ export const mysqlRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -90,7 +95,12 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.mysqlId, "access"); + await checkServiceAccess( + ctx.user.id, + input.mysqlId, + ctx.session.activeOrganizationId, + "access", + ); } const mysql = await findMySqlById(input.mysqlId); if (mysql.project.organizationId !== ctx.session.activeOrganizationId) { @@ -245,7 +255,12 @@ export const mysqlRouter = createTRPCRouter({ .input(apiFindOneMySql) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.mysqlId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.mysqlId, + ctx.session.activeOrganizationId, + "delete", + ); } const mongo = await findMySqlById(input.mysqlId); if (mongo.project.organizationId !== ctx.session.activeOrganizationId) { @@ -265,7 +280,7 @@ export const mysqlRouter = createTRPCRouter({ for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_) {} } return mongo; diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 6a893d363..48ef50b9d 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -297,9 +297,13 @@ export const notificationRouter = createTRPCRouter({ } return await removeNotificationById(input.notificationId); } catch (error) { + const message = + error instanceof Error + ? error.message + : "Error deleting this notification"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error deleting this notification", + message, }); } }), diff --git a/apps/dokploy/server/api/routers/organization.ts b/apps/dokploy/server/api/routers/organization.ts index ad77b85cc..b98fc26c4 100644 --- a/apps/dokploy/server/api/routers/organization.ts +++ b/apps/dokploy/server/api/routers/organization.ts @@ -71,7 +71,7 @@ export const organizationRouter = createTRPCRouter({ organizationId: z.string(), }), ) - .query(async ({ ctx, input }) => { + .query(async ({ input }) => { return await db.query.organization.findFirst({ where: eq(organization.id, input.organizationId), }); @@ -140,12 +140,4 @@ export const organizationRouter = createTRPCRouter({ orderBy: [desc(invitation.status), desc(invitation.expiresAt)], }); }), - acceptInvitation: adminProcedure - .input(z.object({ invitationId: z.string() })) - .mutation(async ({ ctx, input }) => { - // const result = await auth.api.acceptInvitation({ - // invitationId: input.invitationId, - // }); - // return result; - }), }); diff --git a/apps/dokploy/server/api/routers/port.ts b/apps/dokploy/server/api/routers/port.ts index bfbc98633..923fea573 100644 --- a/apps/dokploy/server/api/routers/port.ts +++ b/apps/dokploy/server/api/routers/port.ts @@ -44,9 +44,11 @@ export const portRouter = createTRPCRouter({ try { return removePortById(input.portId); } catch (error) { + const message = + error instanceof Error ? error.message : "Error input: Deleting port"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error input: Deleting port", + message, }); } }), @@ -56,9 +58,11 @@ export const portRouter = createTRPCRouter({ try { return updatePortById(input.portId, input); } catch (error) { + const message = + error instanceof Error ? error.message : "Error updating the port"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error updating the port", + message, }); } }), diff --git a/apps/dokploy/server/api/routers/postgres.ts b/apps/dokploy/server/api/routers/postgres.ts index aa3a0459d..cf3221b48 100644 --- a/apps/dokploy/server/api/routers/postgres.ts +++ b/apps/dokploy/server/api/routers/postgres.ts @@ -1,9 +1,4 @@ -import { EventEmitter } from "node:events"; -import { - createTRPCRouter, - protectedProcedure, - publicProcedure, -} from "@/server/api/trpc"; +import { createTRPCRouter, protectedProcedure } from "@/server/api/trpc"; import { apiChangePostgresStatus, apiCreatePostgres, @@ -35,9 +30,6 @@ import { } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; -import { z } from "zod"; - -const ee = new EventEmitter(); export const postgresRouter = createTRPCRouter({ create: protectedProcedure @@ -45,7 +37,12 @@ export const postgresRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -95,7 +92,12 @@ export const postgresRouter = createTRPCRouter({ .input(apiFindOnePostgres) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.postgresId, "access"); + await checkServiceAccess( + ctx.user.id, + input.postgresId, + ctx.session.activeOrganizationId, + "access", + ); } const postgres = await findPostgresById(input.postgresId); @@ -238,7 +240,12 @@ export const postgresRouter = createTRPCRouter({ .input(apiFindOnePostgres) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.postgresId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.postgresId, + ctx.session.activeOrganizationId, + "delete", + ); } const postgres = await findPostgresById(input.postgresId); diff --git a/apps/dokploy/server/api/routers/project.ts b/apps/dokploy/server/api/routers/project.ts index 68b068bc3..438a3f077 100644 --- a/apps/dokploy/server/api/routers/project.ts +++ b/apps/dokploy/server/api/routers/project.ts @@ -8,7 +8,6 @@ import { applications, compose, mariadb, - member, mongo, mysql, postgres, @@ -16,22 +15,20 @@ import { redis, } from "@/server/db/schema"; -import { TRPCError } from "@trpc/server"; -import { and, desc, eq, sql } from "drizzle-orm"; -import type { AnyPgColumn } from "drizzle-orm/pg-core"; - import { IS_CLOUD, addNewProject, checkProjectAccess, createProject, deleteProject, + findMemberById, findProjectById, - findUserByAuthId, findUserById, updateProjectById, - findMemberById, } from "@dokploy/server"; +import { TRPCError } from "@trpc/server"; +import { and, desc, eq, sql } from "drizzle-orm"; +import type { AnyPgColumn } from "drizzle-orm/pg-core"; export const projectRouter = createTRPCRouter({ create: protectedProcedure .input(apiCreateProject) diff --git a/apps/dokploy/server/api/routers/redis.ts b/apps/dokploy/server/api/routers/redis.ts index 6d5a84d5e..a80660bf5 100644 --- a/apps/dokploy/server/api/routers/redis.ts +++ b/apps/dokploy/server/api/routers/redis.ts @@ -37,7 +37,12 @@ export const redisRouter = createTRPCRouter({ .mutation(async ({ input, ctx }) => { try { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.projectId, "create"); + await checkServiceAccess( + ctx.user.id, + input.projectId, + ctx.session.activeOrganizationId, + "create", + ); } if (IS_CLOUD && !input.serverId) { @@ -80,7 +85,12 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.redisId, "access"); + await checkServiceAccess( + ctx.user.id, + input.redisId, + ctx.session.activeOrganizationId, + "access", + ); } const redis = await findRedisById(input.redisId); @@ -237,7 +247,12 @@ export const redisRouter = createTRPCRouter({ .input(apiFindOneRedis) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - await checkServiceAccess(ctx.user.id, input.redisId, "delete"); + await checkServiceAccess( + ctx.user.id, + input.redisId, + ctx.session.activeOrganizationId, + "delete", + ); } const redis = await findRedisById(input.redisId); @@ -256,7 +271,7 @@ export const redisRouter = createTRPCRouter({ for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_) {} } return redis; diff --git a/apps/dokploy/server/api/routers/registry.ts b/apps/dokploy/server/api/routers/registry.ts index 62c8a9b65..a9a6be891 100644 --- a/apps/dokploy/server/api/routers/registry.ts +++ b/apps/dokploy/server/api/routers/registry.ts @@ -1,3 +1,4 @@ +import { db } from "@/server/db"; import { apiCreateRegistry, apiFindOneRegistry, @@ -18,7 +19,6 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { adminProcedure, createTRPCRouter, protectedProcedure } from "../trpc"; -import { db } from "@/server/db"; export const registryRouter = createTRPCRouter({ create: adminProcedure .input(apiCreateRegistry) diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 1d7fd40e8..3215226e3 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -79,7 +79,7 @@ export const serverRouter = createTRPCRouter({ }), getDefaultCommand: protectedProcedure .input(apiFindOneServer) - .query(async ({ input, ctx }) => { + .query(async () => { return defaultCommand(); }), all: protectedProcedure.query(async ({ ctx }) => { @@ -358,7 +358,7 @@ export const serverRouter = createTRPCRouter({ throw error; } }), - publicIp: protectedProcedure.query(async ({ ctx }) => { + publicIp: protectedProcedure.query(async () => { if (IS_CLOUD) { return ""; } diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index ee69da22d..d2455fdb7 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -377,7 +377,10 @@ export const settingsRouter = createTRPCRouter({ .query(async ({ ctx, input }) => { try { if (ctx.user.rol === "member") { - const canAccess = await canAccessToTraefikFiles(ctx.user.id); + const canAccess = await canAccessToTraefikFiles( + ctx.user.id, + ctx.session.activeOrganizationId, + ); if (!canAccess) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -395,7 +398,10 @@ export const settingsRouter = createTRPCRouter({ .input(apiModifyTraefikConfig) .mutation(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - const canAccess = await canAccessToTraefikFiles(ctx.user.id); + const canAccess = await canAccessToTraefikFiles( + ctx.user.id, + ctx.session.activeOrganizationId, + ); if (!canAccess) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -413,7 +419,10 @@ export const settingsRouter = createTRPCRouter({ .input(apiReadTraefikConfig) .query(async ({ input, ctx }) => { if (ctx.user.rol === "member") { - const canAccess = await canAccessToTraefikFiles(ctx.user.id); + const canAccess = await canAccessToTraefikFiles( + ctx.user.id, + ctx.session.activeOrganizationId, + ); if (!canAccess) { throw new TRPCError({ code: "UNAUTHORIZED" }); @@ -708,7 +717,12 @@ export const settingsRouter = createTRPCRouter({ try { return await checkGPUStatus(input.serverId || ""); } catch (error) { - throw new Error("Failed to check GPU status"); + const message = + error instanceof Error ? error.message : "Failed to check GPU status"; + throw new TRPCError({ + code: "BAD_REQUEST", + message, + }); } }), updateTraefikPorts: adminProcedure diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index d55a23794..fe321de4f 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -9,7 +9,6 @@ import { sshKeys, } from "@/server/db/schema"; import { - IS_CLOUD, createSshKey, findSSHKeyById, generateSSHKey, diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts index 540820f25..0e0e07a47 100644 --- a/apps/dokploy/server/api/routers/stripe.ts +++ b/apps/dokploy/server/api/routers/stripe.ts @@ -87,36 +87,34 @@ export const stripeRouter = createTRPCRouter({ return { sessionId: session.id }; }), - createCustomerPortalSession: adminProcedure.mutation( - async ({ ctx, input }) => { - const user = await findUserById(ctx.user.ownerId); + createCustomerPortalSession: adminProcedure.mutation(async ({ ctx }) => { + const user = await findUserById(ctx.user.ownerId); - if (!user.stripeCustomerId) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Stripe Customer ID not found", - }); - } - const stripeCustomerId = user.stripeCustomerId; + if (!user.stripeCustomerId) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Stripe Customer ID not found", + }); + } + const stripeCustomerId = user.stripeCustomerId; - const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { - apiVersion: "2024-09-30.acacia", + const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { + apiVersion: "2024-09-30.acacia", + }); + + try { + const session = await stripe.billingPortal.sessions.create({ + customer: stripeCustomerId, + return_url: `${WEBSITE_URL}/dashboard/settings/billing`, }); - try { - const session = await stripe.billingPortal.sessions.create({ - customer: stripeCustomerId, - return_url: `${WEBSITE_URL}/dashboard/settings/billing`, - }); - - return { url: session.url }; - } catch (error) { - return { - url: "", - }; - } - }, - ), + return { url: session.url }; + } catch (_) { + return { + url: "", + }; + } + }), canCreateMoreServers: adminProcedure.query(async ({ ctx }) => { const user = await findUserById(ctx.user.ownerId); diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 5c4eb56d5..43cffd9e1 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -1,17 +1,13 @@ -import { apiFindOneUser, apiFindOneUserByAuth } from "@/server/db/schema"; import { IS_CLOUD, findOrganizationById, - findUserByAuthId, findUserById, getUserByToken, removeUserById, updateUser, - verify2FA, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { - account, apiAssignPermissions, apiFindOneToken, apiUpdateUser, @@ -19,7 +15,7 @@ import { member, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; -import { and, asc, desc, eq, gt } from "drizzle-orm"; +import { and, asc, eq, gt } from "drizzle-orm"; import { z } from "zod"; import { adminProcedure, @@ -93,7 +89,7 @@ export const userRouter = createTRPCRouter({ userId: z.string(), }), ) - .mutation(async ({ input, ctx }) => { + .mutation(async ({ input }) => { if (IS_CLOUD) { return true; } @@ -103,8 +99,6 @@ export const userRouter = createTRPCRouter({ .input(apiAssignPermissions) .mutation(async ({ input, ctx }) => { try { - const user = await findUserById(input.id); - const organization = await findOrganizationById( ctx.session?.activeOrganizationId || "", ); diff --git a/apps/dokploy/server/db/seed.ts b/apps/dokploy/server/db/seed.ts index b79350797..3216a44b4 100644 --- a/apps/dokploy/server/db/seed.ts +++ b/apps/dokploy/server/db/seed.ts @@ -1,7 +1,6 @@ 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!; diff --git a/apps/dokploy/server/utils/backup.ts b/apps/dokploy/server/utils/backup.ts index 2f1419719..4fc9db931 100644 --- a/apps/dokploy/server/utils/backup.ts +++ b/apps/dokploy/server/utils/backup.ts @@ -2,7 +2,6 @@ import { type BackupScheduleList, IS_CLOUD, removeScheduleBackup, - scheduleBackup, } from "@dokploy/server/index"; type QueueJob = diff --git a/apps/dokploy/templates/excalidraw/index.ts b/apps/dokploy/templates/excalidraw/index.ts index 13a43c440..7f73f395f 100644 --- a/apps/dokploy/templates/excalidraw/index.ts +++ b/apps/dokploy/templates/excalidraw/index.ts @@ -2,7 +2,6 @@ import { type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; diff --git a/apps/dokploy/templates/ghost/index.ts b/apps/dokploy/templates/ghost/index.ts index 1a88c3629..052b7c6bb 100644 --- a/apps/dokploy/templates/ghost/index.ts +++ b/apps/dokploy/templates/ghost/index.ts @@ -2,7 +2,6 @@ import { type DomainSchema, type Schema, type Template, - generateHash, generateRandomDomain, } from "../utils"; diff --git a/apps/dokploy/templates/penpot/index.ts b/apps/dokploy/templates/penpot/index.ts index f657c698f..a3e90e8ae 100644 --- a/apps/dokploy/templates/penpot/index.ts +++ b/apps/dokploy/templates/penpot/index.ts @@ -2,8 +2,6 @@ import { type DomainSchema, type Schema, type Template, - generateBase64, - generatePassword, generateRandomDomain, } from "../utils"; diff --git a/apps/dokploy/templates/photoprism/index.ts b/apps/dokploy/templates/photoprism/index.ts index d20ac29c8..4a103a624 100644 --- a/apps/dokploy/templates/photoprism/index.ts +++ b/apps/dokploy/templates/photoprism/index.ts @@ -2,7 +2,6 @@ import { type DomainSchema, type Schema, type Template, - generateHash, generatePassword, generateRandomDomain, } from "../utils"; diff --git a/apps/dokploy/templates/triggerdotdev/index.ts b/apps/dokploy/templates/triggerdotdev/index.ts index 7b894acba..c11c708b5 100644 --- a/apps/dokploy/templates/triggerdotdev/index.ts +++ b/apps/dokploy/templates/triggerdotdev/index.ts @@ -1,4 +1,3 @@ -import { Secrets } from "@/components/ui/secrets"; import { type DomainSchema, type Schema, diff --git a/apps/dokploy/templates/unsend/index.ts b/apps/dokploy/templates/unsend/index.ts index 1c4c9c715..dcc80f66e 100644 --- a/apps/dokploy/templates/unsend/index.ts +++ b/apps/dokploy/templates/unsend/index.ts @@ -3,7 +3,6 @@ import { type Schema, type Template, generateBase64, - generateHash, generateRandomDomain, } from "../utils"; diff --git a/biome.json b/biome.json index f5a6c2328..cf677ec40 100644 --- a/biome.json +++ b/biome.json @@ -24,7 +24,10 @@ }, "correctness": { "useExhaustiveDependencies": "off", - "noUnsafeOptionalChaining": "off" + "noUnsafeOptionalChaining": "off", + "noUnusedImports": "error", + "noUnusedFunctionParameters": "error", + "noUnusedVariables": "error" }, "style": { "noNonNullAssertion": "off" diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index de0f4bbb4..a7be9b051 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,10 +1,4 @@ -import { - pgTable, - text, - integer, - timestamp, - boolean, -} from "drizzle-orm/pg-core"; +import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; export const users_temp = pgTable("users_temp", { id: text("id").primaryKey(), diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 67a247414..2f3761fec 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -1,4 +1,4 @@ -import { relations, sql } from "drizzle-orm"; +import { relations } from "drizzle-orm"; import { boolean, integer, diff --git a/packages/server/src/emails/emails/build-failed.tsx b/packages/server/src/emails/emails/build-failed.tsx index b3d999192..79e7b718d 100644 --- a/packages/server/src/emails/emails/build-failed.tsx +++ b/packages/server/src/emails/emails/build-failed.tsx @@ -12,7 +12,6 @@ import { Tailwind, Text, } from "@react-email/components"; -import * as React from "react"; export type TemplateProps = { projectName: string; diff --git a/packages/server/src/emails/emails/build-success.tsx b/packages/server/src/emails/emails/build-success.tsx index eadf7c44b..d9e500ab9 100644 --- a/packages/server/src/emails/emails/build-success.tsx +++ b/packages/server/src/emails/emails/build-success.tsx @@ -12,7 +12,6 @@ import { Tailwind, Text, } from "@react-email/components"; -import * as React from "react"; export type TemplateProps = { projectName: string; diff --git a/packages/server/src/emails/emails/database-backup.tsx b/packages/server/src/emails/emails/database-backup.tsx index 2bdf944c3..754d4d982 100644 --- a/packages/server/src/emails/emails/database-backup.tsx +++ b/packages/server/src/emails/emails/database-backup.tsx @@ -10,7 +10,6 @@ import { Tailwind, Text, } from "@react-email/components"; -import * as React from "react"; export type TemplateProps = { projectName: string; diff --git a/packages/server/src/emails/emails/docker-cleanup.tsx b/packages/server/src/emails/emails/docker-cleanup.tsx index 05d93ed75..985406ae0 100644 --- a/packages/server/src/emails/emails/docker-cleanup.tsx +++ b/packages/server/src/emails/emails/docker-cleanup.tsx @@ -1,6 +1,5 @@ import { Body, - Button, Container, Head, Heading, @@ -11,7 +10,6 @@ import { Tailwind, Text, } from "@react-email/components"; -import * as React from "react"; export type TemplateProps = { message: string; diff --git a/packages/server/src/emails/emails/dokploy-restart.tsx b/packages/server/src/emails/emails/dokploy-restart.tsx index 1ad3d6004..db4edd69c 100644 --- a/packages/server/src/emails/emails/dokploy-restart.tsx +++ b/packages/server/src/emails/emails/dokploy-restart.tsx @@ -10,7 +10,6 @@ import { Tailwind, Text, } from "@react-email/components"; -import * as React from "react"; export type TemplateProps = { date: string; diff --git a/packages/server/src/emails/emails/notion-magic-link.tsx b/packages/server/src/emails/emails/notion-magic-link.tsx index b2286c340..f4071ce00 100644 --- a/packages/server/src/emails/emails/notion-magic-link.tsx +++ b/packages/server/src/emails/emails/notion-magic-link.tsx @@ -9,7 +9,6 @@ import { Preview, Text, } from "@react-email/components"; -import * as React from "react"; interface NotionMagicLinkEmailProps { loginCode?: string; diff --git a/packages/server/src/emails/emails/plaid-verify-identity.tsx b/packages/server/src/emails/emails/plaid-verify-identity.tsx index 650ab4866..88cf893d7 100644 --- a/packages/server/src/emails/emails/plaid-verify-identity.tsx +++ b/packages/server/src/emails/emails/plaid-verify-identity.tsx @@ -9,7 +9,6 @@ import { Section, Text, } from "@react-email/components"; -import * as React from "react"; interface PlaidVerifyIdentityEmailProps { validationCode?: string; diff --git a/packages/server/src/emails/emails/stripe-welcome.tsx b/packages/server/src/emails/emails/stripe-welcome.tsx index 9377853be..dbf02ea0e 100644 --- a/packages/server/src/emails/emails/stripe-welcome.tsx +++ b/packages/server/src/emails/emails/stripe-welcome.tsx @@ -11,7 +11,6 @@ import { Section, Text, } from "@react-email/components"; -import * as React from "react"; const baseUrl = process.env.VERCEL_URL!; diff --git a/packages/server/src/emails/emails/vercel-invite-user.tsx b/packages/server/src/emails/emails/vercel-invite-user.tsx index 53b31987c..79f50cd71 100644 --- a/packages/server/src/emails/emails/vercel-invite-user.tsx +++ b/packages/server/src/emails/emails/vercel-invite-user.tsx @@ -15,7 +15,6 @@ import { Tailwind, Text, } from "@react-email/components"; -import * as React from "react"; interface VercelInviteUserEmailProps { username?: string; diff --git a/packages/server/src/index.ts b/packages/server/src/index.ts index 554b4c124..f74b8d9d0 100644 --- a/packages/server/src/index.ts +++ b/packages/server/src/index.ts @@ -1,5 +1,4 @@ export * from "./auth/random-password"; -// export * from "./db"; export * from "./services/admin"; export * from "./services/user"; export * from "./services/project"; @@ -28,7 +27,6 @@ export * from "./services/ssh-key"; export * from "./services/git-provider"; export * from "./services/bitbucket"; export * from "./services/github"; -export * from "./services/auth"; export * from "./services/gitlab"; export * from "./services/server"; export * from "./services/application"; diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 368b43c99..a8d75637b 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -10,7 +10,6 @@ import { import { and, desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; -import { ac } from "./permissions"; export const auth = betterAuth({ database: drizzleAdapter(db, { diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 07c537dea..1e2b569f4 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -1,7 +1,5 @@ -import { randomBytes } from "node:crypto"; import { db } from "@dokploy/server/db"; import { - account, type apiCreateUserInvitation, invitation, member, @@ -9,7 +7,6 @@ import { users_temp, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; -import * as bcrypt from "bcrypt"; import { eq } from "drizzle-orm"; import { IS_CLOUD } from "../constants"; diff --git a/packages/server/src/services/application.ts b/packages/server/src/services/application.ts index ddc3a4504..425a6adbc 100644 --- a/packages/server/src/services/application.ts +++ b/packages/server/src/services/application.ts @@ -4,7 +4,6 @@ import { type apiCreateApplication, applications, buildAppName, - cleanAppName, } from "@dokploy/server/db/schema"; import { getAdvancedStats } from "@dokploy/server/monitoring/utils"; import { @@ -28,7 +27,6 @@ import { getCustomGitCloneCommand, } from "@dokploy/server/utils/providers/git"; import { - authGithub, cloneGithubRepository, getGithubCloneCommand, } from "@dokploy/server/utils/providers/github"; @@ -40,7 +38,7 @@ import { createTraefikConfig } from "@dokploy/server/utils/traefik/application"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; -import { findAdminById, findUserById, getDokployUrl } from "./admin"; +import { getDokployUrl } from "./admin"; import { createDeployment, createDeploymentPreview, @@ -58,7 +56,6 @@ import { updatePreviewDeployment, } from "./preview-deployment"; import { validUniqueServerAppName } from "./project"; -import { cleanupFullDocker } from "./settings"; export type Application = typeof applications.$inferSelect; export const createApplication = async ( diff --git a/packages/server/src/services/auth.ts b/packages/server/src/services/auth.ts deleted file mode 100644 index 5a7484eb9..000000000 --- a/packages/server/src/services/auth.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { randomBytes } from "node:crypto"; -import { createOTP } from "@better-auth/utils/otp"; -import { db } from "@dokploy/server/db"; -import { users_temp } from "@dokploy/server/db/schema"; -import { getPublicIpWithFallback } from "@dokploy/server/wss/utils"; -import { TRPCError } from "@trpc/server"; -import * as bcrypt from "bcrypt"; -import { eq } from "drizzle-orm"; -import encode from "hi-base32"; -import { TOTP } from "otpauth"; -import QRCode from "qrcode"; -import { IS_CLOUD } from "../constants"; -import { findUserById } from "./admin"; -import type { User } from "./user"; - -export const findAuthById = async (authId: string) => { - const result = await db.query.users_temp.findFirst({ - where: eq(users_temp.id, authId), - columns: { - createdAt: false, - updatedAt: false, - }, - }); - if (!result) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Auth not found", - }); - } - return result; -}; - -const generateBase32Secret = () => { - // Generamos 32 bytes (256 bits) para asegurar que tengamos suficiente longitud - const buffer = randomBytes(32); - // Convertimos directamente a hex para Better Auth - const hex = buffer.toString("hex"); - // También necesitamos la versión base32 para el QR code - const base32 = encode.encode(buffer).replace(/=/g, "").substring(0, 32); - return { - hex, - base32, - }; -}; - -export const generate2FASecret = () => { - const secret = "46JMUCG4NJ3CIU6LQAIVFWUW"; - - const totp = new TOTP({ - issuer: "Dokploy", - label: "siumauricio@hotmail.com", - algorithm: "SHA1", - digits: 6, - secret: secret, - }); - - // Convertir los bytes del secreto a hex - const secretBytes = totp.secret.bytes; - const hexSecret = Buffer.from(secretBytes).toString("hex"); - - console.log("Secret bytes:", secretBytes); - console.log("Hex secret:", hexSecret); - - return { - secret, - hexSecret, - totp, - }; -}; - -export const verify2FA = async (auth: User, secret: string, pin: string) => { - const totp = new TOTP({ - issuer: "Dokploy", - label: `${auth?.email}`, - algorithm: "SHA1", - digits: 6, - secret: secret, - period: 30, - }); - - const delta = totp.validate({ token: pin }); - - if (delta === null) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Invalid 2FA code", - }); - } - return auth; -}; - -const convertBase32ToHex = (base32Secret: string) => { - try { - // Asegurarnos de que la longitud sea múltiplo de 8 agregando padding - let paddedSecret = base32Secret; - while (paddedSecret.length % 8 !== 0) { - paddedSecret += "="; - } - - const bytes = encode.decode.asBytes(paddedSecret.toUpperCase()); - let hex = Buffer.from(bytes).toString("hex"); - - // Asegurarnos de que el hex tenga al menos 32 caracteres (16 bytes) - while (hex.length < 32) { - hex += "0"; - } - - return hex; - } catch (error) { - console.error("Error converting base32 to hex:", error); - return base32Secret; - } -}; - -// Para probar -// const testSecret = "46JMUCG4NJ3CIU6LQAIVFWUW"; -// console.log("Original:", testSecret); -// console.log("Converted:", convertBase32ToHex(testSecret)); -// console.log( -// "Length in bytes:", -// Buffer.from(convertBase32ToHex(testSecret), "hex").length, -// ); -// console.log(generate2FASecret().secret.secret); - -// // Para probar -// const testResult = generate2FASecret(); -// console.log("\nResultados:"); -// console.log("Original base32:", testResult.secret); -// console.log("Hex convertido:", testResult.hexSecret); -// console.log( -// "Longitud en bytes:", -// Buffer.from(testResult.hexSecret, "hex").length, -// ); -export const symmetricDecrypt = async ({ key, data }) => { - const keyAsBytes = await createHash("SHA-256").digest(key); - const dataAsBytes = hexToBytes(data); - const chacha = managedNonce(xchacha20poly1305)(new Uint8Array(keyAsBytes)); - return new TextDecoder().decode(chacha.decrypt(dataAsBytes)); -}; -// export const migrateExistingSecret = async ( -// existingBase32Secret: string, -// encryptionKey: string, -// ) => { -// try { -// // 1. Primero asegurarnos que el secreto base32 tenga el padding correcto -// let paddedSecret = existingBase32Secret; -// while (paddedSecret.length % 8 !== 0) { -// paddedSecret += "="; -// } - -// // 2. Decodificar el base32 a bytes usando hi-base32 -// const bytes = encode.decode.asBytes(paddedSecret.toUpperCase()); - -// // 3. Convertir los bytes a hex -// const hexSecret = Buffer.from(bytes).toString("hex"); - -// // 4. Encriptar el secreto hex usando Better Auth -// const encryptedSecret = await symmetricEncrypt({ -// key: encryptionKey, -// data: hexSecret, -// }); - -// // 5. Crear TOTP con el secreto original para validación -// const originalTotp = new TOTP({ -// issuer: "Dokploy", -// label: "migration-test", -// algorithm: "SHA1", -// digits: 6, -// secret: existingBase32Secret, -// }); - -// // 6. Generar un código de prueba con el secreto original -// const testCode = originalTotp.generate(); - -// // 7. Validar que el código funcione con el secreto original -// const isValid = originalTotp.validate({ token: testCode }) !== null; - -// return { -// originalSecret: existingBase32Secret, -// hexSecret, -// encryptedSecret, // Este es el valor que debes guardar en la base de datos -// isValid, -// testCode, -// secretLength: hexSecret.length, -// }; -// } catch (error: unknown) { -// const errorMessage = -// error instanceof Error ? error.message : "Unknown error"; -// console.error("Error durante la migración:", errorMessage); -// throw new Error(`Error al migrar el secreto: ${errorMessage}`); -// } -// }; - -// // // Ejemplo de uso con el secreto de prueba -// // const testMigration = await migrateExistingSecret( -// // "46JMUCG4NJ3CIU6LQAIVFWUW", -// // process.env.BETTER_AUTH_SECRET || "your-encryption-key", -// // ); -// // console.log("\nPrueba de migración:"); -// // console.log("Secreto original (base32):", testMigration.originalSecret); -// // console.log("Secreto convertido (hex):", testMigration.hexSecret); -// // console.log("Secreto encriptado:", testMigration.encryptedSecret); -// // console.log("Longitud del secreto hex:", testMigration.secretLength); -// // console.log("¿Conversión válida?:", testMigration.isValid); -// // console.log("Código de prueba:", testMigration.testCode); -// const secret = "46JMUCG4NJ3CIU6LQAIVFWUW"; -// const isValid = createOTP(secret, { -// digits: 6, -// period: 30, -// }).verify("123456"); - -// console.log(isValid.then((isValid) => console.log(isValid))); diff --git a/packages/server/src/services/backup.ts b/packages/server/src/services/backup.ts index ef3d04467..327057869 100644 --- a/packages/server/src/services/backup.ts +++ b/packages/server/src/services/backup.ts @@ -2,8 +2,6 @@ import { db } from "@dokploy/server/db"; import { type apiCreateBackup, backups } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; -import { IS_CLOUD } from "../constants"; -import { removeScheduleBackup, scheduleBackup } from "../utils/backups/utils"; export type Backup = typeof backups.$inferSelect; diff --git a/packages/server/src/services/compose.ts b/packages/server/src/services/compose.ts index 70bc411c1..a3ebc26ce 100644 --- a/packages/server/src/services/compose.ts +++ b/packages/server/src/services/compose.ts @@ -44,10 +44,9 @@ import { import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { encodeBase64 } from "../utils/docker/utils"; -import { findAdminById, findUserById, getDokployUrl } from "./admin"; +import { getDokployUrl } from "./admin"; import { createDeploymentCompose, updateDeploymentStatus } from "./deployment"; import { validUniqueServerAppName } from "./project"; -import { cleanupFullDocker } from "./settings"; export type Compose = typeof compose.$inferSelect; diff --git a/packages/server/src/services/deployment.ts b/packages/server/src/services/deployment.ts index 096bdf19c..86d6c88e8 100644 --- a/packages/server/src/services/deployment.ts +++ b/packages/server/src/services/deployment.ts @@ -12,7 +12,7 @@ import { import { removeDirectoryIfExistsContent } from "@dokploy/server/utils/filesystem/directory"; import { TRPCError } from "@trpc/server"; import { format } from "date-fns"; -import { and, desc, eq, isNull } from "drizzle-orm"; +import { desc, eq } from "drizzle-orm"; import { type Application, findApplicationById, @@ -278,9 +278,11 @@ export const removeDeployment = async (deploymentId: string) => { .returning(); return deployment[0]; } catch (error) { + const message = + error instanceof Error ? error.message : "Error creating the deployment"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error deleting this deployment", + message, }); } }; @@ -535,9 +537,11 @@ export const createServerDeployment = async ( } return deploymentCreate[0]; } catch (error) { + const message = + error instanceof Error ? error.message : "Error creating the deployment"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error creating the deployment", + message, }); } }; diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index 99dcde559..fe068fc22 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -4,7 +4,7 @@ import { manageDomain } from "@dokploy/server/utils/traefik/domain"; import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { type apiCreateDomain, domains } from "../db/schema"; -import { findAdmin, findAdminById, findUserById } from "./admin"; +import { findUserById } from "./admin"; import { findApplicationById } from "./application"; import { findServerById } from "./server"; diff --git a/packages/server/src/services/gitlab.ts b/packages/server/src/services/gitlab.ts index 0822aaaba..fdca2775e 100644 --- a/packages/server/src/services/gitlab.ts +++ b/packages/server/src/services/gitlab.ts @@ -1,9 +1,7 @@ import { db } from "@dokploy/server/db"; import { type apiCreateGitlab, - type bitbucket, gitProvider, - type github, gitlab, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; diff --git a/packages/server/src/services/mariadb.ts b/packages/server/src/services/mariadb.ts index 8257b5875..00be29d6a 100644 --- a/packages/server/src/services/mariadb.ts +++ b/packages/server/src/services/mariadb.ts @@ -4,7 +4,7 @@ import { backups, mariadb, } from "@dokploy/server/db/schema"; -import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; +import { buildAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildMariadb } from "@dokploy/server/utils/databases/mariadb"; import { pullImage } from "@dokploy/server/utils/docker/utils"; diff --git a/packages/server/src/services/mongo.ts b/packages/server/src/services/mongo.ts index 031a60131..0ac4cc632 100644 --- a/packages/server/src/services/mongo.ts +++ b/packages/server/src/services/mongo.ts @@ -1,6 +1,6 @@ import { db } from "@dokploy/server/db"; import { type apiCreateMongo, backups, mongo } from "@dokploy/server/db/schema"; -import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; +import { buildAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildMongo } from "@dokploy/server/utils/databases/mongo"; import { pullImage } from "@dokploy/server/utils/docker/utils"; diff --git a/packages/server/src/services/mount.ts b/packages/server/src/services/mount.ts index 38e82d1a1..55557ea0e 100644 --- a/packages/server/src/services/mount.ts +++ b/packages/server/src/services/mount.ts @@ -123,8 +123,8 @@ export const updateMount = async ( mountId: string, mountData: Partial, ) => { - return await db.transaction(async (transaction) => { - const mount = await db + return await db.transaction(async (tx) => { + const mount = await tx .update(mounts) .set({ ...mountData, diff --git a/packages/server/src/services/postgres.ts b/packages/server/src/services/postgres.ts index 682d3f78d..75b81c506 100644 --- a/packages/server/src/services/postgres.ts +++ b/packages/server/src/services/postgres.ts @@ -4,7 +4,7 @@ import { backups, postgres, } from "@dokploy/server/db/schema"; -import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; +import { buildAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildPostgres } from "@dokploy/server/utils/databases/postgres"; import { pullImage } from "@dokploy/server/utils/docker/utils"; diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index d5a2149a7..775621773 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -7,19 +7,15 @@ import { } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { and, desc, eq } from "drizzle-orm"; -import { slugify } from "../setup/server-setup"; -import { generatePassword, generateRandomDomain } from "../templates/utils"; +import { generatePassword } from "../templates/utils"; import { removeService } from "../utils/docker/utils"; import { removeDirectoryCode } from "../utils/filesystem/directory"; import { authGithub } from "../utils/providers/github"; import { removeTraefikConfig } from "../utils/traefik/application"; import { manageDomain } from "../utils/traefik/domain"; -import { findAdminById, findUserById } from "./admin"; +import { findUserById } from "./admin"; import { findApplicationById } from "./application"; -import { - removeDeployments, - removeDeploymentsByPreviewDeploymentId, -} from "./deployment"; +import { removeDeploymentsByPreviewDeploymentId } from "./deployment"; import { createDomain } from "./domain"; import { type Github, getIssueComment } from "./github"; @@ -111,9 +107,13 @@ export const removePreviewDeployment = async (previewDeploymentId: string) => { } return deployment[0]; } catch (error) { + const message = + error instanceof Error + ? error.message + : "Error deleting this preview deployment"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error deleting this preview deployment", + message, }); } }; diff --git a/packages/server/src/services/redirect.ts b/packages/server/src/services/redirect.ts index f16dbe428..1896105fe 100644 --- a/packages/server/src/services/redirect.ts +++ b/packages/server/src/services/redirect.ts @@ -6,7 +6,7 @@ import { updateRedirectMiddleware, } from "@dokploy/server/utils/traefik/redirect"; import { TRPCError } from "@trpc/server"; -import { desc, eq } from "drizzle-orm"; +import { eq } from "drizzle-orm"; import type { z } from "zod"; import { findApplicationById } from "./application"; export type Redirect = typeof redirects.$inferSelect; @@ -114,9 +114,11 @@ export const updateRedirectById = async ( return redirect; } catch (error) { + const message = + error instanceof Error ? error.message : "Error updating this redirect"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error updating this redirect", + message, }); } }; diff --git a/packages/server/src/services/redis.ts b/packages/server/src/services/redis.ts index e0dbbe025..9f4a1f9e6 100644 --- a/packages/server/src/services/redis.ts +++ b/packages/server/src/services/redis.ts @@ -1,6 +1,6 @@ import { db } from "@dokploy/server/db"; import { type apiCreateRedis, redis } from "@dokploy/server/db/schema"; -import { buildAppName, cleanAppName } from "@dokploy/server/db/schema"; +import { buildAppName } from "@dokploy/server/db/schema"; import { generatePassword } from "@dokploy/server/templates/utils"; import { buildRedis } from "@dokploy/server/utils/databases/redis"; import { pullImage } from "@dokploy/server/utils/docker/utils"; diff --git a/packages/server/src/services/registry.ts b/packages/server/src/services/registry.ts index 853f4cf7b..6468cd970 100644 --- a/packages/server/src/services/registry.ts +++ b/packages/server/src/services/registry.ts @@ -112,9 +112,11 @@ export const updateRegistry = async ( return response; } catch (error) { + const message = + error instanceof Error ? error.message : "Error updating this registry"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error updating this registry", + message, }); } }; diff --git a/packages/server/src/services/security.ts b/packages/server/src/services/security.ts index 5efca19fd..d6947b887 100644 --- a/packages/server/src/services/security.ts +++ b/packages/server/src/services/security.ts @@ -76,9 +76,11 @@ export const deleteSecurityById = async (securityId: string) => { await removeSecurityMiddleware(application, result); return result; } catch (error) { + const message = + error instanceof Error ? error.message : "Error removing this security"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error removing this security", + message, }); } }; @@ -98,9 +100,11 @@ export const updateSecurityById = async ( return response[0]; } catch (error) { + const message = + error instanceof Error ? error.message : "Error updating this security"; throw new TRPCError({ code: "BAD_REQUEST", - message: "Error updating this security", + message, }); } }; diff --git a/packages/server/src/services/server.ts b/packages/server/src/services/server.ts index afe851ef5..a4d5c5d85 100644 --- a/packages/server/src/services/server.ts +++ b/packages/server/src/services/server.ts @@ -5,7 +5,7 @@ import { server, } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; -import { desc, eq } from "drizzle-orm"; +import { eq } from "drizzle-orm"; export type Server = typeof server.$inferSelect; diff --git a/packages/server/src/services/settings.ts b/packages/server/src/services/settings.ts index 01ac43a14..75613be02 100644 --- a/packages/server/src/services/settings.ts +++ b/packages/server/src/services/settings.ts @@ -169,7 +169,6 @@ echo "$json_output" const result = JSON.parse(stdout); return result; } - const items = readdirSync(dirPath, { withFileTypes: true }); const stack = [dirPath]; const result: TreeDataItem[] = []; diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 9e924e9f1..f36d8ef65 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,38 +1,10 @@ import { db } from "@dokploy/server/db"; -import { type users_temp, member } from "@dokploy/server/db/schema"; +import { member, type users_temp } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { and, eq } from "drizzle-orm"; -import { findUserById } from "./admin"; export type User = typeof users_temp.$inferSelect; -// export const findUserById = async (userId: string) => { -// // const userR = await db.query.user.findFirst({ -// // where: eq(user.userId, userId), -// // }); -// // if (!userR) { -// // throw new TRPCError({ -// // code: "NOT_FOUND", -// // message: "User not found", -// // }); -// // } -// // return user; -// }; - -export const findUserByAuthId = async (authId: string) => { - // const userR = await db.query.user.findFirst({ - // where: eq(user.id, authId), - // with: {}, - // }); - // if (!userR) { - // throw new TRPCError({ - // code: "NOT_FOUND", - // message: "User not found", - // }); - // } - // return userR; -}; - export const addNewProject = async ( userId: string, projectId: string, diff --git a/packages/server/src/setup/monitoring-setup.ts b/packages/server/src/setup/monitoring-setup.ts index ea6c768ba..afadb6c10 100644 --- a/packages/server/src/setup/monitoring-setup.ts +++ b/packages/server/src/setup/monitoring-setup.ts @@ -1,7 +1,7 @@ import { findServerById } from "@dokploy/server/services/server"; import type { ContainerCreateOptions } from "dockerode"; import { IS_CLOUD } from "../constants"; -import { findAdminById, findUserById } from "../services/admin"; +import { findUserById } from "../services/admin"; import { getDokployImageTag } from "../services/settings"; import { pullImage, pullRemoteImage } from "../utils/docker/utils"; import { execAsync, execAsyncRemote } from "../utils/process/execAsync"; diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index 30b18ea42..5cff7f837 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -1,6 +1,5 @@ import { IS_CLOUD, paths } from "@dokploy/server/constants"; import { type RotatingFileStream, createStream } from "rotating-file-stream"; -import { db } from "../../db"; import { execAsync } from "../process/execAsync"; class LogRotationManager { diff --git a/packages/server/src/utils/backups/mysql.ts b/packages/server/src/utils/backups/mysql.ts index 009a02cf2..1272fc3ed 100644 --- a/packages/server/src/utils/backups/mysql.ts +++ b/packages/server/src/utils/backups/mysql.ts @@ -1,4 +1,3 @@ -import { unlink } from "node:fs/promises"; import path from "node:path"; import type { BackupSchedule } from "@dokploy/server/services/backup"; import type { MySql } from "@dokploy/server/services/mysql"; diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts index 838cf74e3..cbf951c73 100644 --- a/packages/server/src/utils/builders/compose.ts +++ b/packages/server/src/utils/builders/compose.ts @@ -2,7 +2,6 @@ import { createWriteStream, existsSync, mkdirSync, - readFileSync, writeFileSync, } from "node:fs"; import { dirname, join } from "node:path"; diff --git a/packages/server/src/utils/notifications/database-backup.ts b/packages/server/src/utils/notifications/database-backup.ts index 08cff4b54..37a4a1ff2 100644 --- a/packages/server/src/utils/notifications/database-backup.ts +++ b/packages/server/src/utils/notifications/database-backup.ts @@ -1,4 +1,3 @@ -import { error } from "node:console"; import { db } from "@dokploy/server/db"; import { notifications } from "@dokploy/server/db/schema"; import DatabaseBackupEmail from "@dokploy/server/emails/emails/database-backup"; diff --git a/packages/server/src/utils/traefik/middleware.ts b/packages/server/src/utils/traefik/middleware.ts index 60345f66c..934d637e1 100644 --- a/packages/server/src/utils/traefik/middleware.ts +++ b/packages/server/src/utils/traefik/middleware.ts @@ -95,7 +95,7 @@ export const loadRemoteMiddlewares = async (serverId: string) => { } const config = load(stdout) as FileConfig; return config; - } catch (error) { + } catch (_) { throw new Error(`File not found: ${configPath}`); } }; From 3403f8ab36f5fb27c5b9b9c4087364118e695702 Mon Sep 17 00:00:00 2001 From: Mahad Kalam Date: Sun, 23 Feb 2025 00:47:04 +0000 Subject: [PATCH 081/126] chore: update umami to v2.16.1 --- apps/dokploy/templates/templates.ts | 2 +- apps/dokploy/templates/umami/docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f9..7ee9a6305 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -426,7 +426,7 @@ export const templates: TemplateData[] = [ { id: "umami", name: "Umami", - version: "v2.14.0", + version: "v2.16.1", description: "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", logo: "umami.png", diff --git a/apps/dokploy/templates/umami/docker-compose.yml b/apps/dokploy/templates/umami/docker-compose.yml index 875681658..26efd337c 100644 --- a/apps/dokploy/templates/umami/docker-compose.yml +++ b/apps/dokploy/templates/umami/docker-compose.yml @@ -1,6 +1,6 @@ services: umami: - image: ghcr.io/umami-software/umami:postgresql-v2.14.0 + image: ghcr.io/umami-software/umami:postgresql-v2.16.1 restart: always healthcheck: test: ["CMD-SHELL", "curl http://localhost:3000/api/heartbeat"] From 2470d672d404734d4a9e39b2d3683bde25b7401b Mon Sep 17 00:00:00 2001 From: 190km Date: Sun, 23 Feb 2025 01:18:18 +0000 Subject: [PATCH 082/126] fix: fixed highligh search terms color --- .../dashboard/docker/logs/terminal-line.tsx | 57 ++++++++----------- 1 file changed, 23 insertions(+), 34 deletions(-) diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx index c25acc67f..116efedf3 100644 --- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx @@ -35,45 +35,34 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) { }) : "--- No time found ---"; - const highlightMessage = (text: string, term: string) => { - if (!term) { + const highlightMessage = (text: string, term: string) => { + if (!term) { + return ( + + ); + } + + const htmlContent = fancyAnsi.toHtml(text); + const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); + + const modifiedContent = htmlContent.replace( + searchRegex, + (match) => + `${match}`, + ); + return ( ); - } - - const htmlContent = fancyAnsi.toHtml(text); - const modifiedContent = htmlContent.replace( - /]*)>([^<]*)<\/span>/g, - (match, attrs, content) => { - const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); - if (!content.match(searchRegex)) return match; - - const segments = content.split(searchRegex); - const wrappedSegments = segments - .map((segment: string) => - segment.toLowerCase() === term.toLowerCase() - ? `${segment}` - : segment, - ) - .join(""); - - return `${wrappedSegments}`; - }, - ); - - return ( - - ); - }; + }; const tooltip = (color: string, timestamp: string | null) => { const square = ( From 8ab6d6b2828d27522a9a3a1243306973b1bd0ac7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 20:35:21 -0600 Subject: [PATCH 083/126] chore: clean up unused variables and improve error handling across codebase This commit focuses on removing unused variables, adding placeholder error handling, and generally tidying up various files across the Dokploy application. Changes include: - Removing unused imports and variables - Adding placeholder error handling in catch blocks - Cleaning up commented-out code - Removing deprecated utility files - Improving type safety and code consistency --- .../cluster/modify-swarm-settings.tsx | 2 +- .../advanced/volumes/update-volume.tsx | 2 +- .../generic/save-bitbucket-provider.tsx | 1 - .../dashboard/application/general/show.tsx | 3 +- .../show-preview-settings.tsx | 2 +- .../dashboard/compose/delete-service.tsx | 2 +- .../compose/domains/show-domains.tsx | 2 +- .../compose/general/compose-file-editor.tsx | 5 +- .../save-bitbucket-provider-compose.tsx | 1 - .../compose/general/isolated-deployment.tsx | 2 +- .../compose/general/randomize-compose.tsx | 4 +- .../general/show-converted-compose.tsx | 2 +- .../paid/container/container-block-chart.tsx | 8 -- .../organization/handle-organization.tsx | 2 +- .../postgres/advanced/show-custom-command.tsx | 2 +- .../dashboard/project/add-application.tsx | 2 +- .../dashboard/project/add-template.tsx | 4 +- .../show-external-redis-credentials.tsx | 2 +- .../components/dashboard/requests/columns.tsx | 2 +- .../dashboard/requests/requests-table.tsx | 4 +- .../components/dashboard/search-command.tsx | 5 +- .../settings/billing/show-billing.tsx | 8 +- .../settings/cluster/nodes/show-nodes.tsx | 3 +- .../cluster/registry/handle-registry.tsx | 2 +- .../git/bitbucket/add-bitbucket-provider.tsx | 4 +- .../settings/git/show-git-providers.tsx | 2 +- .../notifications/handle-notifications.tsx | 4 +- .../notifications/show-notifications.tsx | 2 +- .../settings/profile/disable-2fa.tsx | 2 +- .../dashboard/settings/profile/enable-2fa.tsx | 12 --- .../settings/profile/profile-form.tsx | 4 +- .../servers/actions/show-storage-actions.tsx | 2 +- .../servers/actions/toggle-docker-cleanup.tsx | 2 +- .../settings/servers/edit-script.tsx | 2 +- .../settings/servers/gpu-support.tsx | 4 +- .../settings/servers/handle-servers.tsx | 2 +- .../settings/servers/security-audit.tsx | 2 +- .../settings/servers/setup-monitoring.tsx | 2 +- .../settings/servers/validate-server.tsx | 2 +- .../servers/welcome-stripe/create-server.tsx | 6 +- .../servers/welcome-stripe/verify.tsx | 9 -- .../settings/users/show-invitations.tsx | 4 +- .../dashboard/settings/users/show-users.tsx | 2 +- .../dashboard/settings/web-server.tsx | 5 +- .../web-server/manage-traefik-ports.tsx | 2 +- .../settings/web-server/update-server-ip.tsx | 2 +- .../dashboard/swarm/applications/columns.tsx | 2 +- .../swarm/applications/data-table.tsx | 2 +- .../swarm/details/show-node-config.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 25 ++--- .../components/layouts/update-server.tsx | 2 +- apps/dokploy/components/layouts/user-nav.tsx | 2 +- .../components/shared/breadcrumb-sidebar.tsx | 2 +- .../dokploy/components/shared/drawer-logs.tsx | 2 +- apps/dokploy/components/ui/file-tree.tsx | 2 +- apps/dokploy/migrate.ts | 2 +- apps/dokploy/pages/_error.tsx | 2 +- apps/dokploy/pages/api/health.ts | 2 +- .../pages/api/providers/github/setup.ts | 3 +- apps/dokploy/pages/api/stripe/webhook.ts | 46 ++++++--- apps/dokploy/pages/dashboard/docker.tsx | 2 +- apps/dokploy/pages/dashboard/monitoring.tsx | 3 +- .../pages/dashboard/project/[projectId].tsx | 6 +- .../services/application/[applicationId].tsx | 5 +- .../services/compose/[composeId].tsx | 5 +- .../services/mariadb/[mariadbId].tsx | 5 +- .../[projectId]/services/mongo/[mongoId].tsx | 5 +- .../[projectId]/services/mysql/[mysqlId].tsx | 5 +- .../services/postgres/[postgresId].tsx | 5 +- .../[projectId]/services/redis/[redisId].tsx | 5 +- .../dashboard/settings/git-providers.tsx | 2 +- .../pages/dashboard/settings/ssh-keys.tsx | 4 +- apps/dokploy/pages/dashboard/swarm.tsx | 2 +- apps/dokploy/pages/dashboard/traefik.tsx | 2 +- apps/dokploy/pages/index.tsx | 21 ++--- apps/dokploy/pages/invitation.tsx | 15 +-- apps/dokploy/pages/register.tsx | 55 +++++------ apps/dokploy/pages/reset-password.tsx | 2 +- apps/dokploy/pages/send-reset-password.tsx | 8 +- apps/dokploy/server/api/routers/auth.ts | 20 ++-- apps/dokploy/server/api/routers/server.ts | 13 +++ apps/dokploy/server/api/routers/stripe.ts | 6 +- apps/dokploy/server/db/seed.ts | 7 +- apps/dokploy/server/utils/docker.ts | 2 +- .../server/wss/docker-container-terminal.ts | 10 +- apps/dokploy/server/wss/drawer-logs.ts | 2 +- apps/dokploy/server/wss/listen-deployment.ts | 2 +- apps/dokploy/server/wss/terminal.ts | 10 +- apps/dokploy/templates/appsmith/index.ts | 2 +- apps/dokploy/templates/blender/index.ts | 2 +- apps/dokploy/templates/cloudflared/index.ts | 2 +- apps/dokploy/templates/drawio/index.ts | 2 +- apps/dokploy/templates/immich/index.ts | 2 +- apps/dokploy/templates/unifi/index.ts | 2 +- packages/server/src/db/schema/certificate.ts | 21 ++--- packages/server/src/db/schema/git-provider.ts | 2 +- packages/server/src/db/schema/registry.ts | 2 +- packages/server/src/lib/auth.ts | 77 ++++++++------- packages/server/src/lib/crypto.ts | 94 ------------------- packages/server/src/lib/scrypt/index.ts | 1 - packages/server/src/monitoring/utils.ts | 4 +- packages/server/src/services/admin.ts | 10 +- packages/server/src/services/docker.ts | 20 ++-- packages/server/src/services/github.ts | 2 +- packages/server/src/services/mount.ts | 2 +- .../server/src/services/preview-deployment.ts | 2 +- packages/server/src/setup/monitoring-setup.ts | 4 +- packages/server/src/setup/postgres-setup.ts | 2 +- packages/server/src/setup/redis-setup.ts | 2 +- packages/server/src/setup/server-audit.ts | 2 +- packages/server/src/setup/server-validate.ts | 2 +- packages/server/src/setup/setup.ts | 4 +- packages/server/src/setup/traefik-setup.ts | 2 +- packages/server/src/types/with.ts | 2 +- .../server/src/utils/access-log/handler.ts | 2 +- packages/server/src/utils/backups/utils.ts | 2 +- packages/server/src/utils/builders/compose.ts | 3 +- packages/server/src/utils/builders/index.ts | 2 +- .../server/src/utils/builders/nixpacks.ts | 2 +- .../server/src/utils/databases/mariadb.ts | 2 +- packages/server/src/utils/databases/mongo.ts | 2 +- packages/server/src/utils/databases/mysql.ts | 2 +- packages/server/src/utils/databases/redis.ts | 2 +- packages/server/src/utils/docker/domain.ts | 2 +- packages/server/src/utils/docker/utils.ts | 4 +- packages/server/src/utils/gpu-setup.ts | 4 +- .../server/src/utils/process/execAsync.ts | 2 +- .../server/src/utils/providers/bitbucket.ts | 1 - packages/server/src/utils/providers/git.ts | 2 +- packages/server/src/utils/providers/gitlab.ts | 11 +-- .../server/src/utils/traefik/application.ts | 8 +- .../verification/send-verification-email.tsx | 49 ++++++++++ 132 files changed, 375 insertions(+), 471 deletions(-) delete mode 100644 packages/server/src/lib/crypto.ts delete mode 100644 packages/server/src/lib/scrypt/index.ts create mode 100644 packages/server/src/verification/send-verification-email.tsx diff --git a/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx b/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx index 9b71a042a..95a559f66 100644 --- a/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/cluster/modify-swarm-settings.tsx @@ -130,7 +130,7 @@ const createStringToJSONSchema = (schema: z.ZodTypeAny) => { } try { return JSON.parse(str); - } catch (e) { + } catch (_e) { ctx.addIssue({ code: "custom", message: "Invalid JSON format" }); return z.NEVER; } diff --git a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx index 687d0f608..8da09b58b 100644 --- a/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx +++ b/apps/dokploy/components/dashboard/application/advanced/volumes/update-volume.tsx @@ -77,7 +77,7 @@ export const UpdateVolume = ({ serviceType, }: Props) => { const [isOpen, setIsOpen] = useState(false); - const utils = api.useUtils(); + const _utils = api.useUtils(); const { data } = api.mounts.one.useQuery( { mountId, diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx index 9b207d636..9af040b79 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx @@ -84,7 +84,6 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { data: repositories, isLoading: isLoadingRepositories, error, - isError, } = api.bitbucket.getBitbucketRepositories.useQuery( { bitbucketId, diff --git a/apps/dokploy/components/dashboard/application/general/show.tsx b/apps/dokploy/components/dashboard/application/general/show.tsx index 0ea331e94..8989ca198 100644 --- a/apps/dokploy/components/dashboard/application/general/show.tsx +++ b/apps/dokploy/components/dashboard/application/general/show.tsx @@ -27,8 +27,7 @@ export const ShowGeneralApplication = ({ applicationId }: Props) => { const { mutateAsync: stop, isLoading: isStopping } = api.application.stop.useMutation(); - const { mutateAsync: deploy, isLoading: isDeploying } = - api.application.deploy.useMutation(); + const { mutateAsync: deploy } = api.application.deploy.useMutation(); const { mutateAsync: reload, isLoading: isReloading } = api.application.reload.useMutation(); diff --git a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx index fec61ca60..9d53f31d8 100644 --- a/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx +++ b/apps/dokploy/components/dashboard/application/preview-deployments/show-preview-settings.tsx @@ -279,7 +279,7 @@ export const ShowPreviewSettings = ({ applicationId }: Props) => { ( + render={() => ( { compose: () => api.compose.one.useQuery({ composeId: id }, { enabled: !!id }), }; - const { data, refetch } = queryMap[type] + const { data } = queryMap[type] ? queryMap[type]() : api.mongo.one.useQuery({ mongoId: id }, { enabled: !!id }); diff --git a/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx b/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx index 7bc451e00..e6468d6fa 100644 --- a/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx +++ b/apps/dokploy/components/dashboard/compose/domains/show-domains.tsx @@ -118,7 +118,7 @@ export const ShowDomainsCompose = ({ composeId }: Props) => { await deleteDomain({ domainId: item.domainId, }) - .then((data) => { + .then((_data) => { refetch(); toast.success("Domain deleted successfully"); }) diff --git a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx index c4ce44e52..725895821 100644 --- a/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx +++ b/apps/dokploy/components/dashboard/compose/general/compose-file-editor.tsx @@ -35,8 +35,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => { { enabled: !!composeId }, ); - const { mutateAsync, isLoading, error, isError } = - api.compose.update.useMutation(); + const { mutateAsync, isLoading } = api.compose.update.useMutation(); const form = useForm({ defaultValues: { @@ -76,7 +75,7 @@ export const ComposeFileEditor = ({ composeId }: Props) => { composeId, }); }) - .catch((e) => { + .catch((_e) => { toast.error("Error updating the Compose config"); }); }; diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx index 1c06fe881..875841338 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx @@ -84,7 +84,6 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { data: repositories, isLoading: isLoadingRepositories, error, - isError, } = api.bitbucket.getBitbucketRepositories.useQuery( { bitbucketId, diff --git a/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx b/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx index 70d685efb..3ae2e9fe3 100644 --- a/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx +++ b/apps/dokploy/components/dashboard/compose/general/isolated-deployment.tsx @@ -70,7 +70,7 @@ export const IsolatedDeployment = ({ composeId }: Props) => { composeId, isolatedDeployment: formData?.isolatedDeployment || false, }) - .then(async (data) => { + .then(async (_data) => { randomizeCompose(); refetch(); toast.success("Compose updated"); diff --git a/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx b/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx index 4462ef0eb..4cc877fde 100644 --- a/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/randomize-compose.tsx @@ -39,7 +39,7 @@ type Schema = z.infer; export const RandomizeCompose = ({ composeId }: Props) => { const utils = api.useUtils(); const [compose, setCompose] = useState(""); - const [isOpen, setIsOpen] = useState(false); + const [_isOpen, _setIsOpen] = useState(false); const { mutateAsync, error, isError } = api.compose.randomizeCompose.useMutation(); @@ -76,7 +76,7 @@ export const RandomizeCompose = ({ composeId }: Props) => { suffix: formData?.suffix || "", randomize: formData?.randomize || false, }) - .then(async (data) => { + .then(async (_data) => { randomizeCompose(); refetch(); toast.success("Compose updated"); diff --git a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx index 8a2186d9e..49606645c 100644 --- a/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/show-converted-compose.tsx @@ -40,7 +40,7 @@ export const ShowConvertedCompose = ({ composeId }: Props) => { .then(() => { refetch(); }) - .catch((err) => {}); + .catch((_err) => {}); } }, [isOpen]); diff --git a/apps/dokploy/components/dashboard/monitoring/paid/container/container-block-chart.tsx b/apps/dokploy/components/dashboard/monitoring/paid/container/container-block-chart.tsx index 9150dbcde..12af6b91d 100644 --- a/apps/dokploy/components/dashboard/monitoring/paid/container/container-block-chart.tsx +++ b/apps/dokploy/components/dashboard/monitoring/paid/container/container-block-chart.tsx @@ -29,14 +29,6 @@ interface Props { data: ContainerMetric[]; } -interface FormattedMetric { - timestamp: string; - read: number; - write: number; - readUnit: string; - writeUnit: string; -} - const chartConfig = { read: { label: "Read", diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 905a244cc..2a595f436 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -20,7 +20,7 @@ interface Props { organizationId?: string; children?: React.ReactNode; } -export function AddOrganization({ organizationId, children }: Props) { +export function AddOrganization({ organizationId }: Props) { const utils = api.useUtils(); const { data: organization } = api.organization.one.useQuery( { diff --git a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx index 2bae245ec..40e84844f 100644 --- a/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx +++ b/apps/dokploy/components/dashboard/postgres/advanced/show-custom-command.tsx @@ -53,7 +53,7 @@ export const ShowCustomCommand = ({ id, type }: Props) => { mongo: () => api.mongo.update.useMutation(), }; - const { mutateAsync, isLoading } = mutationMap[type] + const { mutateAsync } = mutationMap[type] ? mutationMap[type]() : api.mongo.update.useMutation(); diff --git a/apps/dokploy/components/dashboard/project/add-application.tsx b/apps/dokploy/components/dashboard/project/add-application.tsx index da30cfee3..16c56917d 100644 --- a/apps/dokploy/components/dashboard/project/add-application.tsx +++ b/apps/dokploy/components/dashboard/project/add-application.tsx @@ -103,7 +103,7 @@ export const AddApplication = ({ projectId, projectName }: Props) => { projectId, }); }) - .catch((e) => { + .catch((_e) => { toast.error("Error creating the service"); }); }; diff --git a/apps/dokploy/components/dashboard/project/add-template.tsx b/apps/dokploy/components/dashboard/project/add-template.tsx index 3965f8172..5363e6f36 100644 --- a/apps/dokploy/components/dashboard/project/add-template.tsx +++ b/apps/dokploy/components/dashboard/project/add-template.tsx @@ -434,14 +434,14 @@ export const AddTemplate = ({ projectId }: Props) => { }); toast.promise(promise, { loading: "Setting up...", - success: (data) => { + success: (_data) => { utils.project.one.invalidate({ projectId, }); setOpen(false); return `${template.name} template created successfully`; }, - error: (err) => { + error: (_err) => { return `An error ocurred deploying ${template.name} template`; }, }); diff --git a/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx b/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx index 25b5f2ba7..75112cf6e 100644 --- a/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx +++ b/apps/dokploy/components/dashboard/redis/general/show-external-redis-credentials.tsx @@ -79,7 +79,7 @@ export const ShowExternalRedisCredentials = ({ redisId }: Props) => { useEffect(() => { const buildConnectionUrl = () => { - const hostname = window.location.hostname; + const _hostname = window.location.hostname; const port = form.watch("externalPort") || data?.externalPort; return `redis://default:${data?.databasePassword}@${getIp}:${port}`; diff --git a/apps/dokploy/components/dashboard/requests/columns.tsx b/apps/dokploy/components/dashboard/requests/columns.tsx index c1814190e..2c0391f80 100644 --- a/apps/dokploy/components/dashboard/requests/columns.tsx +++ b/apps/dokploy/components/dashboard/requests/columns.tsx @@ -24,7 +24,7 @@ export const getStatusColor = (status: number) => { export const columns: ColumnDef[] = [ { accessorKey: "level", - header: ({ column }) => { + header: () => { return ; }, cell: ({ row }) => { diff --git a/apps/dokploy/components/dashboard/requests/requests-table.tsx b/apps/dokploy/components/dashboard/requests/requests-table.tsx index cd2949c34..4926ce4e3 100644 --- a/apps/dokploy/components/dashboard/requests/requests-table.tsx +++ b/apps/dokploy/components/dashboard/requests/requests-table.tsx @@ -92,7 +92,7 @@ export const RequestsTable = () => { pageSize: 10, }); - const { data: statsLogs, isLoading } = api.settings.readStatsLogs.useQuery( + const { data: statsLogs } = api.settings.readStatsLogs.useQuery( { sort: sorting[0], page: pagination, @@ -300,7 +300,7 @@ export const RequestsTable = () => {
setSelectedRow(undefined)} + onOpenChange={(_open) => setSelectedRow(undefined)} > diff --git a/apps/dokploy/components/dashboard/search-command.tsx b/apps/dokploy/components/dashboard/search-command.tsx index 5726dc99a..b36703036 100644 --- a/apps/dokploy/components/dashboard/search-command.tsx +++ b/apps/dokploy/components/dashboard/search-command.tsx @@ -22,14 +22,11 @@ import { extractServices, } from "@/pages/dashboard/project/[projectId]"; import { api } from "@/utils/api"; -import type { findProjectById } from "@dokploy/server/services/project"; import { BookIcon, CircuitBoard, GlobeIcon } from "lucide-react"; import { useRouter } from "next/router"; import React from "react"; import { StatusTooltip } from "../shared/status-tooltip"; -type Project = Awaited>; - export const SearchCommand = () => { const router = useRouter(); const [open, setOpen] = React.useState(false); @@ -38,7 +35,7 @@ export const SearchCommand = () => { const { data } = api.project.all.useQuery(undefined, { enabled: !!session, }); - const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); + const { data: isCloud } = api.settings.isCloud.useQuery(); React.useEffect(() => { const down = (e: KeyboardEvent) => { diff --git a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx index 029eaa90f..2c20bb81d 100644 --- a/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx +++ b/apps/dokploy/components/dashboard/settings/billing/show-billing.tsx @@ -38,7 +38,7 @@ export const calculatePrice = (count: number, isAnnual = false) => { return count * 3.5; }; export const ShowBilling = () => { - const { data: servers } = api.server.all.useQuery(undefined); + const { data: servers } = api.server.count.useQuery(); const { data: admin } = api.user.get.useQuery(); const { data, isLoading } = api.stripe.getProducts.useQuery(); const { mutateAsync: createCheckoutSession } = @@ -71,7 +71,7 @@ export const ShowBilling = () => { }); const maxServers = admin?.user.serversQuantity ?? 1; - const percentage = ((servers?.length ?? 0) / maxServers) * 100; + const percentage = ((servers ?? 0) / maxServers) * 100; const safePercentage = Math.min(percentage, 100); return ( @@ -102,13 +102,13 @@ export const ShowBilling = () => {

Servers Plan

- You have {servers?.length} server on your plan of{" "} + You have {servers} server on your plan of{" "} {admin?.user.serversQuantity} servers

- {admin && admin.user.serversQuantity! <= servers?.length! && ( + {admin && admin.user.serversQuantity! <= (servers ?? 0) && (
diff --git a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx index ba3eefa56..b84c3b636 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/nodes/show-nodes.tsx @@ -41,8 +41,7 @@ export const ShowNodes = () => { const { data, isLoading, refetch } = api.cluster.getNodes.useQuery(); const { data: registry } = api.registry.all.useQuery(); - const { mutateAsync: deleteNode, isLoading: isRemoving } = - api.cluster.removeWorker.useMutation(); + const { mutateAsync: deleteNode } = api.cluster.removeWorker.useMutation(); const haveAtLeastOneRegistry = !!(registry && registry?.length > 0); return ( diff --git a/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx b/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx index 78f3b868a..55daedca2 100644 --- a/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx +++ b/apps/dokploy/components/dashboard/settings/cluster/registry/handle-registry.tsx @@ -131,7 +131,7 @@ export const HandleRegistry = ({ registryId }: Props) => { serverId: data.serverId, registryId: registryId || "", }) - .then(async (data) => { + .then(async (_data) => { await utils.registry.all.invalidate(); toast.success(registryId ? "Registry updated" : "Registry added"); setIsOpen(false); diff --git a/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx index 2c0f30466..0df2d0610 100644 --- a/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/settings/git/bitbucket/add-bitbucket-provider.tsx @@ -47,10 +47,10 @@ type Schema = z.infer; export const AddBitbucketProvider = () => { const utils = api.useUtils(); const [isOpen, setIsOpen] = useState(false); - const url = useUrl(); + const _url = useUrl(); const { mutateAsync, error, isError } = api.bitbucket.create.useMutation(); const { data: auth } = api.user.get.useQuery(); - const router = useRouter(); + const _router = useRouter(); const form = useForm({ defaultValues: { username: "", diff --git a/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx b/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx index 3343409fe..451ea5d17 100644 --- a/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx +++ b/apps/dokploy/components/dashboard/settings/git/show-git-providers.tsx @@ -102,7 +102,7 @@ export const ShowGitProviders = () => {
- {data?.map((gitProvider, index) => { + {data?.map((gitProvider, _index) => { const isGithub = gitProvider.providerType === "github"; const isGitlab = gitProvider.providerType === "gitlab"; const isBitbucket = diff --git a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx index 6f6d8ff11..d2d3b450d 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/handle-notifications.tsx @@ -136,7 +136,7 @@ export const HandleNotifications = ({ notificationId }: Props) => { const [visible, setVisible] = useState(false); const { data: isCloud } = api.settings.isCloud.useQuery(); - const { data: notification, refetch } = api.notification.one.useQuery( + const { data: notification } = api.notification.one.useQuery( { notificationId: notificationId || "", }, @@ -1038,7 +1038,7 @@ export const HandleNotifications = ({ notificationId }: Props) => { }); } toast.success("Connection Success"); - } catch (err) { + } catch (_err) { toast.error("Error testing the provider"); } }} diff --git a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx index d65069d4b..782b92413 100644 --- a/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx +++ b/apps/dokploy/components/dashboard/settings/notifications/show-notifications.tsx @@ -56,7 +56,7 @@ export const ShowNotifications = () => { ) : (
- {data?.map((notification, index) => ( + {data?.map((notification, _index) => (
{ toast.success("2FA disabled successfully"); utils.auth.get.invalidate(); setIsOpen(false); - } catch (error) { + } catch (_error) { form.setError("password", { message: "Connection error. Please try again.", }); diff --git a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx index 918df4353..3ececb649 100644 --- a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx @@ -47,20 +47,8 @@ const PinSchema = z.object({ type PasswordForm = z.infer; type PinForm = z.infer; -type TwoFactorEnableResponse = { - totpURI: string; - backupCodes: string[]; -}; - -type TwoFactorSetupData = { - qrCodeUrl: string; - secret: string; - totpURI: string; -}; - export const Enable2FA = () => { const utils = api.useUtils(); - const { data: session } = authClient.useSession(); const [data, setData] = useState(null); const [backupCodes, setBackupCodes] = useState([]); const [isDialogOpen, setIsDialogOpen] = useState(false); diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index f4299709a..761cfb71b 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -54,9 +54,7 @@ const randomImages = [ ]; export const ProfileForm = () => { - const utils = api.useUtils(); - const { mutateAsync: disable2FA, isLoading: isDisabling } = - api.auth.disable2FA.useMutation(); + const _utils = api.useUtils(); const { data, refetch, isLoading } = api.user.get.useQuery(); const { mutateAsync, diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx index cb60effd1..3492ba7c2 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/show-storage-actions.tsx @@ -26,7 +26,7 @@ export const ShowStorageActions = ({ serverId }: Props) => { isLoading: cleanDockerBuilderIsLoading, } = api.settings.cleanDockerBuilder.useMutation(); - const { mutateAsync: cleanMonitoring, isLoading: cleanMonitoringIsLoading } = + const { mutateAsync: cleanMonitoring } = api.settings.cleanMonitoring.useMutation(); const { mutateAsync: cleanUnusedImages, diff --git a/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx b/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx index e574593d5..12e279423 100644 --- a/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/actions/toggle-docker-cleanup.tsx @@ -36,7 +36,7 @@ export const ToggleDockerCleanup = ({ serverId }: Props) => { await refetch(); } toast.success("Docker Cleanup updated"); - } catch (error) { + } catch (_error) { toast.error("Docker Cleanup Error"); } }; diff --git a/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx b/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx index 0a22220ed..6225ee771 100644 --- a/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/edit-script.tsx @@ -82,7 +82,7 @@ export const EditScript = ({ serverId }: Props) => { command: formData.command || "", serverId, }) - .then((data) => { + .then((_data) => { toast.success("Script modified successfully"); }) .catch(() => { diff --git a/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx b/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx index ec60fed6d..c24440a61 100644 --- a/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/gpu-support.tsx @@ -56,7 +56,7 @@ export function GPUSupport({ serverId }: GPUSupportProps) { try { await utils.settings.checkGPUStatus.invalidate({ serverId }); await refetch(); - } catch (error) { + } catch (_error) { toast.error("Failed to refresh GPU status"); } finally { setIsRefreshing(false); @@ -74,7 +74,7 @@ export function GPUSupport({ serverId }: GPUSupportProps) { try { await setupGPU.mutateAsync({ serverId }); - } catch (error) { + } catch (_error) { // Error handling is done in mutation's onError } }; diff --git a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx index be71d8367..979941458 100644 --- a/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/handle-servers.tsx @@ -118,7 +118,7 @@ export const HandleServers = ({ serverId }: Props) => { sshKeyId: data.sshKeyId || "", serverId: serverId || "", }) - .then(async (data) => { + .then(async (_data) => { await utils.server.all.invalidate(); refetchServer(); toast.success(serverId ? "Server Updated" : "Server Created"); diff --git a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx index 475f2b8ff..5f693707f 100644 --- a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx @@ -25,7 +25,7 @@ export const SecurityAudit = ({ serverId }: Props) => { enabled: !!serverId, }, ); - const utils = api.useUtils(); + const _utils = api.useUtils(); return (
diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx index 23173047e..b8c699268 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx @@ -80,7 +80,7 @@ const Schema = z.object({ type Schema = z.infer; export const SetupMonitoring = ({ serverId }: Props) => { - const { data, isLoading } = serverId + const { data } = serverId ? api.server.one.useQuery( { serverId: serverId || "", diff --git a/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx b/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx index db4f17b76..0632b97c2 100644 --- a/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx @@ -25,7 +25,7 @@ export const ValidateServer = ({ serverId }: Props) => { enabled: !!serverId, }, ); - const utils = api.useUtils(); + const _utils = api.useUtils(); return (
diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx index a025ad379..24d01553b 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/create-server.tsx @@ -52,10 +52,10 @@ interface Props { export const CreateServer = ({ stepper }: Props) => { const { data: sshKeys } = api.sshKey.all.useQuery(); - const [isOpen, setIsOpen] = useState(false); + const [isOpen, _setIsOpen] = useState(false); const { data: canCreateMoreServers, refetch } = api.stripe.canCreateMoreServers.useQuery(); - const { mutateAsync, error, isError } = api.server.create.useMutation(); + const { mutateAsync } = api.server.create.useMutation(); const cloudSSHKey = sshKeys?.find( (sshKey) => sshKey.name === "dokploy-cloud-ssh-key", ); @@ -96,7 +96,7 @@ export const CreateServer = ({ stepper }: Props) => { username: data.username || "root", sshKeyId: data.sshKeyId || "", }) - .then(async (data) => { + .then(async (_data) => { toast.success("Server Created"); stepper.next(); }) diff --git a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx index fe8c36c2c..f7c2a987c 100644 --- a/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/welcome-stripe/verify.tsx @@ -37,15 +37,6 @@ export const Verify = () => { ); const [isRefreshing, setIsRefreshing] = useState(false); - const { data: server } = api.server.one.useQuery( - { - serverId, - }, - { - enabled: !!serverId, - }, - ); - return (
diff --git a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx index 12670c280..1bf7aa086 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-invitations.tsx @@ -143,7 +143,7 @@ export const ShowInvitations = () => { {invitation.status === "pending" && ( { + onSelect={(_e) => { copy( `${origin}/invitation?token=${invitation.id}`, ); @@ -159,7 +159,7 @@ export const ShowInvitations = () => { {invitation.status === "pending" && ( { + onSelect={async (_e) => { const result = await authClient.organization.cancelInvitation( { diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index ff56698e4..6847558b7 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -35,7 +35,7 @@ import { AddUserPermissions } from "./add-permissions"; export const ShowUsers = () => { const { data: isCloud } = api.settings.isCloud.useQuery(); const { data, isLoading, refetch } = api.user.all.useQuery(); - const { mutateAsync, isLoading: isRemoving } = api.user.remove.useMutation(); + const { mutateAsync } = api.user.remove.useMutation(); return (
diff --git a/apps/dokploy/components/dashboard/settings/web-server.tsx b/apps/dokploy/components/dashboard/settings/web-server.tsx index 326cb0eaf..64b6d634e 100644 --- a/apps/dokploy/components/dashboard/settings/web-server.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server.tsx @@ -14,10 +14,7 @@ import { ShowTraefikActions } from "./servers/actions/show-traefik-actions"; import { ToggleDockerCleanup } from "./servers/actions/toggle-docker-cleanup"; import { UpdateServer } from "./web-server/update-server"; -interface Props { - className?: string; -} -export const WebServer = ({ className }: Props) => { +export const WebServer = () => { const { t } = useTranslation("settings"); const { data } = api.user.get.useQuery(); diff --git a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx index a30823363..a6958b16f 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/manage-traefik-ports.tsx @@ -99,7 +99,7 @@ export const ManageTraefikPorts = ({ children, serverId }: Props) => { }); toast.success(t("settings.server.webServer.traefik.portsUpdated")); setOpen(false); - } catch (error) { + } catch (_error) { toast.error(t("settings.server.webServer.traefik.portsUpdateError")); } }; diff --git a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx index d6e7345e1..3a511d8ea 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx @@ -43,7 +43,7 @@ interface Props { serverId?: string; } -export const UpdateServerIp = ({ children, serverId }: Props) => { +export const UpdateServerIp = ({ children }: Props) => { const [isOpen, setIsOpen] = useState(false); const { data } = api.user.get.useQuery(); diff --git a/apps/dokploy/components/dashboard/swarm/applications/columns.tsx b/apps/dokploy/components/dashboard/swarm/applications/columns.tsx index ab058e851..5ae091a95 100644 --- a/apps/dokploy/components/dashboard/swarm/applications/columns.tsx +++ b/apps/dokploy/components/dashboard/swarm/applications/columns.tsx @@ -214,7 +214,7 @@ export const columns: ColumnDef[] = [ { accessorKey: "Logs", accessorFn: (row) => row.Error, - header: ({ column }) => { + header: () => { return Logs; }, cell: ({ row }) => { diff --git a/apps/dokploy/components/dashboard/swarm/applications/data-table.tsx b/apps/dokploy/components/dashboard/swarm/applications/data-table.tsx index 03915c19b..96f43e617 100644 --- a/apps/dokploy/components/dashboard/swarm/applications/data-table.tsx +++ b/apps/dokploy/components/dashboard/swarm/applications/data-table.tsx @@ -48,7 +48,7 @@ export function DataTable({ const [columnVisibility, setColumnVisibility] = React.useState({}); const [rowSelection, setRowSelection] = React.useState({}); - const [pagination, setPagination] = React.useState({ + const [_pagination, _setPagination] = React.useState({ pageIndex: 0, //initial page index pageSize: 8, //default page size }); diff --git a/apps/dokploy/components/dashboard/swarm/details/show-node-config.tsx b/apps/dokploy/components/dashboard/swarm/details/show-node-config.tsx index a41c5a497..7f27fe3bf 100644 --- a/apps/dokploy/components/dashboard/swarm/details/show-node-config.tsx +++ b/apps/dokploy/components/dashboard/swarm/details/show-node-config.tsx @@ -17,7 +17,7 @@ interface Props { } export const ShowNodeConfig = ({ nodeId, serverId }: Props) => { - const { data, isLoading } = api.swarm.getNodeInfo.useQuery({ + const { data } = api.swarm.getNodeInfo.useQuery({ nodeId, serverId, }); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 939d10841..63155f8eb 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -157,7 +157,7 @@ const MENU: Menu = { url: "/dashboard/monitoring", icon: BarChartHorizontalBigIcon, // Only enabled in non-cloud environments - isEnabled: ({ auth, isCloud }) => !isCloud, + isEnabled: ({ isCloud }) => !isCloud, }, { isSingle: true, @@ -277,7 +277,7 @@ const MENU: Menu = { url: "/dashboard/settings/servers", icon: Server, // Only enabled for admins - isEnabled: ({ auth, isCloud }) => !!(auth?.role === "owner"), + isEnabled: ({ auth }) => !!(auth?.role === "owner"), }, { isSingle: true, @@ -490,8 +490,9 @@ function SidebarLogo() { const { state } = useSidebar(); const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: user } = api.user.get.useQuery(); - const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); + // const { data: dokployVersion } = api.settings.getDokployVersion.useQuery(); const { data: session } = authClient.useSession(); + const { data: organizations, refetch, @@ -501,12 +502,12 @@ function SidebarLogo() { api.organization.delete.useMutation(); const { isMobile } = useSidebar(); const { data: activeOrganization } = authClient.useActiveOrganization(); - const utils = api.useUtils(); + const _utils = api.useUtils(); const { data: invitations, refetch: refetchInvitations } = api.user.getInvitations.useQuery(); - const [activeTeam, setActiveTeam] = useState< + const [_activeTeam, setActiveTeam] = useState< typeof activeOrganization | null >(null); @@ -543,7 +544,7 @@ function SidebarLogo() {

- {activeOrganization?.name} + {activeOrganization?.name ?? "Select Organization"}

@@ -551,7 +552,7 @@ function SidebarLogo() { +
{org.name}
- {org.name} - {(org.ownerId === session?.user?.id || isCloud) && ( + {org.ownerId === session?.user?.id && (
))} - {!isCloud && user?.role === "owner" && ( + {(user?.role === "owner" || isCloud) && ( <> @@ -721,11 +722,11 @@ export default function Page({ children }: Props) { const router = useRouter(); const pathname = usePathname(); - const currentPath = router.pathname; + const _currentPath = router.pathname; const { data: auth } = api.user.get.useQuery(); const includesProjects = pathname?.includes("/dashboard/project"); - const { data: isCloud, isLoading } = api.settings.isCloud.useQuery(); + const { data: isCloud } = api.settings.isCloud.useQuery(); const { home: filteredHome, diff --git a/apps/dokploy/components/layouts/update-server.tsx b/apps/dokploy/components/layouts/update-server.tsx index fa748f849..5d7978859 100644 --- a/apps/dokploy/components/layouts/update-server.tsx +++ b/apps/dokploy/components/layouts/update-server.tsx @@ -11,7 +11,7 @@ export const UpdateServerButton = () => { latestVersion: null, updateAvailable: false, }); - const router = useRouter(); + const _router = useRouter(); const { data: isCloud } = api.settings.isCloud.useQuery(); const { mutateAsync: getUpdateData } = api.settings.getUpdateData.useMutation(); diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 196f6d77d..4a9624de4 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -24,7 +24,7 @@ import { useRouter } from "next/router"; import { ModeToggle } from "../ui/modeToggle"; import { SidebarMenuButton } from "../ui/sidebar"; -const AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7; +const _AUTO_CHECK_UPDATES_INTERVAL_MINUTES = 7; export const UserNav = () => { const router = useRouter(); diff --git a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx index 60730c96b..74e9fdf63 100644 --- a/apps/dokploy/components/shared/breadcrumb-sidebar.tsx +++ b/apps/dokploy/components/shared/breadcrumb-sidebar.tsx @@ -26,7 +26,7 @@ export const BreadcrumbSidebar = ({ list }: Props) => { - {list.map((item, index) => ( + {list.map((item, _index) => ( diff --git a/apps/dokploy/components/shared/drawer-logs.tsx b/apps/dokploy/components/shared/drawer-logs.tsx index 5e4ab554b..d8d1affb7 100644 --- a/apps/dokploy/components/shared/drawer-logs.tsx +++ b/apps/dokploy/components/shared/drawer-logs.tsx @@ -43,7 +43,7 @@ export const DrawerLogs = ({ isOpen, onClose, filteredLogs }: Props) => { return ( { + onOpenChange={(_open) => { onClose(); }} > diff --git a/apps/dokploy/components/ui/file-tree.tsx b/apps/dokploy/components/ui/file-tree.tsx index 9db3786e6..0f50d5089 100644 --- a/apps/dokploy/components/ui/file-tree.tsx +++ b/apps/dokploy/components/ui/file-tree.tsx @@ -85,7 +85,7 @@ const Tree = React.forwardRef( return ids; }, [data, initialSlelectedItemId]); - const { ref: refRoot, width, height } = useResizeObserver(); + const { ref: refRoot } = useResizeObserver(); return (
diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index febd1c0e2..097459b96 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -136,7 +136,7 @@ await db }, }); for (const project of projects) { - const user = await db.update(schema.projects).set({ + const _user = await db.update(schema.projects).set({ organizationId: project.user.organizations[0]?.id || "", }); } diff --git a/apps/dokploy/pages/_error.tsx b/apps/dokploy/pages/_error.tsx index 958e17407..d28e2cb00 100644 --- a/apps/dokploy/pages/_error.tsx +++ b/apps/dokploy/pages/_error.tsx @@ -90,7 +90,7 @@ export default function Custom404({ statusCode, error }: Props) { } // @ts-ignore -Error.getInitialProps = ({ res, err, ...rest }: NextPageContext) => { +Error.getInitialProps = ({ res, err }: NextPageContext) => { const statusCode = res ? res.statusCode : err ? err.statusCode : 404; return { statusCode, error: err }; }; diff --git a/apps/dokploy/pages/api/health.ts b/apps/dokploy/pages/api/health.ts index 9dc8101e5..57875a192 100644 --- a/apps/dokploy/pages/api/health.ts +++ b/apps/dokploy/pages/api/health.ts @@ -1,7 +1,7 @@ import type { NextApiRequest, NextApiResponse } from "next"; export default async function handler( - req: NextApiRequest, + _req: NextApiRequest, res: NextApiResponse, ) { return res.status(200).json({ ok: true }); diff --git a/apps/dokploy/pages/api/providers/github/setup.ts b/apps/dokploy/pages/api/providers/github/setup.ts index ac5e7a6b0..327122509 100644 --- a/apps/dokploy/pages/api/providers/github/setup.ts +++ b/apps/dokploy/pages/api/providers/github/setup.ts @@ -16,8 +16,7 @@ export default async function handler( req: NextApiRequest, res: NextApiResponse, ) { - const { code, state, installation_id, setup_action }: Query = - req.query as Query; + const { code, state, installation_id }: Query = req.query as Query; if (!code) { return res.status(400).json({ error: "Missing code parameter" }); diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index 6200a79ec..592803b15 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -1,7 +1,7 @@ import { buffer } from "node:stream/consumers"; import { db } from "@/server/db"; -import { server, users_temp } from "@/server/db/schema"; -import { findUserById } from "@dokploy/server"; +import { organization, server, users_temp } from "@/server/db/schema"; +import { findUserById, type Server } from "@dokploy/server"; import { asc, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import Stripe from "stripe"; @@ -172,11 +172,11 @@ export default async function handler( } await db - .update(admins) + .update(users_temp) .set({ serversQuantity: suscription?.items?.data?.[0]?.quantity ?? 0, }) - .where(eq(admins.stripeCustomerId, suscription.customer as string)); + .where(eq(users_temp.stripeCustomerId, suscription.customer as string)); const admin = await findUserByStripeCustomerId( suscription.customer as string, @@ -205,11 +205,13 @@ export default async function handler( return res.status(400).send("Webhook Error: Admin not found"); } await db - .update(admins) + .update(users_temp) .set({ serversQuantity: 0, }) - .where(eq(admins.stripeCustomerId, newInvoice.customer as string)); + .where( + eq(users_temp.stripeCustomerId, newInvoice.customer as string), + ); await disableServers(admin.id); } @@ -245,12 +247,18 @@ export default async function handler( } const disableServers = async (userId: string) => { - await db - .update(server) - .set({ - serverStatus: "inactive", - }) - .where(eq(server.userId, userId)); + const organizations = await db.query.organization.findMany({ + where: eq(organization.ownerId, userId), + }); + + for (const org of organizations) { + await db + .update(server) + .set({ + serverStatus: "inactive", + }) + .where(eq(server.organizationId, org.id)); + } }; const findUserByStripeCustomerId = async (stripeCustomerId: string) => { @@ -275,11 +283,19 @@ const deactivateServer = async (serverId: string) => { }; export const findServersByUserIdSorted = async (userId: string) => { - const servers = await db.query.server.findMany({ - where: eq(server.userId, userId), - orderBy: asc(server.createdAt), + const organizations = await db.query.organization.findMany({ + where: eq(organization.ownerId, userId), }); + const servers: Server[] = []; + for (const org of organizations) { + const serversByOrg = await db.query.server.findMany({ + where: eq(server.organizationId, org.id), + orderBy: asc(server.createdAt), + }); + servers.push(...serversByOrg); + } + return servers; }; export const updateServersBasedOnQuantity = async ( diff --git a/apps/dokploy/pages/dashboard/docker.tsx b/apps/dokploy/pages/dashboard/docker.tsx index a9d80353f..e01a763be 100644 --- a/apps/dokploy/pages/dashboard/docker.tsx +++ b/apps/dokploy/pages/dashboard/docker.tsx @@ -72,7 +72,7 @@ export async function getServerSideProps( trpcState: helpers.dehydrate(), }, }; - } catch (error) { + } catch (_error) { return { props: {}, }; diff --git a/apps/dokploy/pages/dashboard/monitoring.tsx b/apps/dokploy/pages/dashboard/monitoring.tsx index 4d8b072f5..4272c4536 100644 --- a/apps/dokploy/pages/dashboard/monitoring.tsx +++ b/apps/dokploy/pages/dashboard/monitoring.tsx @@ -15,8 +15,7 @@ const BASE_URL = "http://localhost:3001/metrics"; const DEFAULT_TOKEN = "metrics"; const Dashboard = () => { - const { data: isCloud } = api.settings.isCloud.useQuery(); - const [toggleMonitoring, setToggleMonitoring] = useLocalStorage( + const [toggleMonitoring, _setToggleMonitoring] = useLocalStorage( "monitoring-enabled", false, ); diff --git a/apps/dokploy/pages/dashboard/project/[projectId].tsx b/apps/dokploy/pages/dashboard/project/[projectId].tsx index f85aa9ee8..62de98c13 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId].tsx @@ -261,7 +261,7 @@ const Project = ( try { await composeActions.start.mutateAsync({ composeId: serviceId }); success++; - } catch (error) { + } catch (_error) { toast.error(`Error starting service ${serviceId}`); } } @@ -281,7 +281,7 @@ const Project = ( try { await composeActions.stop.mutateAsync({ composeId: serviceId }); success++; - } catch (error) { + } catch (_error) { toast.error(`Error stopping service ${serviceId}`); } } @@ -685,7 +685,7 @@ export async function getServerSideProps( projectId: params?.projectId, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx index 59dba68ce..94b8f5f5d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/application/[applicationId].tsx @@ -65,7 +65,7 @@ type TabState = const Service = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { applicationId, activeTab } = props; const router = useRouter(); const { projectId } = router.query; @@ -86,7 +86,6 @@ const Service = ( const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); return (
@@ -399,7 +398,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx index c1331e23f..46b727d2a 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/compose/[composeId].tsx @@ -59,7 +59,7 @@ type TabState = const Service = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { composeId, activeTab } = props; const router = useRouter(); const { projectId } = router.query; @@ -79,7 +79,6 @@ const Service = ( ); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); return ( @@ -393,7 +392,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx index 033b88a9d..e91e0978d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mariadb/[mariadbId].tsx @@ -52,7 +52,7 @@ type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; const Mariadb = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { mariadbId, activeTab } = props; const router = useRouter(); @@ -60,7 +60,6 @@ const Mariadb = ( const [tab, setSab] = useState(activeTab); const { data } = api.mariadb.one.useQuery({ mariadbId }); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -342,7 +341,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx index dea8cd57b..b10b7b93d 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mongo/[mongoId].tsx @@ -52,7 +52,7 @@ type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; const Mongo = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { mongoId, activeTab } = props; const router = useRouter(); const { projectId } = router.query; @@ -60,7 +60,6 @@ const Mongo = ( const { data } = api.mongo.one.useQuery({ mongoId }); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -343,7 +342,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx index cc4eb4aa3..261a2762b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/mysql/[mysqlId].tsx @@ -52,14 +52,13 @@ type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; const MySql = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { mysqlId, activeTab } = props; const router = useRouter(); const { projectId } = router.query; const [tab, setSab] = useState(activeTab); const { data } = api.mysql.one.useQuery({ mysqlId }); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -348,7 +347,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx index d0f1dc106..5d8fd3b1b 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/postgres/[postgresId].tsx @@ -52,7 +52,7 @@ type TabState = "projects" | "monitoring" | "settings" | "backups" | "advanced"; const Postgresql = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { postgresId, activeTab } = props; const router = useRouter(); const { projectId } = router.query; @@ -60,7 +60,6 @@ const Postgresql = ( const { data } = api.postgres.one.useQuery({ postgresId }); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); return ( @@ -345,7 +344,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx index 2b053df47..c4f40281f 100644 --- a/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx +++ b/apps/dokploy/pages/dashboard/project/[projectId]/services/redis/[redisId].tsx @@ -51,7 +51,7 @@ type TabState = "projects" | "monitoring" | "settings" | "advanced"; const Redis = ( props: InferGetServerSidePropsType, ) => { - const [toggleMonitoring, setToggleMonitoring] = useState(false); + const [_toggleMonitoring, _setToggleMonitoring] = useState(false); const { redisId, activeTab } = props; const router = useRouter(); const { projectId } = router.query; @@ -59,7 +59,6 @@ const Redis = ( const { data } = api.redis.one.useQuery({ redisId }); const { data: auth } = api.user.get.useQuery(); - const { data: monitoring } = api.user.getMetricsToken.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery(); @@ -335,7 +334,7 @@ export async function getServerSideProps( activeTab: (activeTab || "general") as TabState, }, }; - } catch (error) { + } catch (_error) { return { redirect: { permanent: false, diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index 4187a0ef7..ce2adc9ce 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -68,7 +68,7 @@ export async function getServerSideProps( trpcState: helpers.dehydrate(), }, }; - } catch (error) { + } catch (_error) { return { props: {}, }; diff --git a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx index 738c647d4..2472feab4 100644 --- a/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx +++ b/apps/dokploy/pages/dashboard/settings/ssh-keys.tsx @@ -33,7 +33,7 @@ export async function getServerSideProps( }, }; } - const { req, res, resolvedUrl } = ctx; + const { req, res } = ctx; const helpers = createServerSideHelpers({ router: appRouter, ctx: { @@ -69,7 +69,7 @@ export async function getServerSideProps( trpcState: helpers.dehydrate(), }, }; - } catch (error) { + } catch (_error) { return { props: {}, }; diff --git a/apps/dokploy/pages/dashboard/swarm.tsx b/apps/dokploy/pages/dashboard/swarm.tsx index c693fd8cf..155531160 100644 --- a/apps/dokploy/pages/dashboard/swarm.tsx +++ b/apps/dokploy/pages/dashboard/swarm.tsx @@ -72,7 +72,7 @@ export async function getServerSideProps( trpcState: helpers.dehydrate(), }, }; - } catch (error) { + } catch (_error) { return { props: {}, }; diff --git a/apps/dokploy/pages/dashboard/traefik.tsx b/apps/dokploy/pages/dashboard/traefik.tsx index 90359ccd6..ce8208beb 100644 --- a/apps/dokploy/pages/dashboard/traefik.tsx +++ b/apps/dokploy/pages/dashboard/traefik.tsx @@ -72,7 +72,7 @@ export async function getServerSideProps( trpcState: helpers.dehydrate(), }, }; - } catch (error) { + } catch (_error) { return { props: {}, }; diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index c910e78ec..783ec651d 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -43,18 +43,11 @@ const LoginSchema = z.object({ password: z.string().min(8), }); -const TwoFactorSchema = z.object({ +const _TwoFactorSchema = z.object({ code: z.string().min(6), }); -const BackupCodeSchema = z.object({ - code: z.string().min(8, { - message: "Backup code must be at least 8 characters", - }), -}); - type LoginForm = z.infer; -type BackupCodeForm = z.infer; interface Props { IS_CLOUD: boolean; @@ -101,7 +94,7 @@ export default function Home({ IS_CLOUD }: Props) { toast.success("Logged in successfully"); router.push("/dashboard/projects"); - } catch (error) { + } catch (_error) { toast.error("An error occurred while logging in"); } finally { setIsLoginLoading(false); @@ -117,7 +110,7 @@ export default function Home({ IS_CLOUD }: Props) { setIsTwoFactorLoading(true); try { - const { data, error } = await authClient.twoFactor.verifyTotp({ + const { error } = await authClient.twoFactor.verifyTotp({ code: twoFactorCode.replace(/\s/g, ""), }); @@ -129,7 +122,7 @@ export default function Home({ IS_CLOUD }: Props) { toast.success("Logged in successfully"); router.push("/dashboard/projects"); - } catch (error) { + } catch (_error) { toast.error("An error occurred while verifying 2FA code"); } finally { setIsTwoFactorLoading(false); @@ -145,7 +138,7 @@ export default function Home({ IS_CLOUD }: Props) { setIsBackupCodeLoading(true); try { - const { data, error } = await authClient.twoFactor.verifyBackupCode({ + const { error } = await authClient.twoFactor.verifyBackupCode({ code: backupCode.trim(), }); @@ -159,7 +152,7 @@ export default function Home({ IS_CLOUD }: Props) { toast.success("Logged in successfully"); router.push("/dashboard/projects"); - } catch (error) { + } catch (_error) { toast.error("An error occurred while verifying backup code"); } finally { setIsBackupCodeLoading(false); @@ -396,7 +389,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { }, }; } - } catch (error) {} + } catch (_error) {} return { props: { diff --git a/apps/dokploy/pages/invitation.tsx b/apps/dokploy/pages/invitation.tsx index 91ca1d0d7..813ec74cc 100644 --- a/apps/dokploy/pages/invitation.tsx +++ b/apps/dokploy/pages/invitation.tsx @@ -16,7 +16,6 @@ import { authClient } from "@/lib/auth-client"; import { api } from "@/utils/api"; import { IS_CLOUD, getUserByToken } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; -import { AlertTriangle } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; @@ -90,9 +89,6 @@ const Invitation = ({ }, ); - const { mutateAsync, error, isError, isSuccess } = - api.auth.createUser.useMutation(); - const form = useForm({ defaultValues: { name: "", @@ -115,7 +111,7 @@ const Invitation = ({ const onSubmit = async (values: Register) => { try { - const { data, error } = await authClient.signUp.email({ + const { error } = await authClient.signUp.email({ email: values.email, password: values.password, name: values.name, @@ -131,13 +127,13 @@ const Invitation = ({ return; } - const result = await authClient.organization.acceptInvitation({ + const _result = await authClient.organization.acceptInvitation({ invitationId: token, }); toast.success("Account created successfully"); router.push("/dashboard/projects"); - } catch (error) { + } catch (_error) { toast.error("An error occurred while creating your account"); } }; @@ -180,14 +176,14 @@ const Invitation = ({
- {isError && ( + {/* {isError && (
{error?.message}
- )} + )} */}
@@ -313,7 +309,6 @@ export async function getServerSideProps(ctx: GetServerSidePropsContext) { const { query } = ctx; const token = query.token; - console.log("query", query); if (typeof token !== "string") { return { diff --git a/apps/dokploy/pages/register.tsx b/apps/dokploy/pages/register.tsx index 701d8f5b5..980f641f6 100644 --- a/apps/dokploy/pages/register.tsx +++ b/apps/dokploy/pages/register.tsx @@ -13,15 +13,15 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { authClient } from "@/lib/auth-client"; -import { api } from "@/utils/api"; import { IS_CLOUD, isAdminPresent, validateRequest } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; import { AlertTriangle } from "lucide-react"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; -import { type ReactElement, useEffect } from "react"; +import { type ReactElement, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import { toast } from "sonner"; import { z } from "zod"; const registerSchema = z @@ -72,15 +72,16 @@ interface Props { const Register = ({ isCloud }: Props) => { const router = useRouter(); - const { mutateAsync, error, isError, data } = - api.auth.createAdmin.useMutation(); + const [isError, setIsError] = useState(false); + const [error, setError] = useState(null); + const [data, setData] = useState(null); const form = useForm({ defaultValues: { name: "Mauricio Siu", email: "user5@yopmail.com", - password: "Password1234", - confirmPassword: "Password1234", + password: "Password123", + confirmPassword: "Password123", }, resolver: zodResolver(registerSchema), }); @@ -96,27 +97,19 @@ const Register = ({ isCloud }: Props) => { name: values.name, }); - // const { data, error } = await authClient.admin.createUser({ - // name: values.name, - // email: values.email, - // password: values.password, - // role: "superAdmin", - // }); - - // consol/e.log(data, error); - // await mutateAsync({ - // email: values.email.toLowerCase(), - // password: values.password, - // }) - // .then(() => { - // toast.success("User registered successfuly", { - // duration: 2000, - // }); - // if (!isCloud) { - // router.push("/"); - // } - // }) - // .catch((e) => e); + if (error) { + setIsError(true); + setError(error.message || "An error occurred"); + } else { + toast.success("User registered successfuly", { + duration: 2000, + }); + if (!isCloud) { + router.push("/"); + } else { + setData(data); + } + } }; return (
@@ -138,15 +131,15 @@ const Register = ({ isCloud }: Props) => {
{isError && ( -
+
- {error?.message} + {error}
)} - {data?.type === "cloud" && ( - + {isCloud && data && ( + Registered successfully, please check your inbox or spam folder to confirm your account. diff --git a/apps/dokploy/pages/reset-password.tsx b/apps/dokploy/pages/reset-password.tsx index 42e2ce002..a34a25ed0 100644 --- a/apps/dokploy/pages/reset-password.tsx +++ b/apps/dokploy/pages/reset-password.tsx @@ -77,7 +77,7 @@ export default function Home({ token }: Props) { resetPasswordToken: token, password: values.password, }) - .then((data) => { + .then((_data) => { toast.success("Password reset successfully", { duration: 2000, }); diff --git a/apps/dokploy/pages/send-reset-password.tsx b/apps/dokploy/pages/send-reset-password.tsx index ce73fbb82..0ea59cf85 100644 --- a/apps/dokploy/pages/send-reset-password.tsx +++ b/apps/dokploy/pages/send-reset-password.tsx @@ -43,13 +43,13 @@ type AuthResponse = { }; export default function Home() { - const [temp, setTemp] = useState({ + const [temp, _setTemp] = useState({ is2FAEnabled: false, authId: "", }); const { mutateAsync, isLoading, isError, error } = api.auth.sendResetPasswordEmail.useMutation(); - const router = useRouter(); + const _router = useRouter(); const form = useForm({ defaultValues: { email: "", @@ -65,7 +65,7 @@ export default function Home() { await mutateAsync({ email: values.email, }) - .then((data) => { + .then((_data) => { toast.success("Email sent", { duration: 2000, }); @@ -150,7 +150,7 @@ export default function Home() { Home.getLayout = (page: ReactElement) => { return {page}; }; -export async function getServerSideProps(context: GetServerSidePropsContext) { +export async function getServerSideProps(_context: GetServerSidePropsContext) { if (!IS_CLOUD) { return { redirect: { diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index da3121b2e..31a50c67b 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -33,7 +33,7 @@ import { } from "../trpc"; export const authRouter = createTRPCRouter({ - createAdmin: publicProcedure.mutation(async ({ ctx, input }) => { + createAdmin: publicProcedure.mutation(async ({ input }) => { try { if (!IS_CLOUD) { const admin = await db.query.admins.findFirst({}); @@ -72,9 +72,9 @@ export const authRouter = createTRPCRouter({ }); } }), - createUser: publicProcedure.mutation(async ({ ctx, input }) => { + createUser: publicProcedure.mutation(async ({ input }) => { try { - const token = await getUserByToken(input.token); + const _token = await getUserByToken(input.token); // if (token.isExpired) { // throw new TRPCError({ // code: "BAD_REQUEST", @@ -103,7 +103,7 @@ export const authRouter = createTRPCRouter({ } }), - login: publicProcedure.mutation(async ({ ctx, input }) => { + login: publicProcedure.mutation(async ({ input }) => { try { const auth = await findAuthByEmail(input.email); @@ -169,7 +169,7 @@ export const authRouter = createTRPCRouter({ }), logout: protectedProcedure.mutation(async ({ ctx }) => { - const { req, res } = ctx; + const { req } = ctx; const { session } = await validateRequest(req); if (!session) return false; @@ -229,7 +229,7 @@ export const authRouter = createTRPCRouter({ message: "Password is incorrect", }); } - const { req, res } = ctx; + const { req } = ctx; const { session } = await validateRequest(req); if (!session) return false; @@ -245,7 +245,7 @@ export const authRouter = createTRPCRouter({ return true; }), - generateToken: protectedProcedure.mutation(async ({ ctx, input }) => { + generateToken: protectedProcedure.mutation(async ({ ctx }) => { const auth = await findUserById(ctx.user.id); console.log(auth); @@ -276,7 +276,7 @@ export const authRouter = createTRPCRouter({ email: z.string().min(1).email(), }), ) - .mutation(async ({ ctx, input }) => { + .mutation(async ({ input }) => { if (!IS_CLOUD) { throw new TRPCError({ code: "NOT_FOUND", @@ -329,7 +329,7 @@ export const authRouter = createTRPCRouter({ password: z.string().min(1), }), ) - .mutation(async ({ ctx, input }) => { + .mutation(async ({ input }) => { if (!IS_CLOUD) { throw new TRPCError({ code: "NOT_FOUND", @@ -373,7 +373,7 @@ export const authRouter = createTRPCRouter({ confirmationToken: z.string().min(1), }), ) - .mutation(async ({ ctx, input }) => { + .mutation(async ({ input }) => { if (!IS_CLOUD) { throw new TRPCError({ code: "NOT_FOUND", diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 3215226e3..1ebb161a4 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -12,6 +12,7 @@ import { mariadb, mongo, mysql, + organization, postgres, redis, server, @@ -102,6 +103,18 @@ export const serverRouter = createTRPCRouter({ return result; }), + count: protectedProcedure.query(async ({ ctx }) => { + const organizations = await db.query.organization.findMany({ + where: eq(organization.ownerId, ctx.user.id), + with: { + servers: true, + }, + }); + + const servers = organizations.flatMap((org) => org.servers); + + return servers.length ?? 0; + }), withSSHKey: protectedProcedure.query(async ({ ctx }) => { const result = await db.query.server.findMany({ orderBy: desc(server.createdAt), diff --git a/apps/dokploy/server/api/routers/stripe.ts b/apps/dokploy/server/api/routers/stripe.ts index 0e0e07a47..a226eeac8 100644 --- a/apps/dokploy/server/api/routers/stripe.ts +++ b/apps/dokploy/server/api/routers/stripe.ts @@ -56,7 +56,7 @@ export const stripeRouter = createTRPCRouter({ }); const items = getStripeItems(input.serverQuantity, input.isAnnual); - const user = await findUserById(ctx.user.ownerId); + const user = await findUserById(ctx.user.id); let stripeCustomerId = user.stripeCustomerId; @@ -78,7 +78,7 @@ export const stripeRouter = createTRPCRouter({ customer: stripeCustomerId, }), metadata: { - ownerId: user.id, + adminId: user.id, }, allow_promotion_codes: true, success_url: `${WEBSITE_URL}/dashboard/settings/servers?success=true`, @@ -88,7 +88,7 @@ export const stripeRouter = createTRPCRouter({ return { sessionId: session.id }; }), createCustomerPortalSession: adminProcedure.mutation(async ({ ctx }) => { - const user = await findUserById(ctx.user.ownerId); + const user = await findUserById(ctx.user.id); if (!user.stripeCustomerId) { throw new TRPCError({ diff --git a/apps/dokploy/server/db/seed.ts b/apps/dokploy/server/db/seed.ts index 3216a44b4..5b3eb6c62 100644 --- a/apps/dokploy/server/db/seed.ts +++ b/apps/dokploy/server/db/seed.ts @@ -1,15 +1,10 @@ -import bc from "bcrypt"; 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); - -function password(txt: string) { - return bc.hashSync(txt, 10); -} +const _db = drizzle(pg); async function seed() { console.log("> Seed:", process.env.DATABASE_PATH, "\n"); diff --git a/apps/dokploy/server/utils/docker.ts b/apps/dokploy/server/utils/docker.ts index 92008678f..3314eb62e 100644 --- a/apps/dokploy/server/utils/docker.ts +++ b/apps/dokploy/server/utils/docker.ts @@ -6,7 +6,7 @@ export const isWSL = async () => { const { stdout } = await execAsync("uname -r"); const isWSL = stdout.includes("microsoft"); return isWSL; - } catch (error) { + } catch (_error) { return false; } }; diff --git a/apps/dokploy/server/wss/docker-container-terminal.ts b/apps/dokploy/server/wss/docker-container-terminal.ts index 04ef5c96c..2f25edb1a 100644 --- a/apps/dokploy/server/wss/docker-container-terminal.ts +++ b/apps/dokploy/server/wss/docker-container-terminal.ts @@ -50,8 +50,8 @@ export const setupDockerContainerTerminalWebSocketServer = ( throw new Error("No SSH key available for this server"); const conn = new Client(); - let stdout = ""; - let stderr = ""; + let _stdout = ""; + let _stderr = ""; conn .once("ready", () => { conn.exec( @@ -61,16 +61,16 @@ export const setupDockerContainerTerminalWebSocketServer = ( if (err) throw err; stream - .on("close", (code: number, signal: string) => { + .on("close", (code: number, _signal: string) => { ws.send(`\nContainer closed with code: ${code}\n`); conn.end(); }) .on("data", (data: string) => { - stdout += data.toString(); + _stdout += data.toString(); ws.send(data.toString()); }) .stderr.on("data", (data) => { - stderr += data.toString(); + _stderr += data.toString(); ws.send(data.toString()); console.error("Error: ", data.toString()); }); diff --git a/apps/dokploy/server/wss/drawer-logs.ts b/apps/dokploy/server/wss/drawer-logs.ts index 9fe947ffa..404dfeee5 100644 --- a/apps/dokploy/server/wss/drawer-logs.ts +++ b/apps/dokploy/server/wss/drawer-logs.ts @@ -32,7 +32,7 @@ export const setupDrawerLogsWebSocketServer = ( }); wssTerm.on("connection", async (ws, req) => { - const url = new URL(req.url || "", `http://${req.headers.host}`); + const _url = new URL(req.url || "", `http://${req.headers.host}`); const { user, session } = await validateRequest(req); if (!user || !session) { diff --git a/apps/dokploy/server/wss/listen-deployment.ts b/apps/dokploy/server/wss/listen-deployment.ts index ee470fcbf..4a25c6f0e 100644 --- a/apps/dokploy/server/wss/listen-deployment.ts +++ b/apps/dokploy/server/wss/listen-deployment.ts @@ -103,7 +103,7 @@ export const setupDeploymentLogsWebSocketServer = ( ws.close(); }); } - } catch (error) { + } catch (_error) { // @ts-ignore // const errorMessage = error?.message as unknown as string; // ws.send(errorMessage); diff --git a/apps/dokploy/server/wss/terminal.ts b/apps/dokploy/server/wss/terminal.ts index 6e9c3614f..094c5e157 100644 --- a/apps/dokploy/server/wss/terminal.ts +++ b/apps/dokploy/server/wss/terminal.ts @@ -144,8 +144,8 @@ export const setupTerminalWebSocketServer = ( } const conn = new Client(); - let stdout = ""; - let stderr = ""; + let _stdout = ""; + let _stderr = ""; ws.send("Connecting...\n"); @@ -158,16 +158,16 @@ export const setupTerminalWebSocketServer = ( if (err) throw err; stream - .on("close", (code: number, signal: string) => { + .on("close", (code: number, _signal: string) => { ws.send(`\nContainer closed with code: ${code}\n`); conn.end(); }) .on("data", (data: string) => { - stdout += data.toString(); + _stdout += data.toString(); ws.send(data.toString()); }) .stderr.on("data", (data) => { - stderr += data.toString(); + _stderr += data.toString(); ws.send(data.toString()); console.error("Error: ", data.toString()); }); diff --git a/apps/dokploy/templates/appsmith/index.ts b/apps/dokploy/templates/appsmith/index.ts index ff744a249..73279e91b 100644 --- a/apps/dokploy/templates/appsmith/index.ts +++ b/apps/dokploy/templates/appsmith/index.ts @@ -7,7 +7,7 @@ import { } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); + const _mainServiceHash = generateHash(schema.projectName); const domains: DomainSchema[] = [ { diff --git a/apps/dokploy/templates/blender/index.ts b/apps/dokploy/templates/blender/index.ts index 84e527554..79508bed5 100644 --- a/apps/dokploy/templates/blender/index.ts +++ b/apps/dokploy/templates/blender/index.ts @@ -7,7 +7,7 @@ import { } from "../utils"; export function generate(schema: Schema): Template { - const mainServiceHash = generateHash(schema.projectName); + const _mainServiceHash = generateHash(schema.projectName); const mainDomain = generateRandomDomain(schema); const domains: DomainSchema[] = [ diff --git a/apps/dokploy/templates/cloudflared/index.ts b/apps/dokploy/templates/cloudflared/index.ts index 661fa31d0..93ea091c6 100644 --- a/apps/dokploy/templates/cloudflared/index.ts +++ b/apps/dokploy/templates/cloudflared/index.ts @@ -1,6 +1,6 @@ import type { Schema, Template } from "../utils"; -export function generate(schema: Schema): Template { +export function generate(_schema: Schema): Template { const envs = [`CLOUDFLARE_TUNNEL_TOKEN=""`]; return { diff --git a/apps/dokploy/templates/drawio/index.ts b/apps/dokploy/templates/drawio/index.ts index e3c57c5a5..701283c8d 100644 --- a/apps/dokploy/templates/drawio/index.ts +++ b/apps/dokploy/templates/drawio/index.ts @@ -8,7 +8,7 @@ import { export function generate(schema: Schema): Template { const mainDomain = generateRandomDomain(schema); - const secretKeyBase = generateBase64(64); + const _secretKeyBase = generateBase64(64); const domains: DomainSchema[] = [ { diff --git a/apps/dokploy/templates/immich/index.ts b/apps/dokploy/templates/immich/index.ts index b1b11afb1..4beca87da 100644 --- a/apps/dokploy/templates/immich/index.ts +++ b/apps/dokploy/templates/immich/index.ts @@ -11,7 +11,7 @@ export function generate(schema: Schema): Template { const mainDomain = generateRandomDomain(schema); const dbPassword = generatePassword(); const dbUser = "immich"; - const appSecret = generateBase64(32); + const _appSecret = generateBase64(32); const domains: DomainSchema[] = [ { diff --git a/apps/dokploy/templates/unifi/index.ts b/apps/dokploy/templates/unifi/index.ts index 975ce63d9..ea67b0fae 100644 --- a/apps/dokploy/templates/unifi/index.ts +++ b/apps/dokploy/templates/unifi/index.ts @@ -1,6 +1,6 @@ import type { Schema, Template } from "../utils"; -export function generate(schema: Schema): Template { +export function generate(_schema: Schema): Template { const mounts: Template["mounts"] = [ { filePath: "init-mongo.sh", diff --git a/packages/server/src/db/schema/certificate.ts b/packages/server/src/db/schema/certificate.ts index 80d533508..bf72f7db3 100644 --- a/packages/server/src/db/schema/certificate.ts +++ b/packages/server/src/db/schema/certificate.ts @@ -28,19 +28,16 @@ export const certificates = pgTable("certificate", { }), }); -export const certificatesRelations = relations( - certificates, - ({ one, many }) => ({ - server: one(server, { - fields: [certificates.serverId], - references: [server.serverId], - }), - organization: one(organization, { - fields: [certificates.organizationId], - references: [organization.id], - }), +export const certificatesRelations = relations(certificates, ({ one }) => ({ + server: one(server, { + fields: [certificates.serverId], + references: [server.serverId], }), -); + organization: one(organization, { + fields: [certificates.organizationId], + references: [organization.id], + }), +})); export const apiCreateCertificate = createInsertSchema(certificates, { name: z.string().min(1), diff --git a/packages/server/src/db/schema/git-provider.ts b/packages/server/src/db/schema/git-provider.ts index dc88131a9..922307376 100644 --- a/packages/server/src/db/schema/git-provider.ts +++ b/packages/server/src/db/schema/git-provider.ts @@ -29,7 +29,7 @@ export const gitProvider = pgTable("git_provider", { .references(() => organization.id, { onDelete: "cascade" }), }); -export const gitProviderRelations = relations(gitProvider, ({ one, many }) => ({ +export const gitProviderRelations = relations(gitProvider, ({ one }) => ({ github: one(github, { fields: [gitProvider.gitProviderId], references: [github.gitProviderId], diff --git a/packages/server/src/db/schema/registry.ts b/packages/server/src/db/schema/registry.ts index 35526f90c..b18747095 100644 --- a/packages/server/src/db/schema/registry.ts +++ b/packages/server/src/db/schema/registry.ts @@ -32,7 +32,7 @@ export const registry = pgTable("registry", { .references(() => organization.id, { onDelete: "cascade" }), }); -export const registryRelations = relations(registry, ({ one, many }) => ({ +export const registryRelations = relations(registry, ({ many }) => ({ applications: many(applications), })); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index a8d75637b..192f6d0ea 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -2,14 +2,12 @@ import type { IncomingMessage } from "node:http"; import * as bcrypt from "bcrypt"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { - createAuthMiddleware, - organization, - twoFactor, -} from "better-auth/plugins"; +import { organization, twoFactor } from "better-auth/plugins"; import { and, desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; +import { sendVerificationEmail } from "../verification/send-verification-email"; +import { IS_CLOUD } from "../constants"; export const auth = betterAuth({ database: drizzleAdapter(db, { @@ -27,9 +25,18 @@ export const auth = betterAuth({ clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, }, + emailVerification: { + sendOnSignUp: true, + autoSignInAfterVerification: true, + sendVerificationEmail: async ({ user, url }) => { + console.log("Sending verification email to", user.email); + await sendVerificationEmail(user.email, url); + }, + }, emailAndPassword: { enabled: true, - + autoSignIn: !IS_CLOUD, + requireEmailVerification: IS_CLOUD, password: { async hash(password) { return bcrypt.hashSync(password, 10); @@ -39,33 +46,37 @@ export const auth = betterAuth({ }, }, }, - hooks: { - after: createAuthMiddleware(async (ctx) => { - if (ctx.path.startsWith("/sign-up")) { - const newSession = ctx.context.newSession; - if (ctx.headers?.get("x-dokploy-token")) { - } else { - const organization = await db - .insert(schema.organization) - .values({ - name: "My Organization", - ownerId: newSession?.user?.id || "", - createdAt: new Date(), - }) - .returning() - .then((res) => res[0]); - - await db.insert(schema.member).values({ - userId: newSession?.user?.id || "", - organizationId: organization?.id || "", - role: "owner", - createdAt: new Date(), - }); - } - } - }), - }, databaseHooks: { + user: { + create: { + after: async (user) => { + const isAdminPresent = await db.query.member.findFirst({ + where: eq(schema.member.role, "owner"), + }); + + if (IS_CLOUD || !isAdminPresent) { + await db.transaction(async (tx) => { + const organization = await tx + .insert(schema.organization) + .values({ + name: "My Organization", + ownerId: user.id, + createdAt: new Date(), + }) + .returning() + .then((res) => res[0]); + + await tx.insert(schema.member).values({ + userId: user.id, + organizationId: organization?.id || "", + role: "owner", + createdAt: new Date(), + }); + }); + } + }, + }, + }, session: { create: { before: async (session) => { @@ -106,7 +117,7 @@ export const auth = betterAuth({ plugins: [ twoFactor(), organization({ - async sendInvitationEmail(data, request) { + async sendInvitationEmail(data, _request) { const inviteLink = `https://example.com/accept-invitation/${data.id}`; // https://example.com/accept-invitation/8jlBi9Tb9isDb8mc8Sb85u1BaJYklKB2 // sendOrganizationInvitation({ diff --git a/packages/server/src/lib/crypto.ts b/packages/server/src/lib/crypto.ts deleted file mode 100644 index 27d86dbd5..000000000 --- a/packages/server/src/lib/crypto.ts +++ /dev/null @@ -1,94 +0,0 @@ -// import { -// decodeHex, -// encodeBase32LowerCaseNoPadding, -// encodeHexLowerCase, -// } from "@oslojs/encoding"; -// import { generateRandomString } from "@oslojs/crypto/random"; -// import { constantTimeEqual } from "@oslojs/crypto/subtle"; -// import { scrypt } from "./scrypt/index"; - -// import type { RandomReader } from "@oslojs/crypto/random"; - -// async function generateScryptKey( -// data: string, -// salt: string, -// blockSize = 16, -// ): Promise { -// const encodedData = new TextEncoder().encode(data); -// const encodedSalt = new TextEncoder().encode(salt); -// const keyUint8Array = await scrypt(encodedData, encodedSalt, { -// N: 16384, -// r: blockSize, -// p: 1, -// dkLen: 64, -// }); -// return new Uint8Array(keyUint8Array); -// } - -// const random: RandomReader = { -// read(bytes: Uint8Array): void { -// crypto.getRandomValues(bytes); -// }, -// }; - -// export function generateId(length: number): string { -// const alphabet = "abcdefghijklmnopqrstuvwxyz0123456789"; -// return generateRandomString(random, alphabet, length); -// } - -// export function generateIdFromEntropySize(size: number): string { -// const buffer = crypto.getRandomValues(new Uint8Array(size)); -// return encodeBase32LowerCaseNoPadding(buffer); -// } - -// export class Scrypt implements PasswordHashingAlgorithm { -// async hash(password: string): Promise { -// const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16))); -// const key = await generateScryptKey(password.normalize("NFKC"), salt); -// return `${salt}:${encodeHexLowerCase(key)}`; -// } -// async verify(hash: string, password: string): Promise { -// const parts = hash.split(":"); -// if (parts.length !== 2) return false; - -// const [salt, key] = parts; -// const targetKey = await generateScryptKey(password.normalize("NFKC"), salt); -// return constantTimeEqual(targetKey, decodeHex(key)); -// } -// } - -// export class LegacyScrypt implements PasswordHashingAlgorithm { -// async hash(password: string): Promise { -// const salt = encodeHexLowerCase(crypto.getRandomValues(new Uint8Array(16))); -// const key = await generateScryptKey(password.normalize("NFKC"), salt); -// return `s2:${salt}:${encodeHexLowerCase(key)}`; -// } -// async verify(hash: string, password: string): Promise { -// const parts = hash.split(":"); -// if (parts.length === 2) { -// const [salt, key] = parts; -// const targetKey = await generateScryptKey( -// password.normalize("NFKC"), -// salt, -// 8, -// ); -// const result = constantTimeEqual(targetKey, decodeHex(key)); -// return result; -// } -// if (parts.length !== 3) return false; -// const [version, salt, key] = parts; -// if (version === "s2") { -// const targetKey = await generateScryptKey( -// password.normalize("NFKC"), -// salt, -// ); -// return constantTimeEqual(targetKey, decodeHex(key)); -// } -// return false; -// } -// } - -// export interface PasswordHashingAlgorithm { -// hash(password: string): Promise; -// verify(hash: string, password: string): Promise; -// } diff --git a/packages/server/src/lib/scrypt/index.ts b/packages/server/src/lib/scrypt/index.ts deleted file mode 100644 index 8337712ea..000000000 --- a/packages/server/src/lib/scrypt/index.ts +++ /dev/null @@ -1 +0,0 @@ -// diff --git a/packages/server/src/monitoring/utils.ts b/packages/server/src/monitoring/utils.ts index 561cc8355..147ade0ad 100644 --- a/packages/server/src/monitoring/utils.ts +++ b/packages/server/src/monitoring/utils.ts @@ -73,7 +73,7 @@ export const readStatsFile = async ( const filePath = `${MONITORING_PATH}/${appName}/${statType}.json`; const data = await promises.readFile(filePath, "utf-8"); return JSON.parse(data); - } catch (error) { + } catch (_error) { return []; } }; @@ -108,7 +108,7 @@ export const readLastValueStatsFile = async ( const data = await promises.readFile(filePath, "utf-8"); const stats = JSON.parse(data); return stats[stats.length - 1] || null; - } catch (error) { + } catch (_error) { return null; } }; diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 1e2b569f4..78df14aa4 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -12,8 +12,8 @@ import { IS_CLOUD } from "../constants"; export type User = typeof users_temp.$inferSelect; export const createInvitation = async ( - input: typeof apiCreateUserInvitation._type, - adminId: string, + _input: typeof apiCreateUserInvitation._type, + _adminId: string, ) => { // await db.transaction(async (tx) => { // const result = await tx @@ -83,8 +83,8 @@ export const updateUser = async (userId: string, userData: Partial) => { }; export const updateAdminById = async ( - adminId: string, - adminData: Partial, + _adminId: string, + _adminData: Partial, ) => { // const admin = await db // .update(admins) @@ -102,8 +102,6 @@ export const isAdminPresent = async () => { where: eq(member.role, "owner"), }); - console.log("admin", admin); - if (!admin) { return false; } diff --git a/packages/server/src/services/docker.ts b/packages/server/src/services/docker.ts index 597c03fa1..a4a3b0b5e 100644 --- a/packages/server/src/services/docker.ts +++ b/packages/server/src/services/docker.ts @@ -98,7 +98,7 @@ export const getConfig = async ( const config = JSON.parse(stdout); return config; - } catch (error) {} + } catch (_error) {} }; export const getContainersByAppNameMatch = async ( @@ -156,7 +156,7 @@ export const getContainersByAppNameMatch = async ( }); return containers || []; - } catch (error) {} + } catch (_error) {} return []; }; @@ -214,7 +214,7 @@ export const getStackContainersByAppName = async ( }); return containers || []; - } catch (error) {} + } catch (_error) {} return []; }; @@ -274,7 +274,7 @@ export const getServiceContainersByAppName = async ( }); return containers || []; - } catch (error) {} + } catch (_error) {} return []; }; @@ -325,7 +325,7 @@ export const getContainersByAppLabel = async ( }); return containers || []; - } catch (error) {} + } catch (_error) {} return []; }; @@ -344,7 +344,7 @@ export const containerRestart = async (containerId: string) => { const config = JSON.parse(stdout); return config; - } catch (error) {} + } catch (_error) {} }; export const getSwarmNodes = async (serverId?: string) => { @@ -373,7 +373,7 @@ export const getSwarmNodes = async (serverId?: string) => { .split("\n") .map((line) => JSON.parse(line)); return nodesArray; - } catch (error) {} + } catch (_error) {} }; export const getNodeInfo = async (nodeId: string, serverId?: string) => { @@ -399,7 +399,7 @@ export const getNodeInfo = async (nodeId: string, serverId?: string) => { const nodeInfo = JSON.parse(stdout); return nodeInfo; - } catch (error) {} + } catch (_error) {} }; export const getNodeApplications = async (serverId?: string) => { @@ -431,7 +431,7 @@ export const getNodeApplications = async (serverId?: string) => { .filter((service) => !service.Name.startsWith("dokploy-")); return appArray; - } catch (error) {} + } catch (_error) {} }; export const getApplicationInfo = async ( @@ -464,5 +464,5 @@ export const getApplicationInfo = async ( .map((line) => JSON.parse(line)); return appArray; - } catch (error) {} + } catch (_error) {} }; diff --git a/packages/server/src/services/github.ts b/packages/server/src/services/github.ts index deb5f3fa2..19deb2b24 100644 --- a/packages/server/src/services/github.ts +++ b/packages/server/src/services/github.ts @@ -119,7 +119,7 @@ export const issueCommentExists = async ({ comment_id: comment_id, }); return true; - } catch (error) { + } catch (_error) { return false; } }; diff --git a/packages/server/src/services/mount.ts b/packages/server/src/services/mount.ts index 55557ea0e..836feacec 100644 --- a/packages/server/src/services/mount.ts +++ b/packages/server/src/services/mount.ts @@ -211,7 +211,7 @@ export const deleteFileMount = async (mountId: string) => { } else { await removeFileOrDirectory(fullPath); } - } catch (error) {} + } catch (_error) {} }; export const getBaseFilesPath = async (mountId: string) => { diff --git a/packages/server/src/services/preview-deployment.ts b/packages/server/src/services/preview-deployment.ts index 775621773..a1ffca4ba 100644 --- a/packages/server/src/services/preview-deployment.ts +++ b/packages/server/src/services/preview-deployment.ts @@ -103,7 +103,7 @@ export const removePreviewDeployment = async (previewDeploymentId: string) => { for (const operation of cleanupOperations) { try { await operation(); - } catch (error) {} + } catch (_error) {} } return deployment[0]; } catch (error) { diff --git a/packages/server/src/setup/monitoring-setup.ts b/packages/server/src/setup/monitoring-setup.ts index afadb6c10..75b9a928f 100644 --- a/packages/server/src/setup/monitoring-setup.ts +++ b/packages/server/src/setup/monitoring-setup.ts @@ -66,7 +66,7 @@ export const setupMonitoring = async (serverId: string) => { await container.inspect(); await container.remove({ force: true }); console.log("Removed existing container"); - } catch (error) { + } catch (_error) { // Container doesn't exist, continue } @@ -135,7 +135,7 @@ export const setupWebMonitoring = async (userId: string) => { await container.inspect(); await container.remove({ force: true }); console.log("Removed existing container"); - } catch (error) {} + } catch (_error) {} await docker.createContainer(settings); const newContainer = docker.getContainer(containerName); diff --git a/packages/server/src/setup/postgres-setup.ts b/packages/server/src/setup/postgres-setup.ts index b5794c2b2..55361b689 100644 --- a/packages/server/src/setup/postgres-setup.ts +++ b/packages/server/src/setup/postgres-setup.ts @@ -56,7 +56,7 @@ export const initializePostgres = async () => { }); console.log("Postgres Started ✅"); - } catch (error) { + } catch (_error) { await docker.createService(settings); console.log("Postgres Not Found: Starting ✅"); } diff --git a/packages/server/src/setup/redis-setup.ts b/packages/server/src/setup/redis-setup.ts index 1c3b545a5..774e41927 100644 --- a/packages/server/src/setup/redis-setup.ts +++ b/packages/server/src/setup/redis-setup.ts @@ -52,7 +52,7 @@ export const initializeRedis = async () => { ...settings, }); console.log("Redis Started ✅"); - } catch (error) { + } catch (_error) { await docker.createService(settings); console.log("Redis Not Found: Starting ✅"); } diff --git a/packages/server/src/setup/server-audit.ts b/packages/server/src/setup/server-audit.ts index df00e9a74..b9283c313 100644 --- a/packages/server/src/setup/server-audit.ts +++ b/packages/server/src/setup/server-audit.ts @@ -89,7 +89,7 @@ export const serverAudit = async (serverId: string) => { .on("data", (data: string) => { output += data; }) - .stderr.on("data", (data) => {}); + .stderr.on("data", (_data) => {}); }); }) .on("error", (err) => { diff --git a/packages/server/src/setup/server-validate.ts b/packages/server/src/setup/server-validate.ts index 4ca21df85..c86206b61 100644 --- a/packages/server/src/setup/server-validate.ts +++ b/packages/server/src/setup/server-validate.ts @@ -128,7 +128,7 @@ export const serverValidate = async (serverId: string) => { .on("data", (data: string) => { output += data; }) - .stderr.on("data", (data) => {}); + .stderr.on("data", (_data) => {}); }); }) .on("error", (err) => { diff --git a/packages/server/src/setup/setup.ts b/packages/server/src/setup/setup.ts index c59877022..eeef32dd2 100644 --- a/packages/server/src/setup/setup.ts +++ b/packages/server/src/setup/setup.ts @@ -18,7 +18,7 @@ export const dockerSwarmInitialized = async () => { await docker.swarmInspect(); return true; - } catch (e) { + } catch (_e) { return false; } }; @@ -41,7 +41,7 @@ export const dockerNetworkInitialized = async () => { try { await docker.getNetwork("dokploy-network").inspect(); return true; - } catch (e) { + } catch (_e) { return false; } }; diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 1d60e577c..4fb570f2d 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -127,7 +127,7 @@ export const initializeTraefik = async ({ }); console.log("Traefik Started ✅"); - } catch (error) { + } catch (_error) { await docker.createService(settings); console.log("Traefik Not Found: Starting ✅"); } diff --git a/packages/server/src/types/with.ts b/packages/server/src/types/with.ts index c4826f734..467020a21 100644 --- a/packages/server/src/types/with.ts +++ b/packages/server/src/types/with.ts @@ -36,7 +36,7 @@ type AnyObj = Record; type ZodObj = { [key in keyof T]: z.ZodType; }; -const zObject = (arg: ZodObj) => z.object(arg); +const _zObject = (arg: ZodObj) => z.object(arg); // const goodDogScheme = zObject({ // // prueba: schema.selectDatabaseSchema, diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index 5cff7f837..bbd32cff2 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -32,7 +32,7 @@ class LogRotationManager { // return setting?.enableLogRotation ?? false; } - private async setStateInDB(active: boolean): Promise { + private async setStateInDB(_active: boolean): Promise { // const admin = await db.query.admins.findFirst({}); // if (!admin) { // return; diff --git a/packages/server/src/utils/backups/utils.ts b/packages/server/src/utils/backups/utils.ts index 0d78ff961..c76f79626 100644 --- a/packages/server/src/utils/backups/utils.ts +++ b/packages/server/src/utils/backups/utils.ts @@ -28,7 +28,7 @@ export const removeScheduleBackup = (backupId: string) => { }; export const getS3Credentials = (destination: Destination) => { - const { accessKey, secretAccessKey, bucket, region, endpoint, provider } = + const { accessKey, secretAccessKey, region, endpoint, provider } = destination; const rcloneFlags = [ `--s3-access-key-id=${accessKey}`, diff --git a/packages/server/src/utils/builders/compose.ts b/packages/server/src/utils/builders/compose.ts index cbf951c73..19e7d1529 100644 --- a/packages/server/src/utils/builders/compose.ts +++ b/packages/server/src/utils/builders/compose.ts @@ -98,8 +98,7 @@ export const getBuildComposeCommand = async ( logPath: string, ) => { const { COMPOSE_PATH } = paths(true); - const { sourceType, appName, mounts, composeType, domains, composePath } = - compose; + const { sourceType, appName, mounts, composeType, domains } = compose; const command = createCommand(compose); const envCommand = getCreateEnvFileCommand(compose); const projectPath = join(COMPOSE_PATH, compose.appName, "code"); diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index d67482755..d777b1a36 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -197,7 +197,7 @@ export const mechanizeDockerContainer = async ( ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1, }, }); - } catch (error) { + } catch (_error) { await docker.createService(settings); } }; diff --git a/packages/server/src/utils/builders/nixpacks.ts b/packages/server/src/utils/builders/nixpacks.ts index 56560e4e2..c13f82a56 100644 --- a/packages/server/src/utils/builders/nixpacks.ts +++ b/packages/server/src/utils/builders/nixpacks.ts @@ -91,7 +91,7 @@ export const getNixpacksCommand = ( application: ApplicationNested, logPath: string, ) => { - const { env, appName, publishDirectory, serverId } = application; + const { env, appName, publishDirectory } = application; const buildAppDirectory = getBuildAppDirectory(application); const buildContainerId = `${appName}-${nanoid(10)}`; diff --git a/packages/server/src/utils/databases/mariadb.ts b/packages/server/src/utils/databases/mariadb.ts index d1b41fc33..ead5a618a 100644 --- a/packages/server/src/utils/databases/mariadb.ts +++ b/packages/server/src/utils/databases/mariadb.ts @@ -98,7 +98,7 @@ export const buildMariadb = async (mariadb: MariadbNested) => { version: Number.parseInt(inspect.Version.Index), ...settings, }); - } catch (error) { + } catch (_error) { await docker.createService(settings); } }; diff --git a/packages/server/src/utils/databases/mongo.ts b/packages/server/src/utils/databases/mongo.ts index 5af58eef7..ace9c9721 100644 --- a/packages/server/src/utils/databases/mongo.ts +++ b/packages/server/src/utils/databases/mongo.ts @@ -152,7 +152,7 @@ ${command ?? "wait $MONGOD_PID"}`; version: Number.parseInt(inspect.Version.Index), ...settings, }); - } catch (error) { + } catch (_error) { await docker.createService(settings); } }; diff --git a/packages/server/src/utils/databases/mysql.ts b/packages/server/src/utils/databases/mysql.ts index 5a6911771..de28cfe6b 100644 --- a/packages/server/src/utils/databases/mysql.ts +++ b/packages/server/src/utils/databases/mysql.ts @@ -104,7 +104,7 @@ export const buildMysql = async (mysql: MysqlNested) => { version: Number.parseInt(inspect.Version.Index), ...settings, }); - } catch (error) { + } catch (_error) { await docker.createService(settings); } }; diff --git a/packages/server/src/utils/databases/redis.ts b/packages/server/src/utils/databases/redis.ts index 724069a17..aef862802 100644 --- a/packages/server/src/utils/databases/redis.ts +++ b/packages/server/src/utils/databases/redis.ts @@ -95,7 +95,7 @@ export const buildRedis = async (redis: RedisNested) => { version: Number.parseInt(inspect.Version.Index), ...settings, }); - } catch (error) { + } catch (_error) { await docker.createService(settings); } }; diff --git a/packages/server/src/utils/docker/domain.ts b/packages/server/src/utils/docker/domain.ts index 8a1b0608f..c4ced3f44 100644 --- a/packages/server/src/utils/docker/domain.ts +++ b/packages/server/src/utils/docker/domain.ts @@ -109,7 +109,7 @@ export const loadDockerComposeRemote = async ( if (!stdout) return null; const parsedConfig = load(stdout) as ComposeSpecification; return parsedConfig; - } catch (err) { + } catch (_err) { return null; } }; diff --git a/packages/server/src/utils/docker/utils.ts b/packages/server/src/utils/docker/utils.ts index 062e07226..71b7e4aac 100644 --- a/packages/server/src/utils/docker/utils.ts +++ b/packages/server/src/utils/docker/utils.ts @@ -100,7 +100,7 @@ export const containerExists = async (containerName: string) => { try { await container.inspect(); return true; - } catch (error) { + } catch (_error) { return false; } }; @@ -240,7 +240,7 @@ export const startServiceRemote = async (serverId: string, appName: string) => { export const removeService = async ( appName: string, serverId?: string | null, - deleteVolumes = false, + _deleteVolumes = false, ) => { try { const command = `docker service rm ${appName}`; diff --git a/packages/server/src/utils/gpu-setup.ts b/packages/server/src/utils/gpu-setup.ts index 6a6611b4a..a815a00cc 100644 --- a/packages/server/src/utils/gpu-setup.ts +++ b/packages/server/src/utils/gpu-setup.ts @@ -34,7 +34,7 @@ export async function checkGPUStatus(serverId?: string): Promise { ...gpuInfo, ...cudaInfo, }; - } catch (error) { + } catch (_error) { return { driverInstalled: false, driverVersion: undefined, @@ -315,7 +315,7 @@ const setupLocalServer = async (daemonConfig: any) => { try { await execAsync(setupCommands); - } catch (error) { + } catch (_error) { throw new Error( "Failed to configure GPU support. Please ensure you have sudo privileges and try again.", ); diff --git a/packages/server/src/utils/process/execAsync.ts b/packages/server/src/utils/process/execAsync.ts index 19a16ac1e..aee1e821a 100644 --- a/packages/server/src/utils/process/execAsync.ts +++ b/packages/server/src/utils/process/execAsync.ts @@ -27,7 +27,7 @@ export const execAsyncRemote = async ( throw err; } stream - .on("close", (code: number, signal: string) => { + .on("close", (code: number, _signal: string) => { conn.end(); if (code === 0) { resolve({ stdout, stderr }); diff --git a/packages/server/src/utils/providers/bitbucket.ts b/packages/server/src/utils/providers/bitbucket.ts index 7059e65f7..dd98a93bd 100644 --- a/packages/server/src/utils/providers/bitbucket.ts +++ b/packages/server/src/utils/providers/bitbucket.ts @@ -176,7 +176,6 @@ export const getBitbucketCloneCommand = async ( bitbucketBranch, bitbucketId, serverId, - bitbucket, } = entity; if (!serverId) { diff --git a/packages/server/src/utils/providers/git.ts b/packages/server/src/utils/providers/git.ts index 8f8a38301..c26af3af2 100644 --- a/packages/server/src/utils/providers/git.ts +++ b/packages/server/src/utils/providers/git.ts @@ -320,7 +320,7 @@ export const cloneGitRawRepository = async (entity: { outputPath, "--progress", ], - (data) => {}, + (_data) => {}, { env: { ...process.env, diff --git a/packages/server/src/utils/providers/gitlab.ts b/packages/server/src/utils/providers/gitlab.ts index a2c4b8ccb..c380a9203 100644 --- a/packages/server/src/utils/providers/gitlab.ts +++ b/packages/server/src/utils/providers/gitlab.ts @@ -162,8 +162,6 @@ export const getGitlabCloneCommand = async ( ) => { const { appName, - gitlabRepository, - gitlabOwner, gitlabPathNamespace, gitlabBranch, gitlabId, @@ -328,14 +326,7 @@ export const getGitlabBranches = async (input: { }; export const cloneRawGitlabRepository = async (entity: Compose) => { - const { - appName, - gitlabRepository, - gitlabOwner, - gitlabBranch, - gitlabId, - gitlabPathNamespace, - } = entity; + const { appName, gitlabBranch, gitlabId, gitlabPathNamespace } = entity; if (!gitlabId) { throw new TRPCError({ diff --git a/packages/server/src/utils/traefik/application.ts b/packages/server/src/utils/traefik/application.ts index 4434d8585..61150abf6 100644 --- a/packages/server/src/utils/traefik/application.ts +++ b/packages/server/src/utils/traefik/application.ts @@ -67,7 +67,7 @@ export const removeTraefikConfig = async ( if (fs.existsSync(configPath)) { await fs.promises.unlink(configPath); } - } catch (error) {} + } catch (_error) {} }; export const removeTraefikConfigRemote = async ( @@ -78,7 +78,7 @@ export const removeTraefikConfigRemote = async ( const { DYNAMIC_TRAEFIK_PATH } = paths(true); const configPath = path.join(DYNAMIC_TRAEFIK_PATH, `${appName}.yml`); await execAsyncRemote(serverId, `rm ${configPath}`); - } catch (error) {} + } catch (_error) {} }; export const loadOrCreateConfig = (appName: string): FileConfig => { @@ -110,7 +110,7 @@ export const loadOrCreateConfigRemote = async ( http: { routers: {}, services: {} }, }; return parsedConfig; - } catch (err) { + } catch (_err) { return fileConfig; } }; @@ -132,7 +132,7 @@ export const readRemoteConfig = async (serverId: string, appName: string) => { const { stdout } = await execAsyncRemote(serverId, `cat ${configPath}`); if (!stdout) return null; return stdout; - } catch (err) { + } catch (_err) { return null; } }; diff --git a/packages/server/src/verification/send-verification-email.tsx b/packages/server/src/verification/send-verification-email.tsx new file mode 100644 index 000000000..0b3b3bdcc --- /dev/null +++ b/packages/server/src/verification/send-verification-email.tsx @@ -0,0 +1,49 @@ +import { + sendDiscordNotification, + sendEmailNotification, +} from "../utils/notifications/utils"; +export const sendVerificationEmail = async (email: string, url: string) => { + await sendEmailNotification( + { + fromAddress: process.env.SMTP_FROM_ADDRESS || "", + toAddresses: [email], + smtpServer: process.env.SMTP_SERVER || "", + smtpPort: Number(process.env.SMTP_PORT), + username: process.env.SMTP_USERNAME || "", + password: process.env.SMTP_PASSWORD || "", + }, + "Confirm your email | Dokploy", + ` + Welcome to Dokploy! + Please confirm your email by clicking the link below: + + Confirm Email + + `, + ); + + return true; +}; + +export const sendDiscordNotificationWelcome = async (email: string) => { + await sendDiscordNotification( + { + webhookUrl: process.env.DISCORD_WEBHOOK_URL || "", + }, + { + title: "New User Registered", + color: 0x00ff00, + fields: [ + { + name: "Email", + value: email, + inline: true, + }, + ], + timestamp: new Date(), + footer: { + text: "Dokploy User Registration Notification", + }, + }, + ); +}; From b00c12965ae38be0968e2342ee8bf76784702df9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 21:09:21 -0600 Subject: [PATCH 084/126] refactor: update reset password and authentication flows This commit removes several authentication-related components and simplifies the password reset process: - Removed login-2fa component - Deleted confirm-email page - Updated reset password logic to use Drizzle ORM directly - Removed unused authentication-related functions - Simplified server-side authentication routes --- .../server/update-server-config.test.ts | 12 - apps/dokploy/components/auth/login-2fa.tsx | 131 ----------- .../paid/servers/show-paid-monitoring.tsx | 2 +- .../dashboard/settings/profile/enable-2fa.tsx | 6 + .../settings/web-server/update-server-ip.tsx | 2 +- apps/dokploy/pages/api/stripe/webhook.ts | 2 +- apps/dokploy/pages/confirm-email.tsx | 96 -------- apps/dokploy/pages/index.tsx | 1 + apps/dokploy/pages/send-reset-password.tsx | 5 +- apps/dokploy/reset-password.ts | 13 +- apps/dokploy/server/api/routers/auth.ts | 207 +++++------------- apps/dokploy/server/api/routers/server.ts | 59 +++++ apps/dokploy/server/api/routers/user.ts | 59 +++++ packages/server/src/services/admin.ts | 23 +- 14 files changed, 214 insertions(+), 404 deletions(-) delete mode 100644 apps/dokploy/components/auth/login-2fa.tsx delete mode 100644 apps/dokploy/pages/confirm-email.tsx diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index 7e4a3c82a..c72d72542 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -51,20 +51,9 @@ const baseAdmin: User = { serversQuantity: 0, stripeCustomerId: "", stripeSubscriptionId: "", - accessedProjects: [], - accessedServices: [], banExpires: new Date(), banned: true, banReason: "", - canAccessToAPI: false, - canCreateProjects: false, - canDeleteProjects: false, - canDeleteServices: false, - canAccessToDocker: false, - canAccessToSSHKeys: false, - canCreateServices: false, - canAccessToTraefikFiles: false, - canAccessToGitProviders: false, email: "", expirationDate: "", id: "", @@ -73,7 +62,6 @@ const baseAdmin: User = { createdAt2: new Date().toISOString(), emailVerified: false, image: "", - token: "", updatedAt: new Date(), twoFactorEnabled: false, }; diff --git a/apps/dokploy/components/auth/login-2fa.tsx b/apps/dokploy/components/auth/login-2fa.tsx deleted file mode 100644 index 634f28146..000000000 --- a/apps/dokploy/components/auth/login-2fa.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { Button } from "@/components/ui/button"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; - -import { CardTitle } from "@/components/ui/card"; -import { - InputOTP, - InputOTPGroup, - InputOTPSlot, -} from "@/components/ui/input-otp"; -import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { REGEXP_ONLY_DIGITS } from "input-otp"; -import { AlertTriangle } from "lucide-react"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; - -const Login2FASchema = z.object({ - pin: z.string().min(6, { - message: "Pin is required", - }), -}); - -type Login2FA = z.infer; - -interface Props { - authId: string; -} - -export const Login2FA = ({ authId }: Props) => { - const { push } = useRouter(); - - const { mutateAsync, isLoading, isError, error } = - api.auth.verifyLogin2FA.useMutation(); - - const form = useForm({ - defaultValues: { - pin: "", - }, - resolver: zodResolver(Login2FASchema), - }); - - useEffect(() => { - form.reset({ - pin: "", - }); - }, [form, form.reset, form.formState.isSubmitSuccessful]); - - const onSubmit = async (data: Login2FA) => { - await mutateAsync({ - pin: data.pin, - id: authId, - }) - .then(() => { - toast.success("Signin successfully", { - duration: 2000, - }); - - push("/dashboard/projects"); - }) - .catch(() => { - toast.error("Signin failed", { - duration: 2000, - }); - }); - }; - return ( - - - {isError && ( -
- - - {error?.message} - -
- )} - 2FA Login - - ( - - Pin - -
- - - - - - - - - - -
-
- - Please enter the 6 digits code provided by your authenticator - app. - - -
- )} - /> - - - - ); -}; diff --git a/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx b/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx index e299cfb38..e92ce03fc 100644 --- a/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx +++ b/apps/dokploy/components/dashboard/monitoring/paid/servers/show-paid-monitoring.tsx @@ -72,7 +72,7 @@ export const ShowPaidMonitoring = ({ data, isLoading, error: queryError, - } = api.user.getServerMetrics.useQuery( + } = api.server.getServerMetrics.useQuery( { url: BASE_URL, token, diff --git a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx index 3ececb649..28fba67df 100644 --- a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx @@ -44,6 +44,12 @@ const PinSchema = z.object({ }), }); +type TwoFactorSetupData = { + qrCodeUrl: string; + secret: string; + totpURI: string; +}; + type PasswordForm = z.infer; type PinForm = z.infer; diff --git a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx index 3a511d8ea..afe1e4c36 100644 --- a/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx +++ b/apps/dokploy/components/dashboard/settings/web-server/update-server-ip.tsx @@ -80,7 +80,7 @@ export const UpdateServerIp = ({ children }: Props) => { }) .then(async () => { toast.success("Server IP Updated"); - await utils.admin.one.invalidate(); + await utils.user.get.invalidate(); setIsOpen(false); }) .catch(() => { diff --git a/apps/dokploy/pages/api/stripe/webhook.ts b/apps/dokploy/pages/api/stripe/webhook.ts index 592803b15..9e8c9da5e 100644 --- a/apps/dokploy/pages/api/stripe/webhook.ts +++ b/apps/dokploy/pages/api/stripe/webhook.ts @@ -1,7 +1,7 @@ import { buffer } from "node:stream/consumers"; import { db } from "@/server/db"; import { organization, server, users_temp } from "@/server/db/schema"; -import { findUserById, type Server } from "@dokploy/server"; +import { type Server, findUserById } from "@dokploy/server"; import { asc, eq } from "drizzle-orm"; import type { NextApiRequest, NextApiResponse } from "next"; import Stripe from "stripe"; diff --git a/apps/dokploy/pages/confirm-email.tsx b/apps/dokploy/pages/confirm-email.tsx deleted file mode 100644 index 2910a2677..000000000 --- a/apps/dokploy/pages/confirm-email.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; -import { Logo } from "@/components/shared/logo"; -import { CardDescription, CardTitle } from "@/components/ui/card"; -import { db } from "@/server/db"; -import { auth } from "@/server/db/schema"; -import { IS_CLOUD, updateAuthById } from "@dokploy/server"; -import { isBefore } from "date-fns"; -import { eq } from "drizzle-orm"; -import type { GetServerSidePropsContext } from "next"; -import Link from "next/link"; -import type { ReactElement } from "react"; - -export default function Home() { - return ( -
-
- - - Dokploy - - Email Confirmed - - Congratulations, your email is confirmed. - -
- - Click here to login - -
-
-
- ); -} - -Home.getLayout = (page: ReactElement) => { - return {page}; -}; -export async function getServerSideProps(context: GetServerSidePropsContext) { - if (!IS_CLOUD) { - return { - redirect: { - permanent: true, - destination: "/", - }, - }; - } - const { token } = context.query; - - if (typeof token !== "string") { - return { - redirect: { - permanent: true, - destination: "/", - }, - }; - } - - const authR = await db.query.auth.findFirst({ - where: eq(auth.confirmationToken, token), - }); - - if ( - !authR || - authR?.confirmationToken === null || - authR?.confirmationExpiresAt === null - ) { - return { - redirect: { - permanent: true, - destination: "/", - }, - }; - } - - const isExpired = isBefore(new Date(authR.confirmationExpiresAt), new Date()); - - if (isExpired) { - return { - redirect: { - permanent: true, - destination: "/", - }, - }; - } - - await updateAuthById(authR.id, { - confirmationToken: null, - confirmationExpiresAt: null, - }); - - return { - props: { - token: authR.confirmationToken, - }, - }; -} diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 783ec651d..4a85952e6 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -85,6 +85,7 @@ export default function Home({ IS_CLOUD }: Props) { return; } + // @ts-ignore if (data?.twoFactorRedirect as boolean) { setTwoFactorCode(""); setIsTwoFactor(true); diff --git a/apps/dokploy/pages/send-reset-password.tsx b/apps/dokploy/pages/send-reset-password.tsx index 0ea59cf85..8f6902f6e 100644 --- a/apps/dokploy/pages/send-reset-password.tsx +++ b/apps/dokploy/pages/send-reset-password.tsx @@ -1,4 +1,3 @@ -import { Login2FA } from "@/components/auth/login-2fa"; import { OnboardingLayout } from "@/components/layouts/onboarding-layout"; import { AlertBlock } from "@/components/shared/alert-block"; import { Logo } from "@/components/shared/logo"; @@ -126,9 +125,7 @@ export default function Home() {
- ) : ( - - )} + ) : null}
diff --git a/apps/dokploy/reset-password.ts b/apps/dokploy/reset-password.ts index 43b11fdf6..32cab4334 100644 --- a/apps/dokploy/reset-password.ts +++ b/apps/dokploy/reset-password.ts @@ -1,6 +1,8 @@ import { findAdmin } from "@dokploy/server"; -import { updateAuthById } from "@dokploy/server"; import { generateRandomPassword } from "@dokploy/server"; +import { db } from "@dokploy/server/db"; +import { account } from "@dokploy/server/db/schema"; +import { eq } from "drizzle-orm"; (async () => { try { @@ -8,9 +10,12 @@ import { generateRandomPassword } from "@dokploy/server"; const result = await findAdmin(); - const update = await updateAuthById(result.authId, { - password: randomPassword.hashedPassword, - }); + const update = await db + .update(account) + .set({ + password: randomPassword.hashedPassword, + }) + .where(eq(account.userId, result.userId)); if (update) { console.log("Password reset successful"); diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 31a50c67b..1a6c046e3 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -14,13 +14,11 @@ import { IS_CLOUD, findUserById, getUserByToken, - sendDiscordNotification, sendEmailNotification, validateRequest, } from "@dokploy/server"; import { TRPCError } from "@trpc/server"; import * as bcrypt from "bcrypt"; -import { isBefore } from "date-fns"; import { and, eq } from "drizzle-orm"; import { nanoid } from "nanoid"; import { z } from "zod"; @@ -321,157 +319,64 @@ export const authRouter = createTRPCRouter({ `, ); }), - - resetPassword: publicProcedure - .input( - z.object({ - resetPasswordToken: z.string().min(1), - password: z.string().min(1), - }), - ) - .mutation(async ({ input }) => { - if (!IS_CLOUD) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "This feature is only available in the cloud version", - }); - } - const authR = await db.query.auth.findFirst({ - where: eq(auth.resetPasswordToken, input.resetPasswordToken), - }); - - if (!authR || authR.resetPasswordExpiresAt === null) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Token not found", - }); - } - - const isExpired = isBefore( - new Date(authR.resetPasswordExpiresAt), - new Date(), - ); - - if (isExpired) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Token expired", - }); - } - - await updateAuthById(authR.id, { - resetPasswordExpiresAt: null, - resetPasswordToken: null, - password: bcrypt.hashSync(input.password, 10), - }); - - return true; - }), - confirmEmail: adminProcedure - .input( - z.object({ - confirmationToken: z.string().min(1), - }), - ) - .mutation(async ({ input }) => { - if (!IS_CLOUD) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Functionality not available in cloud version", - }); - } - const authR = await db.query.auth.findFirst({ - where: eq(auth.confirmationToken, input.confirmationToken), - }); - if (!authR || authR.confirmationExpiresAt === null) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Token not found", - }); - } - if (authR.confirmationToken !== input.confirmationToken) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Confirmation Token not found", - }); - } - - const isExpired = isBefore( - new Date(authR.confirmationExpiresAt), - new Date(), - ); - - if (isExpired) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "Confirmation Token expired", - }); - } - 1; - await updateAuthById(authR.id, { - confirmationToken: null, - confirmationExpiresAt: null, - }); - return true; - }), }); -export const sendVerificationEmail = async (authId: string) => { - const token = nanoid(); - const result = await updateAuthById(authId, { - confirmationToken: token, - confirmationExpiresAt: new Date( - new Date().getTime() + 24 * 60 * 60 * 1000, - ).toISOString(), - }); +// export const sendVerificationEmail = async (authId: string) => { +// const token = nanoid(); +// const result = await updateAuthById(authId, { +// confirmationToken: token, +// confirmationExpiresAt: new Date( +// new Date().getTime() + 24 * 60 * 60 * 1000, +// ).toISOString(), +// }); - if (!result) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "User not found", - }); - } - await sendEmailNotification( - { - fromAddress: process.env.SMTP_FROM_ADDRESS || "", - toAddresses: [result?.email], - smtpServer: process.env.SMTP_SERVER || "", - smtpPort: Number(process.env.SMTP_PORT), - username: process.env.SMTP_USERNAME || "", - password: process.env.SMTP_PASSWORD || "", - }, - "Confirm your email | Dokploy", - ` - Welcome to Dokploy! - Please confirm your email by clicking the link below: - - Confirm Email - - `, - ); +// if (!result) { +// throw new TRPCError({ +// code: "BAD_REQUEST", +// message: "User not found", +// }); +// } +// await sendEmailNotification( +// { +// fromAddress: process.env.SMTP_FROM_ADDRESS || "", +// toAddresses: [result?.email], +// smtpServer: process.env.SMTP_SERVER || "", +// smtpPort: Number(process.env.SMTP_PORT), +// username: process.env.SMTP_USERNAME || "", +// password: process.env.SMTP_PASSWORD || "", +// }, +// "Confirm your email | Dokploy", +// ` +// Welcome to Dokploy! +// Please confirm your email by clicking the link below: +// +// Confirm Email +// +// `, +// ); - return true; -}; +// return true; +// }; -export const sendDiscordNotificationWelcome = async (newAdmin: Auth) => { - await sendDiscordNotification( - { - webhookUrl: process.env.DISCORD_WEBHOOK_URL || "", - }, - { - title: "New User Registered", - color: 0x00ff00, - fields: [ - { - name: "Email", - value: newAdmin.email, - inline: true, - }, - ], - timestamp: newAdmin.createdAt, - footer: { - text: "Dokploy User Registration Notification", - }, - }, - ); -}; +// export const sendDiscordNotificationWelcome = async (newAdmin: Auth) => { +// await sendDiscordNotification( +// { +// webhookUrl: process.env.DISCORD_WEBHOOK_URL || "", +// }, +// { +// title: "New User Registered", +// color: 0x00ff00, +// fields: [ +// { +// name: "Email", +// value: newAdmin.email, +// inline: true, +// }, +// ], +// timestamp: newAdmin.createdAt, +// footer: { +// text: "Dokploy User Registration Notification", +// }, +// }, +// ); +// }; diff --git a/apps/dokploy/server/api/routers/server.ts b/apps/dokploy/server/api/routers/server.ts index 1ebb161a4..1a9ebc0ac 100644 --- a/apps/dokploy/server/api/routers/server.ts +++ b/apps/dokploy/server/api/routers/server.ts @@ -37,6 +37,7 @@ import { import { TRPCError } from "@trpc/server"; import { observable } from "@trpc/server/observable"; import { and, desc, eq, getTableColumns, isNotNull, sql } from "drizzle-orm"; +import { z } from "zod"; export const serverRouter = createTRPCRouter({ create: protectedProcedure @@ -378,4 +379,62 @@ export const serverRouter = createTRPCRouter({ const ip = await getPublicIpWithFallback(); return ip; }), + getServerMetrics: protectedProcedure + .input( + z.object({ + url: z.string(), + token: z.string(), + dataPoints: z.string(), + }), + ) + .query(async ({ input }) => { + try { + const url = new URL(input.url); + url.searchParams.append("limit", input.dataPoints); + const response = await fetch(url.toString(), { + headers: { + Authorization: `Bearer ${input.token}`, + }, + }); + if (!response.ok) { + throw new Error( + `Error ${response.status}: ${response.statusText}. Ensure the container is running and this service is included in the monitoring configuration.`, + ); + } + + const data = await response.json(); + if (!Array.isArray(data) || data.length === 0) { + throw new Error( + [ + "No monitoring data available. This could be because:", + "", + "1. You don't have setup the monitoring service, you can do in web server section.", + "2. If you already have setup the monitoring service, wait a few minutes and refresh the page.", + ].join("\n"), + ); + } + return data as { + cpu: string; + cpuModel: string; + cpuCores: number; + cpuPhysicalCores: number; + cpuSpeed: number; + os: string; + distro: string; + kernel: string; + arch: string; + memUsed: string; + memUsedGB: string; + memTotal: string; + uptime: number; + diskUsed: string; + totalDisk: string; + networkIn: string; + networkOut: string; + timestamp: string; + }[]; + } catch (error) { + throw error; + } + }), }); diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 43cffd9e1..5717d59bf 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -143,4 +143,63 @@ export const userRouter = createTRPCRouter({ }, }); }), + + getContainerMetrics: protectedProcedure + .input( + z.object({ + url: z.string(), + token: z.string(), + appName: z.string(), + dataPoints: z.string(), + }), + ) + .query(async ({ input }) => { + try { + if (!input.appName) { + throw new Error( + [ + "No Application Selected:", + "", + "Make Sure to select an application to monitor.", + ].join("\n"), + ); + } + const url = new URL(`${input.url}/metrics/containers`); + url.searchParams.append("limit", input.dataPoints); + url.searchParams.append("appName", input.appName); + const response = await fetch(url.toString(), { + headers: { + Authorization: `Bearer ${input.token}`, + }, + }); + if (!response.ok) { + throw new Error( + `Error ${response.status}: ${response.statusText}. Please verify that the application "${input.appName}" is running and this service is included in the monitoring configuration.`, + ); + } + + const data = await response.json(); + if (!Array.isArray(data) || data.length === 0) { + throw new Error( + [ + `No monitoring data available for "${input.appName}". This could be because:`, + "", + "1. The container was recently started - wait a few minutes for data to be collected", + "2. The container is not running - verify its status", + "3. The service is not included in your monitoring configuration", + ].join("\n"), + ); + } + return data as { + containerId: string; + containerName: string; + containerImage: string; + containerLabels: string; + containerCommand: string; + containerCreated: string; + }[]; + } catch (error) { + throw error; + } + }), }); diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 78df14aa4..099018f2b 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -108,6 +108,23 @@ export const isAdminPresent = async () => { return true; }; +export const findAdmin = async () => { + const admin = await db.query.member.findFirst({ + where: eq(member.role, "owner"), + with: { + user: true, + }, + }); + + if (!admin) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Admin not found", + }); + } + return admin; +}; + export const getUserByToken = async (token: string) => { const user = await db.query.invitation.findFirst({ where: eq(invitation.id, token), @@ -154,8 +171,8 @@ export const getDokployUrl = async () => { } const admin = await findAdmin(); - if (admin.host) { - return `https://${admin.host}`; + if (admin.user.host) { + return `https://${admin.user.host}`; } - return `http://${admin.serverIp}:${process.env.PORT}`; + return `http://${admin.user.serverIp}:${process.env.PORT}`; }; From 0478419f7cb7df083e7fdf146aa19410db81b488 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 22:02:12 -0600 Subject: [PATCH 085/126] refactor: migrate authentication routes to user router and update related components This commit continues the refactoring of authentication-related code by: - Moving authentication routes from `auth.ts` to `user.ts` - Updating import paths and function calls across components - Removing commented-out authentication code - Simplifying user-related queries and mutations - Updating server-side authentication handling --- .../settings/profile/disable-2fa.tsx | 2 +- .../dashboard/settings/profile/enable-2fa.tsx | 2 +- .../settings/profile/generate-token.tsx | 4 +- .../settings/profile/remove-self-account.tsx | 138 ----- .../settings/servers/setup-monitoring.tsx | 2 +- apps/dokploy/components/layouts/side.tsx | 2 +- apps/dokploy/pages/api/[...trpc].ts | 10 +- apps/dokploy/pages/dashboard/projects.tsx | 2 +- .../pages/dashboard/settings/billing.tsx | 2 +- .../pages/dashboard/settings/certificates.tsx | 2 +- .../pages/dashboard/settings/cluster.tsx | 2 +- .../pages/dashboard/settings/destinations.tsx | 2 +- .../dashboard/settings/git-providers.tsx | 2 +- .../pages/dashboard/settings/index.tsx | 2 +- .../dashboard/settings/notifications.tsx | 2 +- .../pages/dashboard/settings/profile.tsx | 15 +- .../pages/dashboard/settings/registry.tsx | 2 +- .../pages/dashboard/settings/server.tsx | 2 +- .../pages/dashboard/settings/servers.tsx | 2 +- .../pages/dashboard/settings/users.tsx | 2 +- apps/dokploy/pages/reset-password.tsx | 85 +-- apps/dokploy/pages/send-reset-password.tsx | 34 +- apps/dokploy/server/api/routers/auth.ts | 550 +++++++++--------- apps/dokploy/server/api/routers/user.ts | 18 +- packages/server/src/lib/auth.ts | 15 +- packages/server/src/services/admin.ts | 65 --- packages/server/src/services/user.ts | 15 +- .../server/src/utils/access-log/handler.ts | 1 + packages/server/src/utils/backups/index.ts | 7 +- .../verification/send-verification-email.tsx | 20 +- 30 files changed, 394 insertions(+), 615 deletions(-) delete mode 100644 apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx diff --git a/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx index ceeb386d7..458bf5632 100644 --- a/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/disable-2fa.tsx @@ -61,7 +61,7 @@ export const Disable2FA = () => { } toast.success("2FA disabled successfully"); - utils.auth.get.invalidate(); + utils.user.get.invalidate(); setIsOpen(false); } catch (_error) { form.setError("password", { diff --git a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx index 28fba67df..f47c8d9c0 100644 --- a/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/enable-2fa.tsx @@ -125,7 +125,7 @@ export const Enable2FA = () => { } toast.success("2FA configured successfully"); - utils.auth.get.invalidate(); + utils.user.get.invalidate(); setIsDialogOpen(false); } catch (error) { if (error instanceof Error) { diff --git a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx index d0299eb6e..4d36ab7a1 100644 --- a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx @@ -17,7 +17,7 @@ export const GenerateToken = () => { const { data, refetch } = api.user.get.useQuery(); const { mutateAsync: generateToken, isLoading: isLoadingToken } = - api.auth.generateToken.useMutation(); + api.user.generateToken.useMutation(); return (
@@ -51,7 +51,7 @@ export const GenerateToken = () => {
diff --git a/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx b/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx deleted file mode 100644 index 935019941..000000000 --- a/apps/dokploy/components/dashboard/settings/profile/remove-self-account.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { AlertBlock } from "@/components/shared/alert-block"; -import { DialogAction } from "@/components/shared/dialog-action"; -import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { - Form, - FormControl, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { api } from "@/utils/api"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useTranslation } from "next-i18next"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; -import { useForm } from "react-hook-form"; -import { toast } from "sonner"; -import { z } from "zod"; - -const profileSchema = z.object({ - password: z.string().min(1, { - message: "Password is required", - }), -}); - -type Profile = z.infer; - -export const RemoveSelfAccount = () => { - const { data } = api.user.get.useQuery(); - const { mutateAsync, isLoading, error, isError } = - api.auth.removeSelfAccount.useMutation(); - const { t } = useTranslation("settings"); - const router = useRouter(); - - const form = useForm({ - defaultValues: { - password: "", - }, - resolver: zodResolver(profileSchema), - }); - - useEffect(() => { - if (data) { - form.reset({ - password: "", - }); - } - form.reset(); - }, [form, form.reset, data]); - - const onSubmit = async (values: Profile) => { - await mutateAsync({ - password: values.password, - }) - .then(async () => { - toast.success("Profile Deleted"); - router.push("/"); - }) - .catch(() => {}); - }; - - return ( -
- -
- -
- Remove Self Account - - If you want to remove your account, you can do it here - -
-
- - {isError && {error?.message}} - -
- e.preventDefault()} - onKeyDown={(e) => { - if (e.key === "Enter") { - e.preventDefault(); - } - }} - className="grid gap-4" - > -
- ( - - {t("settings.profile.password")} - - - - - - )} - /> -
-
- -
- form.handleSubmit(onSubmit)()} - > - - -
-
-
-
-
- ); -}; diff --git a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx index b8c699268..9a49277e2 100644 --- a/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/setup-monitoring.tsx @@ -89,7 +89,7 @@ export const SetupMonitoring = ({ serverId }: Props) => { enabled: !!serverId, }, ) - : api.user.get.useQuery(); + : api.user.getServerMetrics.useQuery(); const url = useUrl(); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 63155f8eb..9c61fb396 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -89,7 +89,7 @@ import { UpdateServerButton } from "./update-server"; import { UserNav } from "./user-nav"; // The types of the queries we are going to use -type AuthQueryOutput = inferRouterOutputs["auth"]["get"]; +type AuthQueryOutput = inferRouterOutputs["user"]["get"]; type SingleNavItem = { isSingle?: true; diff --git a/apps/dokploy/pages/api/[...trpc].ts b/apps/dokploy/pages/api/[...trpc].ts index df85440b9..85ddbb286 100644 --- a/apps/dokploy/pages/api/[...trpc].ts +++ b/apps/dokploy/pages/api/[...trpc].ts @@ -1,17 +1,11 @@ import { appRouter } from "@/server/api/root"; import { createTRPCContext } from "@/server/api/trpc"; -import { validateBearerToken, validateRequest } from "@dokploy/server"; +import { validateRequest } from "@dokploy/server"; import { createOpenApiNextHandler } from "@dokploy/trpc-openapi"; import type { NextApiRequest, NextApiResponse } from "next"; const handler = async (req: NextApiRequest, res: NextApiResponse) => { - let { session, user } = await validateBearerToken(req); - - if (!session) { - const cookieResult = await validateRequest(req, res); - session = cookieResult.session; - user = cookieResult.user; - } + const { session, user } = await validateRequest(req); if (!user || !session) { res.status(401).json({ message: "Unauthorized" }); diff --git a/apps/dokploy/pages/dashboard/projects.tsx b/apps/dokploy/pages/dashboard/projects.tsx index 49427c25b..5434163ad 100644 --- a/apps/dokploy/pages/dashboard/projects.tsx +++ b/apps/dokploy/pages/dashboard/projects.tsx @@ -52,7 +52,7 @@ export async function getServerSideProps( }); await helpers.settings.isCloud.prefetch(); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); if (!user) { return { redirect: { diff --git a/apps/dokploy/pages/dashboard/settings/billing.tsx b/apps/dokploy/pages/dashboard/settings/billing.tsx index ee1ecdbe5..7ba5717e9 100644 --- a/apps/dokploy/pages/dashboard/settings/billing.tsx +++ b/apps/dokploy/pages/dashboard/settings/billing.tsx @@ -52,7 +52,7 @@ export async function getServerSideProps( transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); diff --git a/apps/dokploy/pages/dashboard/settings/certificates.tsx b/apps/dokploy/pages/dashboard/settings/certificates.tsx index 96bec90bf..0c82ed4fb 100644 --- a/apps/dokploy/pages/dashboard/settings/certificates.tsx +++ b/apps/dokploy/pages/dashboard/settings/certificates.tsx @@ -45,7 +45,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); return { diff --git a/apps/dokploy/pages/dashboard/settings/cluster.tsx b/apps/dokploy/pages/dashboard/settings/cluster.tsx index 77ece29b8..a1a46bb65 100644 --- a/apps/dokploy/pages/dashboard/settings/cluster.tsx +++ b/apps/dokploy/pages/dashboard/settings/cluster.tsx @@ -53,7 +53,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); return { props: { diff --git a/apps/dokploy/pages/dashboard/settings/destinations.tsx b/apps/dokploy/pages/dashboard/settings/destinations.tsx index 8605a7c18..3c906b55c 100644 --- a/apps/dokploy/pages/dashboard/settings/destinations.tsx +++ b/apps/dokploy/pages/dashboard/settings/destinations.tsx @@ -46,7 +46,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); return { diff --git a/apps/dokploy/pages/dashboard/settings/git-providers.tsx b/apps/dokploy/pages/dashboard/settings/git-providers.tsx index ce2adc9ce..7a9b08df9 100644 --- a/apps/dokploy/pages/dashboard/settings/git-providers.tsx +++ b/apps/dokploy/pages/dashboard/settings/git-providers.tsx @@ -45,7 +45,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); try { await helpers.project.all.prefetch(); await helpers.settings.isCloud.prefetch(); diff --git a/apps/dokploy/pages/dashboard/settings/index.tsx b/apps/dokploy/pages/dashboard/settings/index.tsx index 713e51133..4c060cbb5 100644 --- a/apps/dokploy/pages/dashboard/settings/index.tsx +++ b/apps/dokploy/pages/dashboard/settings/index.tsx @@ -209,7 +209,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); return { props: { diff --git a/apps/dokploy/pages/dashboard/settings/notifications.tsx b/apps/dokploy/pages/dashboard/settings/notifications.tsx index 76566fdf2..fbdc2e205 100644 --- a/apps/dokploy/pages/dashboard/settings/notifications.tsx +++ b/apps/dokploy/pages/dashboard/settings/notifications.tsx @@ -46,7 +46,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); return { diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 446e6c875..404d400c8 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -1,6 +1,5 @@ import { GenerateToken } from "@/components/dashboard/settings/profile/generate-token"; import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form"; -import { RemoveSelfAccount } from "@/components/dashboard/settings/profile/remove-self-account"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { appRouter } from "@/server/api/root"; @@ -15,14 +14,14 @@ import superjson from "superjson"; const Page = () => { const { data } = api.user.get.useQuery(); - const { data: isCloud } = api.settings.isCloud.useQuery(); + // const { data: isCloud } = api.settings.isCloud.useQuery(); return (
{(data?.canAccessToAPI || data?.role === "owner") && } - {isCloud && } + {/* {isCloud && } */}
); @@ -53,15 +52,7 @@ export async function getServerSideProps( }); await helpers.settings.isCloud.prefetch(); - await helpers.auth.get.prefetch(); - if (user?.role === "member") { - // const userR = await helpers.user.one.fetch({ - // userId: user.id, - // }); - // await helpers.user.byAuthId.prefetch({ - // authId: user.authId, - // }); - } + await helpers.user.get.prefetch(); if (!user) { return { diff --git a/apps/dokploy/pages/dashboard/settings/registry.tsx b/apps/dokploy/pages/dashboard/settings/registry.tsx index 678e0da46..42f0627f4 100644 --- a/apps/dokploy/pages/dashboard/settings/registry.tsx +++ b/apps/dokploy/pages/dashboard/settings/registry.tsx @@ -45,7 +45,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); return { diff --git a/apps/dokploy/pages/dashboard/settings/server.tsx b/apps/dokploy/pages/dashboard/settings/server.tsx index 4f88c794b..0c5e36dc0 100644 --- a/apps/dokploy/pages/dashboard/settings/server.tsx +++ b/apps/dokploy/pages/dashboard/settings/server.tsx @@ -110,7 +110,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); return { props: { diff --git a/apps/dokploy/pages/dashboard/settings/servers.tsx b/apps/dokploy/pages/dashboard/settings/servers.tsx index 08d4ab694..5cc30b832 100644 --- a/apps/dokploy/pages/dashboard/settings/servers.tsx +++ b/apps/dokploy/pages/dashboard/settings/servers.tsx @@ -56,7 +56,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); return { diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index ac5355218..226153145 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -50,7 +50,7 @@ export async function getServerSideProps( }, transformer: superjson, }); - await helpers.auth.get.prefetch(); + await helpers.user.get.prefetch(); await helpers.settings.isCloud.prefetch(); return { diff --git a/apps/dokploy/pages/reset-password.tsx b/apps/dokploy/pages/reset-password.tsx index a34a25ed0..0f6cf0b32 100644 --- a/apps/dokploy/pages/reset-password.tsx +++ b/apps/dokploy/pages/reset-password.tsx @@ -12,17 +12,13 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { db } from "@/server/db"; -import { auth } from "@/server/db/schema"; -import { api } from "@/utils/api"; +import { authClient } from "@/lib/auth-client"; import { IS_CLOUD } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; -import { isBefore } from "date-fns"; -import { eq } from "drizzle-orm"; import type { GetServerSidePropsContext } from "next"; import Link from "next/link"; import { useRouter } from "next/router"; -import { type ReactElement, useEffect } from "react"; +import { type ReactElement, useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; @@ -54,11 +50,12 @@ const loginSchema = z type Login = z.infer; interface Props { - token: string; + tokenResetPassword: string; } -export default function Home({ token }: Props) { - const { mutateAsync, isLoading, isError, error } = - api.auth.resetPassword.useMutation(); +export default function Home({ tokenResetPassword }: Props) { + const [token, setToken] = useState(tokenResetPassword); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); const router = useRouter(); const form = useForm({ defaultValues: { @@ -68,26 +65,32 @@ export default function Home({ token }: Props) { resolver: zodResolver(loginSchema), }); + useEffect(() => { + const token = new URLSearchParams(window.location.search).get("token"); + + if (token) { + setToken(token); + } + }, [token]); + useEffect(() => { form.reset(); }, [form, form.reset, form.formState.isSubmitSuccessful]); const onSubmit = async (values: Login) => { - await mutateAsync({ - resetPasswordToken: token, - password: values.password, - }) - .then((_data) => { - toast.success("Password reset successfully", { - duration: 2000, - }); - router.push("/"); - }) - .catch(() => { - toast.error("Error resetting password", { - duration: 2000, - }); - }); + setIsLoading(true); + const { error } = await authClient.resetPassword({ + newPassword: values.password, + token: token || "", + }); + + if (error) { + setError(error.message || "An error occurred"); + } else { + toast.success("Password reset successfully"); + router.push("/"); + } + setIsLoading(false); }; return (
@@ -104,9 +107,9 @@ export default function Home({ token }: Props) {
- {isError && ( + {error && ( - {error?.message} + {error} )}
@@ -194,35 +197,9 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { }; } - const authR = await db.query.auth.findFirst({ - where: eq(auth.resetPasswordToken, token), - }); - - if (!authR || authR?.resetPasswordExpiresAt === null) { - return { - redirect: { - permanent: true, - destination: "/", - }, - }; - } - const isExpired = isBefore( - new Date(authR.resetPasswordExpiresAt), - new Date(), - ); - - if (isExpired) { - return { - redirect: { - permanent: true, - destination: "/", - }, - }; - } - return { props: { - token: authR.resetPasswordToken, + tokenResetPassword: token, }, }; } diff --git a/apps/dokploy/pages/send-reset-password.tsx b/apps/dokploy/pages/send-reset-password.tsx index 8f6902f6e..10d1058aa 100644 --- a/apps/dokploy/pages/send-reset-password.tsx +++ b/apps/dokploy/pages/send-reset-password.tsx @@ -12,7 +12,7 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { api } from "@/utils/api"; +import { authClient } from "@/lib/auth-client"; import { IS_CLOUD } from "@dokploy/server"; import { zodResolver } from "@hookform/resolvers/zod"; import type { GetServerSidePropsContext } from "next"; @@ -46,8 +46,9 @@ export default function Home() { is2FAEnabled: false, authId: "", }); - const { mutateAsync, isLoading, isError, error } = - api.auth.sendResetPasswordEmail.useMutation(); + + const [error, setError] = useState(null); + const [isLoading, setIsLoading] = useState(false); const _router = useRouter(); const form = useForm({ defaultValues: { @@ -61,19 +62,20 @@ export default function Home() { }, [form, form.reset, form.formState.isSubmitSuccessful]); const onSubmit = async (values: Login) => { - await mutateAsync({ + setIsLoading(true); + const { error } = await authClient.forgetPassword({ email: values.email, - }) - .then((_data) => { - toast.success("Email sent", { - duration: 2000, - }); - }) - .catch(() => { - toast.error("Error sending email", { - duration: 2000, - }); + redirectTo: "/reset-password", + }); + if (error) { + setError(error.message || "An error occurred"); + setIsLoading(false); + } else { + toast.success("Email sent", { + duration: 2000, }); + } + setIsLoading(false); }; return (
@@ -89,9 +91,9 @@ export default function Home() {
- {isError && ( + {error && ( - {error?.message} + {error} )} {!temp.is2FAEnabled ? ( diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 1a6c046e3..68a2eba73 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -31,294 +31,268 @@ import { } from "../trpc"; export const authRouter = createTRPCRouter({ - createAdmin: publicProcedure.mutation(async ({ input }) => { - try { - if (!IS_CLOUD) { - const admin = await db.query.admins.findFirst({}); - if (admin) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Admin already exists", - }); - } - } - const newAdmin = await createAdmin(input); - - if (IS_CLOUD) { - await sendDiscordNotificationWelcome(newAdmin); - await sendVerificationEmail(newAdmin.id); - return { - status: "success", - type: "cloud", - }; - } - // const session = await lucia.createSession(newAdmin.id || "", {}); - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); - return { - status: "success", - type: "selfhosted", - }; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - // @ts-ignore - message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`, - cause: error, - }); - } - }), - createUser: publicProcedure.mutation(async ({ input }) => { - try { - const _token = await getUserByToken(input.token); - // if (token.isExpired) { - // throw new TRPCError({ - // code: "BAD_REQUEST", - // message: "Invalid token", - // }); - // } - - // const newUser = await createUser(input); - - // if (IS_CLOUD) { - // await sendVerificationEmail(token.authId); - // return true; - // } - // const session = await lucia.createSession(newUser?.authId || "", {}); - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); - return true; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Error creating the user", - cause: error, - }); - } - }), - - login: publicProcedure.mutation(async ({ input }) => { - try { - const auth = await findAuthByEmail(input.email); - - const correctPassword = bcrypt.compareSync( - input.password, - auth?.password || "", - ); - - if (!correctPassword) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Credentials do not match", - }); - } - - if (auth?.confirmationToken && IS_CLOUD) { - await sendVerificationEmail(auth.id); - throw new TRPCError({ - code: "BAD_REQUEST", - message: - "Email not confirmed, we have sent you a confirmation email please check your inbox.", - }); - } - - if (auth?.is2FAEnabled) { - return { - is2FAEnabled: true, - authId: auth.id, - }; - } - - // const session = await lucia.createSession(auth?.id || "", {}); - - // ctx.res.appendHeader( - // "Set-Cookie", - // lucia.createSessionCookie(session.id).serialize(), - // ); - return { - is2FAEnabled: false, - authId: auth?.id, - }; - } catch (error) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: `Error: ${error instanceof Error ? error.message : "Login error"}`, - cause: error, - }); - } - }), - - get: protectedProcedure.query(async ({ ctx }) => { - const memberResult = await db.query.member.findFirst({ - where: and( - eq(member.userId, ctx.user.id), - eq(member.organizationId, ctx.session?.activeOrganizationId || ""), - ), - with: { - user: true, - }, - }); - - return memberResult; - }), - - logout: protectedProcedure.mutation(async ({ ctx }) => { - const { req } = ctx; - const { session } = await validateRequest(req); - if (!session) return false; - - // await lucia.invalidateSession(session.id); - // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); - return true; - }), - - update: protectedProcedure.mutation(async ({ ctx, input }) => { - const currentAuth = await findAuthByEmail(ctx.user.email); - - if (input.currentPassword || input.password) { - const correctPassword = bcrypt.compareSync( - input.currentPassword || "", - currentAuth?.password || "", - ); - if (!correctPassword) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Current password is incorrect", - }); - } - } - // const auth = await updateAuthById(ctx.user.authId, { - // ...(input.email && { email: input.email.toLowerCase() }), - // ...(input.password && { - // password: bcrypt.hashSync(input.password, 10), - // }), - // ...(input.image && { image: input.image }), - // }); - - return auth; - }), - removeSelfAccount: protectedProcedure - .input( - z.object({ - password: z.string().min(1), - }), - ) - .mutation(async ({ ctx, input }) => { - if (!IS_CLOUD) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "This feature is only available in the cloud version", - }); - } - const currentAuth = await findAuthByEmail(ctx.user.email); - - const correctPassword = bcrypt.compareSync( - input.password, - currentAuth?.password || "", - ); - - if (!correctPassword) { - throw new TRPCError({ - code: "BAD_REQUEST", - message: "Password is incorrect", - }); - } - const { req } = ctx; - const { session } = await validateRequest(req); - if (!session) return false; - - // await lucia.invalidateSession(session.id); - // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); - - // if (ctx.user.rol === "owner") { - // await removeAdminByAuthId(ctx.user.authId); - // } else { - // await removeUserByAuthId(ctx.user.authId); - // } - - return true; - }), - - generateToken: protectedProcedure.mutation(async ({ ctx }) => { - const auth = await findUserById(ctx.user.id); - console.log(auth); - - // if (auth.token) { - // await luciaToken.invalidateSession(auth.token); - // } - // const session = await luciaToken.createSession(auth?.id || "", { - // expiresIn: 60 * 60 * 24 * 30, - // }); - // await updateUser(auth.id, { - // token: session.id, - // }); - return auth; - }), - verifyToken: protectedProcedure.mutation(async () => { - return true; - }), - one: adminProcedure - .input(z.object({ userId: z.string().min(1) })) - .query(async ({ input }) => { - // TODO: Check if the user is admin or member - const user = await findUserById(input.userId); - return user; - }), - sendResetPasswordEmail: publicProcedure - .input( - z.object({ - email: z.string().min(1).email(), - }), - ) - .mutation(async ({ input }) => { - if (!IS_CLOUD) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "This feature is only available in the cloud version", - }); - } - const authR = await db.query.auth.findFirst({ - where: eq(auth.email, input.email), - }); - if (!authR) { - throw new TRPCError({ - code: "NOT_FOUND", - message: "User not found", - }); - } - const token = nanoid(); - await updateAuthById(authR.id, { - resetPasswordToken: token, - // Make resetPassword in 24 hours - resetPasswordExpiresAt: new Date( - new Date().getTime() + 24 * 60 * 60 * 1000, - ).toISOString(), - }); - - await sendEmailNotification( - { - fromAddress: process.env.SMTP_FROM_ADDRESS!, - toAddresses: [authR.email], - smtpServer: process.env.SMTP_SERVER!, - smtpPort: Number(process.env.SMTP_PORT), - username: process.env.SMTP_USERNAME!, - password: process.env.SMTP_PASSWORD!, - }, - "Reset Password", - ` - Reset your password by clicking the link below: - The link will expire in 24 hours. - - Reset Password - - - `, - ); - }), + // createAdmin: publicProcedure.mutation(async ({ input }) => { + // try { + // if (!IS_CLOUD) { + // const admin = await db.query.admins.findFirst({}); + // if (admin) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Admin already exists", + // }); + // } + // } + // const newAdmin = await createAdmin(input); + // if (IS_CLOUD) { + // await sendDiscordNotificationWelcome(newAdmin); + // await sendVerificationEmail(newAdmin.id); + // return { + // status: "success", + // type: "cloud", + // }; + // } + // // const session = await lucia.createSession(newAdmin.id || "", {}); + // // ctx.res.appendHeader( + // // "Set-Cookie", + // // lucia.createSessionCookie(session.id).serialize(), + // // ); + // return { + // status: "success", + // type: "selfhosted", + // }; + // } catch (error) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // // @ts-ignore + // message: `Error: ${error?.code === "23505" ? "Email already exists" : "Error creating admin"}`, + // cause: error, + // }); + // } + // }), + // createUser: publicProcedure.mutation(async ({ input }) => { + // try { + // const _token = await getUserByToken(input.token); + // // if (token.isExpired) { + // // throw new TRPCError({ + // // code: "BAD_REQUEST", + // // message: "Invalid token", + // // }); + // // } + // // const newUser = await createUser(input); + // // if (IS_CLOUD) { + // // await sendVerificationEmail(token.authId); + // // return true; + // // } + // // const session = await lucia.createSession(newUser?.authId || "", {}); + // // ctx.res.appendHeader( + // // "Set-Cookie", + // // lucia.createSessionCookie(session.id).serialize(), + // // ); + // return true; + // } catch (error) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Error creating the user", + // cause: error, + // }); + // } + // }), + // login: publicProcedure.mutation(async ({ input }) => { + // try { + // const auth = await findAuthByEmail(input.email); + // const correctPassword = bcrypt.compareSync( + // input.password, + // auth?.password || "", + // ); + // if (!correctPassword) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Credentials do not match", + // }); + // } + // if (auth?.confirmationToken && IS_CLOUD) { + // await sendVerificationEmail(auth.id); + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: + // "Email not confirmed, we have sent you a confirmation email please check your inbox.", + // }); + // } + // if (auth?.is2FAEnabled) { + // return { + // is2FAEnabled: true, + // authId: auth.id, + // }; + // } + // // const session = await lucia.createSession(auth?.id || "", {}); + // // ctx.res.appendHeader( + // // "Set-Cookie", + // // lucia.createSessionCookie(session.id).serialize(), + // // ); + // return { + // is2FAEnabled: false, + // authId: auth?.id, + // }; + // } catch (error) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: `Error: ${error instanceof Error ? error.message : "Login error"}`, + // cause: error, + // }); + // } + // }), + // get: protectedProcedure.query(async ({ ctx }) => { + // const memberResult = await db.query.member.findFirst({ + // where: and( + // eq(member.userId, ctx.user.id), + // eq(member.organizationId, ctx.session?.activeOrganizationId || ""), + // ), + // with: { + // user: true, + // }, + // }); + // return memberResult; + // }), + // logout: protectedProcedure.mutation(async ({ ctx }) => { + // const { req } = ctx; + // const { session } = await validateRequest(req); + // if (!session) return false; + // // await lucia.invalidateSession(session.id); + // // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); + // return true; + // }), + // update: protectedProcedure.mutation(async ({ ctx, input }) => { + // const currentAuth = await findAuthByEmail(ctx.user.email); + // if (input.currentPassword || input.password) { + // const correctPassword = bcrypt.compareSync( + // input.currentPassword || "", + // currentAuth?.password || "", + // ); + // if (!correctPassword) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Current password is incorrect", + // }); + // } + // } + // // const auth = await updateAuthById(ctx.user.authId, { + // // ...(input.email && { email: input.email.toLowerCase() }), + // // ...(input.password && { + // // password: bcrypt.hashSync(input.password, 10), + // // }), + // // ...(input.image && { image: input.image }), + // // }); + // return auth; + // }), + // removeSelfAccount: protectedProcedure + // .input( + // z.object({ + // password: z.string().min(1), + // }), + // ) + // .mutation(async ({ ctx, input }) => { + // if (!IS_CLOUD) { + // throw new TRPCError({ + // code: "NOT_FOUND", + // message: "This feature is only available in the cloud version", + // }); + // } + // const currentAuth = await findAuthByEmail(ctx.user.email); + // const correctPassword = bcrypt.compareSync( + // input.password, + // currentAuth?.password || "", + // ); + // if (!correctPassword) { + // throw new TRPCError({ + // code: "BAD_REQUEST", + // message: "Password is incorrect", + // }); + // } + // const { req } = ctx; + // const { session } = await validateRequest(req); + // if (!session) return false; + // // await lucia.invalidateSession(session.id); + // // res.setHeader("Set-Cookie", lucia.createBlankSessionCookie().serialize()); + // // if (ctx.user.rol === "owner") { + // // await removeAdminByAuthId(ctx.user.authId); + // // } else { + // // await removeUserByAuthId(ctx.user.authId); + // // } + // return true; + // }), + // generateToken: protectedProcedure.mutation(async ({ ctx }) => { + // const auth = await findUserById(ctx.user.id); + // console.log(auth); + // // if (auth.token) { + // // await luciaToken.invalidateSession(auth.token); + // // } + // // const session = await luciaToken.createSession(auth?.id || "", { + // // expiresIn: 60 * 60 * 24 * 30, + // // }); + // // await updateUser(auth.id, { + // // token: session.id, + // // }); + // return auth; + // }), + // verifyToken: protectedProcedure.mutation(async () => { + // return true; + // }), + // one: adminProcedure + // .input(z.object({ userId: z.string().min(1) })) + // .query(async ({ input }) => { + // // TODO: Check if the user is admin or member + // const user = await findUserById(input.userId); + // return user; + // }), + // sendResetPasswordEmail: publicProcedure + // .input( + // z.object({ + // email: z.string().min(1).email(), + // }), + // ) + // .mutation(async ({ input }) => { + // if (!IS_CLOUD) { + // throw new TRPCError({ + // code: "NOT_FOUND", + // message: "This feature is only available in the cloud version", + // }); + // } + // const authR = await db.query.auth.findFirst({ + // where: eq(auth.email, input.email), + // }); + // if (!authR) { + // throw new TRPCError({ + // code: "NOT_FOUND", + // message: "User not found", + // }); + // } + // const token = nanoid(); + // await updateAuthById(authR.id, { + // resetPasswordToken: token, + // // Make resetPassword in 24 hours + // resetPasswordExpiresAt: new Date( + // new Date().getTime() + 24 * 60 * 60 * 1000, + // ).toISOString(), + // }); + // await sendEmailNotification( + // { + // fromAddress: process.env.SMTP_FROM_ADDRESS!, + // toAddresses: [authR.email], + // smtpServer: process.env.SMTP_SERVER!, + // smtpPort: Number(process.env.SMTP_PORT), + // username: process.env.SMTP_USERNAME!, + // password: process.env.SMTP_PASSWORD!, + // }, + // "Reset Password", + // ` + // Reset your password by clicking the link below: + // The link will expire in 24 hours. + // + // Reset Password + // + // `, + // ); + // }), }); // export const sendVerificationEmail = async (authId: string) => { diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 5717d59bf..b9f0adcd0 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -65,6 +65,19 @@ export const userRouter = createTRPCRouter({ return memberResult; }), + getServerMetrics: protectedProcedure.query(async ({ ctx }) => { + const memberResult = await db.query.member.findFirst({ + where: and( + eq(member.userId, ctx.user.id), + eq(member.organizationId, ctx.session?.activeOrganizationId || ""), + ), + with: { + user: true, + }, + }); + + return memberResult?.user; + }), update: protectedProcedure .input(apiUpdateUser) .mutation(async ({ input, ctx }) => { @@ -112,7 +125,6 @@ export const userRouter = createTRPCRouter({ const { id, ...rest } = input; - console.log(rest); await db .update(member) .set({ @@ -202,4 +214,8 @@ export const userRouter = createTRPCRouter({ throw error; } }), + + generateToken: protectedProcedure.mutation(async () => { + return "token"; + }), }); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 192f6d0ea..2b807a57d 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -6,7 +6,7 @@ import { organization, twoFactor } from "better-auth/plugins"; import { and, desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; -import { sendVerificationEmail } from "../verification/send-verification-email"; +import { sendEmail } from "../verification/send-verification-email"; import { IS_CLOUD } from "../constants"; export const auth = betterAuth({ @@ -30,7 +30,11 @@ export const auth = betterAuth({ autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url }) => { console.log("Sending verification email to", user.email); - await sendVerificationEmail(user.email, url); + await sendEmail({ + email: user.email, + subject: "Verify your email", + text: `Click the link to verify your email: ${url}`, + }); }, }, emailAndPassword: { @@ -45,6 +49,13 @@ export const auth = betterAuth({ return bcrypt.compareSync(password, hash); }, }, + sendResetPassword: async ({ user, url }) => { + await sendEmail({ + email: user.email, + subject: "Reset your password", + text: `Click the link to reset your password: ${url}`, + }); + }, }, databaseHooks: { user: { diff --git a/packages/server/src/services/admin.ts b/packages/server/src/services/admin.ts index 099018f2b..3509868be 100644 --- a/packages/server/src/services/admin.ts +++ b/packages/server/src/services/admin.ts @@ -1,6 +1,5 @@ import { db } from "@dokploy/server/db"; import { - type apiCreateUserInvitation, invitation, member, organization, @@ -10,42 +9,6 @@ import { TRPCError } from "@trpc/server"; import { eq } from "drizzle-orm"; import { IS_CLOUD } from "../constants"; -export type User = typeof users_temp.$inferSelect; -export const createInvitation = async ( - _input: typeof apiCreateUserInvitation._type, - _adminId: string, -) => { - // await db.transaction(async (tx) => { - // const result = await tx - // .insert(auth) - // .values({ - // email: input.email.toLowerCase(), - // rol: "user", - // password: bcrypt.hashSync("01231203012312", 10), - // }) - // .returning() - // .then((res) => res[0]); - // if (!result) { - // throw new TRPCError({ - // code: "BAD_REQUEST", - // message: "Error creating the user", - // }); - // } - // const expiresIn24Hours = new Date(); - // expiresIn24Hours.setDate(expiresIn24Hours.getDate() + 1); - // const token = randomBytes(32).toString("hex"); - // await tx - // .insert(users) - // .values({ - // adminId: adminId, - // authId: result.id, - // token, - // expirationDate: expiresIn24Hours.toISOString(), - // }) - // .returning(); - // }); -}; - export const findUserById = async (userId: string) => { const user = await db.query.users_temp.findFirst({ where: eq(users_temp.id, userId), @@ -69,34 +32,6 @@ export const findOrganizationById = async (organizationId: string) => { return organizationResult; }; -export const updateUser = async (userId: string, userData: Partial) => { - const user = await db - .update(users_temp) - .set({ - ...userData, - }) - .where(eq(users_temp.id, userId)) - .returning() - .then((res) => res[0]); - - return user; -}; - -export const updateAdminById = async ( - _adminId: string, - _adminData: Partial, -) => { - // const admin = await db - // .update(admins) - // .set({ - // ...adminData, - // }) - // .where(eq(admins.adminId, adminId)) - // .returning() - // .then((res) => res[0]); - // return admin; -}; - export const isAdminPresent = async () => { const admin = await db.query.member.findFirst({ where: eq(member.role, "owner"), diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index f36d8ef65..a1901d716 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,5 +1,5 @@ import { db } from "@dokploy/server/db"; -import { member, type users_temp } from "@dokploy/server/db/schema"; +import { member, users_temp } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { and, eq } from "drizzle-orm"; @@ -235,3 +235,16 @@ export const findMemberById = async ( } return result; }; + +export const updateUser = async (userId: string, userData: Partial) => { + const user = await db + .update(users_temp) + .set({ + ...userData, + }) + .where(eq(users_temp.id, userId)) + .returning() + .then((res) => res[0]); + + return user; +}; diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index bbd32cff2..d05d805f3 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -30,6 +30,7 @@ class LogRotationManager { private async getStateFromDB(): Promise { // const setting = await db.query.admins.findFirst({}); // return setting?.enableLogRotation ?? false; + return false; } private async setStateInDB(_active: boolean): Promise { diff --git a/packages/server/src/utils/backups/index.ts b/packages/server/src/utils/backups/index.ts index d1b87d692..7699a42e4 100644 --- a/packages/server/src/utils/backups/index.ts +++ b/packages/server/src/utils/backups/index.ts @@ -11,13 +11,14 @@ import { runMariadbBackup } from "./mariadb"; import { runMongoBackup } from "./mongo"; import { runMySqlBackup } from "./mysql"; import { runPostgresBackup } from "./postgres"; +import { findAdmin } from "../../services/admin"; export const initCronJobs = async () => { console.log("Setting up cron jobs...."); const admin = await findAdmin(); - if (admin?.enableDockerCleanup) { + if (admin?.user.enableDockerCleanup) { scheduleJob("docker-cleanup", "0 0 * * *", async () => { console.log( `Docker Cleanup ${new Date().toLocaleString()}] Running docker cleanup`, @@ -25,7 +26,7 @@ export const initCronJobs = async () => { await cleanUpUnusedImages(); await cleanUpDockerBuilder(); await cleanUpSystemPrune(); - await sendDockerCleanupNotifications(admin.adminId); + await sendDockerCleanupNotifications(admin.user.id); }); } @@ -42,7 +43,7 @@ export const initCronJobs = async () => { await cleanUpDockerBuilder(serverId); await cleanUpSystemPrune(serverId); await sendDockerCleanupNotifications( - admin.adminId, + admin.user.id, `Docker cleanup for Server ${name} (${serverId})`, ); }); diff --git a/packages/server/src/verification/send-verification-email.tsx b/packages/server/src/verification/send-verification-email.tsx index 0b3b3bdcc..c673c0f77 100644 --- a/packages/server/src/verification/send-verification-email.tsx +++ b/packages/server/src/verification/send-verification-email.tsx @@ -2,7 +2,15 @@ import { sendDiscordNotification, sendEmailNotification, } from "../utils/notifications/utils"; -export const sendVerificationEmail = async (email: string, url: string) => { +export const sendEmail = async ({ + email, + subject, + text, +}: { + email: string; + subject: string; + text: string; +}) => { await sendEmailNotification( { fromAddress: process.env.SMTP_FROM_ADDRESS || "", @@ -12,14 +20,8 @@ export const sendVerificationEmail = async (email: string, url: string) => { username: process.env.SMTP_USERNAME || "", password: process.env.SMTP_PASSWORD || "", }, - "Confirm your email | Dokploy", - ` - Welcome to Dokploy! - Please confirm your email by clicking the link below: - - Confirm Email - - `, + subject, + text, ); return true; From 47f7648cb356a0228d8829ebba21d80ff3c2ec8f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 22:37:57 -0600 Subject: [PATCH 086/126] feat: improve user profile update and password change functionality This commit adds enhanced password change validation and handling: - Add password change validation in user update route - Implement password verification before allowing changes - Update user schema to support optional password fields - Fix token display in generate token component - Disable migration script temporarily --- .../settings/profile/generate-token.tsx | 2 +- .../settings/profile/profile-form.tsx | 4 +- apps/dokploy/migrate.ts | 280 +++++++++--------- apps/dokploy/server/api/routers/user.ts | 31 ++ packages/server/src/db/schema/user.ts | 2 + 5 files changed, 176 insertions(+), 143 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx index 4d36ab7a1..5213c0b9d 100644 --- a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx @@ -51,7 +51,7 @@ export const GenerateToken = () => {
diff --git a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx index 761cfb71b..32179378a 100644 --- a/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx +++ b/apps/dokploy/components/dashboard/settings/profile/profile-form.tsx @@ -103,9 +103,9 @@ export const ProfileForm = () => { const onSubmit = async (values: Profile) => { await mutateAsync({ email: values.email.toLowerCase(), - password: values.password, + password: values.password || undefined, image: values.image, - currentPassword: values.currentPassword, + currentPassword: values.currentPassword || undefined, }) .then(async () => { await refetch(); diff --git a/apps/dokploy/migrate.ts b/apps/dokploy/migrate.ts index 097459b96..e1f52c9a3 100644 --- a/apps/dokploy/migrate.ts +++ b/apps/dokploy/migrate.ts @@ -1,149 +1,149 @@ -import { drizzle } from "drizzle-orm/postgres-js"; -import { nanoid } from "nanoid"; -import postgres from "postgres"; -import * as schema from "./server/db/schema"; +// import { drizzle } from "drizzle-orm/postgres-js"; +// import { nanoid } from "nanoid"; +// import postgres from "postgres"; +// import * as schema from "./server/db/schema"; -const connectionString = process.env.DATABASE_URL!; +// const connectionString = process.env.DATABASE_URL!; -const sql = postgres(connectionString, { max: 1 }); -const db = drizzle(sql, { - schema, -}); +// const sql = postgres(connectionString, { max: 1 }); +// const db = drizzle(sql, { +// schema, +// }); -await db - .transaction(async (db) => { - const admins = await db.query.admins.findMany({ - with: { - auth: true, - users: { - with: { - auth: true, - }, - }, - }, - }); - for (const admin of admins) { - const user = await db - .insert(schema.users_temp) - .values({ - id: admin.adminId, - email: admin.auth.email, - token: admin.auth.token || "", - emailVerified: true, - updatedAt: new Date(), - role: "admin", - serverIp: admin.serverIp, - image: admin.auth.image, - certificateType: admin.certificateType, - host: admin.host, - letsEncryptEmail: admin.letsEncryptEmail, - sshPrivateKey: admin.sshPrivateKey, - enableDockerCleanup: admin.enableDockerCleanup, - enableLogRotation: admin.enableLogRotation, - enablePaidFeatures: admin.enablePaidFeatures, - metricsConfig: admin.metricsConfig, - cleanupCacheApplications: admin.cleanupCacheApplications, - cleanupCacheOnPreviews: admin.cleanupCacheOnPreviews, - cleanupCacheOnCompose: admin.cleanupCacheOnCompose, - stripeCustomerId: admin.stripeCustomerId, - stripeSubscriptionId: admin.stripeSubscriptionId, - serversQuantity: admin.serversQuantity, - }) - .returning() - .then((user) => user[0]); +// await db +// .transaction(async (db) => { +// const admins = await db.query.admins.findMany({ +// with: { +// auth: true, +// users: { +// with: { +// auth: true, +// }, +// }, +// }, +// }); +// for (const admin of admins) { +// const user = await db +// .insert(schema.users_temp) +// .values({ +// id: admin.adminId, +// email: admin.auth.email, +// token: admin.auth.token || "", +// emailVerified: true, +// updatedAt: new Date(), +// role: "admin", +// serverIp: admin.serverIp, +// image: admin.auth.image, +// certificateType: admin.certificateType, +// host: admin.host, +// letsEncryptEmail: admin.letsEncryptEmail, +// sshPrivateKey: admin.sshPrivateKey, +// enableDockerCleanup: admin.enableDockerCleanup, +// enableLogRotation: admin.enableLogRotation, +// enablePaidFeatures: admin.enablePaidFeatures, +// metricsConfig: admin.metricsConfig, +// cleanupCacheApplications: admin.cleanupCacheApplications, +// cleanupCacheOnPreviews: admin.cleanupCacheOnPreviews, +// cleanupCacheOnCompose: admin.cleanupCacheOnCompose, +// stripeCustomerId: admin.stripeCustomerId, +// stripeSubscriptionId: admin.stripeSubscriptionId, +// serversQuantity: admin.serversQuantity, +// }) +// .returning() +// .then((user) => user[0]); - await db.insert(schema.account).values({ - providerId: "credential", - userId: user?.id || "", - password: admin.auth.password, - is2FAEnabled: admin.auth.is2FAEnabled || false, - createdAt: new Date(admin.auth.createdAt) || new Date(), - updatedAt: new Date(admin.auth.createdAt) || new Date(), - }); +// await db.insert(schema.account).values({ +// providerId: "credential", +// userId: user?.id || "", +// password: admin.auth.password, +// is2FAEnabled: admin.auth.is2FAEnabled || false, +// createdAt: new Date(admin.auth.createdAt) || new Date(), +// updatedAt: new Date(admin.auth.createdAt) || new Date(), +// }); - const organization = await db - .insert(schema.organization) - .values({ - name: "My Organization", - slug: nanoid(), - ownerId: user?.id || "", - createdAt: new Date(admin.createdAt) || new Date(), - }) - .returning() - .then((organization) => organization[0]); +// const organization = await db +// .insert(schema.organization) +// .values({ +// name: "My Organization", +// slug: nanoid(), +// ownerId: user?.id || "", +// createdAt: new Date(admin.createdAt) || new Date(), +// }) +// .returning() +// .then((organization) => organization[0]); - for (const member of admin.users) { - const userTemp = await db - .insert(schema.users_temp) - .values({ - id: member.userId, - email: member.auth.email, - token: member.token || "", - emailVerified: true, - updatedAt: new Date(admin.createdAt) || new Date(), - role: "user", - image: member.auth.image, - createdAt: admin.createdAt, - canAccessToAPI: member.canAccessToAPI || false, - canAccessToDocker: member.canAccessToDocker || false, - canAccessToGitProviders: member.canAccessToGitProviders || false, - canAccessToSSHKeys: member.canAccessToSSHKeys || false, - canAccessToTraefikFiles: member.canAccessToTraefikFiles || false, - canCreateProjects: member.canCreateProjects || false, - canCreateServices: member.canCreateServices || false, - canDeleteProjects: member.canDeleteProjects || false, - canDeleteServices: member.canDeleteServices || false, - accessedProjects: member.accessedProjects || [], - accessedServices: member.accessedServices || [], - }) - .returning() - .then((userTemp) => userTemp[0]); +// for (const member of admin.users) { +// const userTemp = await db +// .insert(schema.users_temp) +// .values({ +// id: member.userId, +// email: member.auth.email, +// token: member.token || "", +// emailVerified: true, +// updatedAt: new Date(admin.createdAt) || new Date(), +// role: "user", +// image: member.auth.image, +// createdAt: admin.createdAt, +// canAccessToAPI: member.canAccessToAPI || false, +// canAccessToDocker: member.canAccessToDocker || false, +// canAccessToGitProviders: member.canAccessToGitProviders || false, +// canAccessToSSHKeys: member.canAccessToSSHKeys || false, +// canAccessToTraefikFiles: member.canAccessToTraefikFiles || false, +// canCreateProjects: member.canCreateProjects || false, +// canCreateServices: member.canCreateServices || false, +// canDeleteProjects: member.canDeleteProjects || false, +// canDeleteServices: member.canDeleteServices || false, +// accessedProjects: member.accessedProjects || [], +// accessedServices: member.accessedServices || [], +// }) +// .returning() +// .then((userTemp) => userTemp[0]); - await db.insert(schema.account).values({ - providerId: "credential", - userId: member?.userId || "", - password: member.auth.password, - is2FAEnabled: member.auth.is2FAEnabled || false, - createdAt: new Date(member.auth.createdAt) || new Date(), - updatedAt: new Date(member.auth.createdAt) || new Date(), - }); +// await db.insert(schema.account).values({ +// providerId: "credential", +// userId: member?.userId || "", +// password: member.auth.password, +// is2FAEnabled: member.auth.is2FAEnabled || false, +// createdAt: new Date(member.auth.createdAt) || new Date(), +// updatedAt: new Date(member.auth.createdAt) || new Date(), +// }); - await db.insert(schema.member).values({ - organizationId: organization?.id || "", - userId: userTemp?.id || "", - role: "admin", - createdAt: new Date(member.createdAt) || new Date(), - }); - } - } - }) - .then(() => { - console.log("Migration finished"); - }) - .catch((error) => { - console.error(error); - }); +// await db.insert(schema.member).values({ +// organizationId: organization?.id || "", +// userId: userTemp?.id || "", +// role: "admin", +// createdAt: new Date(member.createdAt) || new Date(), +// }); +// } +// } +// }) +// .then(() => { +// console.log("Migration finished"); +// }) +// .catch((error) => { +// console.error(error); +// }); -await db - .transaction(async (db) => { - const projects = await db.query.projects.findMany({ - with: { - user: { - with: { - organizations: true, - }, - }, - }, - }); - for (const project of projects) { - const _user = await db.update(schema.projects).set({ - organizationId: project.user.organizations[0]?.id || "", - }); - } - }) - .then(() => { - console.log("Migration finished"); - }) - .catch((error) => { - console.error(error); - }); +// await db +// .transaction(async (db) => { +// const projects = await db.query.projects.findMany({ +// with: { +// user: { +// with: { +// organizations: true, +// }, +// }, +// }, +// }); +// for (const project of projects) { +// const _user = await db.update(schema.projects).set({ +// organizationId: project.user.organizations[0]?.id || "", +// }); +// } +// }) +// .then(() => { +// console.log("Migration finished"); +// }) +// .catch((error) => { +// console.error(error); +// }); diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index b9f0adcd0..1dac65fef 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -8,12 +8,14 @@ import { } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { + account, apiAssignPermissions, apiFindOneToken, apiUpdateUser, invitation, member, } from "@dokploy/server/db/schema"; +import * as bcrypt from "bcrypt"; import { TRPCError } from "@trpc/server"; import { and, asc, eq, gt } from "drizzle-orm"; import { z } from "zod"; @@ -81,6 +83,35 @@ export const userRouter = createTRPCRouter({ update: protectedProcedure .input(apiUpdateUser) .mutation(async ({ input, ctx }) => { + if (input.password || input.currentPassword) { + const currentAuth = await db.query.account.findFirst({ + where: eq(account.userId, ctx.user.id), + }); + const correctPassword = bcrypt.compareSync( + input.currentPassword || "", + currentAuth?.password || "", + ); + + if (!correctPassword) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Current password is incorrect", + }); + } + + if (!input.password) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "New password is required", + }); + } + await db + .update(account) + .set({ + password: bcrypt.hashSync(input.password, 10), + }) + .where(eq(account.userId, ctx.user.id)); + } return await updateUser(ctx.user.id, input); }), getUserByToken: publicProcedure diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 2f3761fec..3916f1e70 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -278,6 +278,8 @@ export const apiUpdateWebServerMonitoring = z.object({ }); export const apiUpdateUser = createSchema.partial().extend({ + password: z.string().optional(), + currentPassword: z.string().optional(), metricsConfig: z .object({ server: z.object({ From 3f2722f28d55e94e84b67bad031acda272241829 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 22:38:38 -0600 Subject: [PATCH 087/126] refactor: remove unused imports and simplify auth router --- apps/dokploy/server/api/routers/auth.ts | 32 +------------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/apps/dokploy/server/api/routers/auth.ts b/apps/dokploy/server/api/routers/auth.ts index 68a2eba73..2c7469c3f 100644 --- a/apps/dokploy/server/api/routers/auth.ts +++ b/apps/dokploy/server/api/routers/auth.ts @@ -1,34 +1,4 @@ -import { - // apiCreateAdmin, - // apiCreateUser, - // apiFindOneAuth, - // apiLogin, - // apiUpdateAuth, - // apiVerify2FA, - // apiVerifyLogin2FA, - // auth, - member, -} from "@/server/db/schema"; -import { WEBSITE_URL } from "@/server/utils/stripe"; -import { - IS_CLOUD, - findUserById, - getUserByToken, - sendEmailNotification, - validateRequest, -} from "@dokploy/server"; -import { TRPCError } from "@trpc/server"; -import * as bcrypt from "bcrypt"; -import { and, eq } from "drizzle-orm"; -import { nanoid } from "nanoid"; -import { z } from "zod"; -import { db } from "../../db"; -import { - adminProcedure, - createTRPCRouter, - protectedProcedure, - publicProcedure, -} from "../trpc"; +import { createTRPCRouter } from "../trpc"; export const authRouter = createTRPCRouter({ // createAdmin: publicProcedure.mutation(async ({ input }) => { From 0ec8e2baa1a7c8ad3d36b9122f0f104adc6f589a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 23:20:27 -0600 Subject: [PATCH 088/126] chore: update GitHub workflow branch trigger for authentication feature branch --- .github/workflows/dokploy.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dokploy.yml b/.github/workflows/dokploy.yml index 9914811e8..adcb1bb54 100644 --- a/.github/workflows/dokploy.yml +++ b/.github/workflows/dokploy.yml @@ -2,7 +2,7 @@ name: Dokploy Docker Build on: push: - branches: [main, canary, "feat/monitoring"] + branches: [main, canary, "feat/better-auth-2"] env: IMAGE_NAME: dokploy/dokploy From 30cbad93d21f9d732a4dc9b4897fd5efed09f1e3 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 23:22:15 -0600 Subject: [PATCH 089/126] refactor: improve Traefik error handling in service initialization --- packages/server/src/setup/traefik-setup.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/setup/traefik-setup.ts b/packages/server/src/setup/traefik-setup.ts index 21caa5cf2..e8d019424 100644 --- a/packages/server/src/setup/traefik-setup.ts +++ b/packages/server/src/setup/traefik-setup.ts @@ -127,7 +127,7 @@ export const initializeTraefik = async ({ }); console.log("Traefik Started ✅"); - } catch (error) { + } catch (_) { try { await docker.createService(settings); } catch (error: any) { From 87836d23c37837a7571b1b4b5dd60f87c2696c16 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 22 Feb 2025 23:25:22 -0600 Subject: [PATCH 090/126] chore: disable TypeScript declaration generation in server tsconfig --- packages/server/tsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index 0eb9923a6..db7ded39e 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -2,6 +2,7 @@ "compilerOptions": { /* Base Options: */ "esModuleInterop": true, + "declaration": false, "skipLibCheck": true, "target": "es2022", "allowJs": true, From 4a1a5a9bb1d11204c3a6ff44b5a0d82773e51307 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:04:24 -0600 Subject: [PATCH 091/126] chore: comment out database schema definitions in auth-schema --- packages/server/auth-schema.ts | 182 ++++++++++++++++----------------- 1 file changed, 90 insertions(+), 92 deletions(-) diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index a7be9b051..b9b682fbf 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,99 +1,97 @@ -import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +// export const users_temp = pgTable("users_temp", { +// id: text("id").primaryKey(), +// name: text("name").notNull(), +// email: text("email").notNull().unique(), +// emailVerified: boolean("email_verified").notNull(), +// image: text("image"), +// createdAt: timestamp("created_at").notNull(), +// updatedAt: timestamp("updated_at").notNull(), +// twoFactorEnabled: boolean("two_factor_enabled"), +// role: text("role"), +// ownerId: text("owner_id"), +// }); -export const users_temp = pgTable("users_temp", { - id: text("id").primaryKey(), - name: text("name").notNull(), - email: text("email").notNull().unique(), - emailVerified: boolean("email_verified").notNull(), - image: text("image"), - createdAt: timestamp("created_at").notNull(), - updatedAt: timestamp("updated_at").notNull(), - twoFactorEnabled: boolean("two_factor_enabled"), - role: text("role"), - ownerId: text("owner_id"), -}); +// export const session = pgTable("session", { +// id: text("id").primaryKey(), +// expiresAt: timestamp("expires_at").notNull(), +// token: text("token").notNull().unique(), +// createdAt: timestamp("created_at").notNull(), +// updatedAt: timestamp("updated_at").notNull(), +// ipAddress: text("ip_address"), +// userAgent: text("user_agent"), +// userId: text("user_id") +// .notNull() +// .references(() => users_temp.id, { onDelete: "cascade" }), +// activeOrganizationId: text("active_organization_id"), +// }); -export const session = pgTable("session", { - id: text("id").primaryKey(), - expiresAt: timestamp("expires_at").notNull(), - token: text("token").notNull().unique(), - createdAt: timestamp("created_at").notNull(), - updatedAt: timestamp("updated_at").notNull(), - ipAddress: text("ip_address"), - userAgent: text("user_agent"), - userId: text("user_id") - .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), - activeOrganizationId: text("active_organization_id"), -}); +// export const account = pgTable("account", { +// id: text("id").primaryKey(), +// accountId: text("account_id").notNull(), +// providerId: text("provider_id").notNull(), +// userId: text("user_id") +// .notNull() +// .references(() => users_temp.id, { onDelete: "cascade" }), +// accessToken: text("access_token"), +// refreshToken: text("refresh_token"), +// idToken: text("id_token"), +// accessTokenExpiresAt: timestamp("access_token_expires_at"), +// refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), +// scope: text("scope"), +// password: text("password"), +// createdAt: timestamp("created_at").notNull(), +// updatedAt: timestamp("updated_at").notNull(), +// }); -export const account = pgTable("account", { - id: text("id").primaryKey(), - accountId: text("account_id").notNull(), - providerId: text("provider_id").notNull(), - userId: text("user_id") - .notNull() - .references(() => users_temp.id, { onDelete: "cascade" }), - accessToken: text("access_token"), - refreshToken: text("refresh_token"), - idToken: text("id_token"), - accessTokenExpiresAt: timestamp("access_token_expires_at"), - refreshTokenExpiresAt: timestamp("refresh_token_expires_at"), - scope: text("scope"), - password: text("password"), - createdAt: timestamp("created_at").notNull(), - updatedAt: timestamp("updated_at").notNull(), -}); +// export const verification = pgTable("verification", { +// id: text("id").primaryKey(), +// identifier: text("identifier").notNull(), +// value: text("value").notNull(), +// expiresAt: timestamp("expires_at").notNull(), +// createdAt: timestamp("created_at"), +// updatedAt: timestamp("updated_at"), +// }); -export const verification = pgTable("verification", { - id: text("id").primaryKey(), - identifier: text("identifier").notNull(), - value: text("value").notNull(), - expiresAt: timestamp("expires_at").notNull(), - createdAt: timestamp("created_at"), - updatedAt: timestamp("updated_at"), -}); +// export const twoFactor = pgTable("two_factor", { +// id: text("id").primaryKey(), +// secret: text("secret").notNull(), +// backupCodes: text("backup_codes").notNull(), +// userId: text("user_id") +// .notNull() +// .references(() => user.id, { onDelete: "cascade" }), +// }); -export const twoFactor = pgTable("two_factor", { - id: text("id").primaryKey(), - secret: text("secret").notNull(), - backupCodes: text("backup_codes").notNull(), - userId: text("user_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), -}); +// export const organization = pgTable("organization", { +// id: text("id").primaryKey(), +// name: text("name").notNull(), +// slug: text("slug").unique(), +// logo: text("logo"), +// createdAt: timestamp("created_at").notNull(), +// metadata: text("metadata"), +// }); -export const organization = pgTable("organization", { - id: text("id").primaryKey(), - name: text("name").notNull(), - slug: text("slug").unique(), - logo: text("logo"), - createdAt: timestamp("created_at").notNull(), - metadata: text("metadata"), -}); +// export const member = pgTable("member", { +// id: text("id").primaryKey(), +// organizationId: text("organization_id") +// .notNull() +// .references(() => organization.id, { onDelete: "cascade" }), +// userId: text("user_id") +// .notNull() +// .references(() => user.id, { onDelete: "cascade" }), +// role: text("role").notNull(), +// createdAt: timestamp("created_at").notNull(), +// }); -export const member = pgTable("member", { - id: text("id").primaryKey(), - organizationId: text("organization_id") - .notNull() - .references(() => organization.id, { onDelete: "cascade" }), - userId: text("user_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), - role: text("role").notNull(), - createdAt: timestamp("created_at").notNull(), -}); - -export const invitation = pgTable("invitation", { - id: text("id").primaryKey(), - organizationId: text("organization_id") - .notNull() - .references(() => organization.id, { onDelete: "cascade" }), - email: text("email").notNull(), - role: text("role"), - status: text("status").notNull(), - expiresAt: timestamp("expires_at").notNull(), - inviterId: text("inviter_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), -}); +// export const invitation = pgTable("invitation", { +// id: text("id").primaryKey(), +// organizationId: text("organization_id") +// .notNull() +// .references(() => organization.id, { onDelete: "cascade" }), +// email: text("email").notNull(), +// role: text("role"), +// status: text("status").notNull(), +// expiresAt: timestamp("expires_at").notNull(), +// inviterId: text("inviter_id") +// .notNull() +// .references(() => user.id, { onDelete: "cascade" }), +// }); From 9dd7f51eebb2bc47663e657d5dc63c5aa6dea794 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:07:38 -0600 Subject: [PATCH 092/126] chore: disable TypeScript declaration generation in schedules tsconfig --- apps/schedules/tsconfig.json | 3 ++- packages/server/tsconfig.server.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/schedules/tsconfig.json b/apps/schedules/tsconfig.json index 3c0b02bc0..3d4adb168 100644 --- a/apps/schedules/tsconfig.json +++ b/apps/schedules/tsconfig.json @@ -7,7 +7,8 @@ "skipLibCheck": true, "outDir": "dist", "jsx": "react-jsx", - "jsxImportSource": "hono/jsx" + "jsxImportSource": "hono/jsx", + "declaration": false }, "exclude": ["node_modules", "dist"] } diff --git a/packages/server/tsconfig.server.json b/packages/server/tsconfig.server.json index 7f349eb82..9a7cdd1bb 100644 --- a/packages/server/tsconfig.server.json +++ b/packages/server/tsconfig.server.json @@ -6,7 +6,7 @@ "target": "ESNext", "isolatedModules": false, "noEmit": false, - "declaration": true, + "declaration": false, "moduleResolution": "Node", "rootDir": "./src", "baseUrl": ".", From 716c1db7992a720f8c2620a771497795d9504528 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:13:35 -0600 Subject: [PATCH 093/126] Revert "chore: disable TypeScript declaration generation in server tsconfig" This reverts commit 87836d23c37837a7571b1b4b5dd60f87c2696c16. --- packages/server/tsconfig.json | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/tsconfig.json b/packages/server/tsconfig.json index db7ded39e..0eb9923a6 100644 --- a/packages/server/tsconfig.json +++ b/packages/server/tsconfig.json @@ -2,7 +2,6 @@ "compilerOptions": { /* Base Options: */ "esModuleInterop": true, - "declaration": false, "skipLibCheck": true, "target": "es2022", "allowJs": true, From 7429a1f65f3e9f39dd452690b3610fbabae475a1 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:16:51 -0600 Subject: [PATCH 094/126] chore: enable TypeScript declaration generation in server tsconfig --- packages/server/tsconfig.server.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/tsconfig.server.json b/packages/server/tsconfig.server.json index 9a7cdd1bb..645d3425a 100644 --- a/packages/server/tsconfig.server.json +++ b/packages/server/tsconfig.server.json @@ -6,7 +6,8 @@ "target": "ESNext", "isolatedModules": false, "noEmit": false, - "declaration": false, + "declaration": true, + "declarationMap": true, "moduleResolution": "Node", "rootDir": "./src", "baseUrl": ".", From 579faf2f58273536b5f325d67a32e12f8c903790 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:20:27 -0600 Subject: [PATCH 095/126] chore: adjust TypeScript configuration in server tsconfig --- packages/server/tsconfig.server.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/tsconfig.server.json b/packages/server/tsconfig.server.json index 645d3425a..cd408f7ac 100644 --- a/packages/server/tsconfig.server.json +++ b/packages/server/tsconfig.server.json @@ -14,7 +14,9 @@ "jsx": "react-jsx", "paths": { "@dokploy/server/*": ["./src/*"] - } + }, + "skipLibCheck": true, + "noImplicitAny": false }, "include": ["next-env.d.ts", "./src/**/*"], "exclude": ["**/dist", "tsup.ts"], From c0a7347ef56b5edfd512bc6527453a9bd71a35ec Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:22:17 -0600 Subject: [PATCH 096/126] chore: remove additional TypeScript configuration options in server tsconfig --- packages/server/tsconfig.server.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/server/tsconfig.server.json b/packages/server/tsconfig.server.json index cd408f7ac..7f349eb82 100644 --- a/packages/server/tsconfig.server.json +++ b/packages/server/tsconfig.server.json @@ -7,16 +7,13 @@ "isolatedModules": false, "noEmit": false, "declaration": true, - "declarationMap": true, "moduleResolution": "Node", "rootDir": "./src", "baseUrl": ".", "jsx": "react-jsx", "paths": { "@dokploy/server/*": ["./src/*"] - }, - "skipLibCheck": true, - "noImplicitAny": false + } }, "include": ["next-env.d.ts", "./src/**/*"], "exclude": ["**/dist", "tsup.ts"], From b1e7ffea21d4386d1e46a9b26cd5dea902a6c825 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:22:35 -0600 Subject: [PATCH 097/126] chore: enable TypeScript size limit bypass in server tsconfig --- packages/server/tsconfig.server.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/server/tsconfig.server.json b/packages/server/tsconfig.server.json index 7f349eb82..33777c025 100644 --- a/packages/server/tsconfig.server.json +++ b/packages/server/tsconfig.server.json @@ -1,6 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { + "disableSizeLimit": true, "module": "ESNext", "outDir": "dist/", "target": "ESNext", From 0ea138571d45e3b163af913df5480cd93f423962 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 00:35:08 -0600 Subject: [PATCH 098/126] refactor: update auth module to separate handler and API --- packages/server/src/lib/auth.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 2b807a57d..24e23bfd7 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -9,7 +9,7 @@ import * as schema from "../db/schema"; import { sendEmail } from "../verification/send-verification-email"; import { IS_CLOUD } from "../constants"; -export const auth = betterAuth({ +const { handler, api } = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: schema, @@ -144,8 +144,12 @@ export const auth = betterAuth({ ], }); +export const auth = { + handler, +}; + export const validateRequest = async (request: IncomingMessage) => { - const session = await auth.api.getSession({ + const session = await api.getSession({ headers: new Headers({ cookie: request.headers.cookie || "", }), From 73d3b5886745b835551347a418016511dab11b69 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 01:59:00 -0600 Subject: [PATCH 099/126] feat: add GitHub sign-in option for cloud environment --- apps/dokploy/pages/index.tsx | 124 +++++++++++++++++++++++------------ 1 file changed, 82 insertions(+), 42 deletions(-) diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 4a85952e6..768498e8c 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -62,6 +62,7 @@ export default function Home({ IS_CLOUD }: Props) { const [twoFactorCode, setTwoFactorCode] = useState(""); const [isBackupCodeModalOpen, setIsBackupCodeModalOpen] = useState(false); const [backupCode, setBackupCode] = useState(""); + const [isGithubLoading, setIsGithubLoading] = useState(false); const loginForm = useForm({ resolver: zodResolver(LoginSchema), @@ -160,6 +161,26 @@ export default function Home({ IS_CLOUD }: Props) { } }; + const handleGithubSignIn = async () => { + setIsGithubLoading(true); + try { + const { error } = await authClient.signIn.social({ + provider: "github", + }); + + if (error) { + toast.error(error.message); + return; + } + } catch (error) { + toast.error("An error occurred while signing in with GitHub", { + description: error instanceof Error ? error.message : "Unknown error", + }); + } finally { + setIsGithubLoading(false); + } + }; + return ( <>
@@ -180,51 +201,70 @@ export default function Home({ IS_CLOUD }: Props) { )} {!isTwoFactor ? ( - - - ( - - Email - - - - - - )} - /> - ( - - Password - - - - - - )} - /> + <> + {IS_CLOUD && ( - - + )} +
+ + ( + + Email + + + + + + )} + /> + ( + + Password + + + + + + )} + /> + + + + ) : ( <>
Date: Sun, 23 Feb 2025 10:51:50 -0500 Subject: [PATCH 100/126] feat: add Linkwarden template --- apps/dokploy/public/templates/linkwarden.png | Bin 0 -> 80831 bytes .../templates/linkwarden/docker-compose.yml | 40 ++++++++++++++++++ apps/dokploy/templates/linkwarden/index.ts | 33 +++++++++++++++ apps/dokploy/templates/templates.ts | 15 +++++++ 4 files changed, 88 insertions(+) create mode 100644 apps/dokploy/public/templates/linkwarden.png create mode 100644 apps/dokploy/templates/linkwarden/docker-compose.yml create mode 100644 apps/dokploy/templates/linkwarden/index.ts diff --git a/apps/dokploy/public/templates/linkwarden.png b/apps/dokploy/public/templates/linkwarden.png new file mode 100644 index 0000000000000000000000000000000000000000..843f681eb9fe01e745aa4a4d2935b69ebde0c825 GIT binary patch literal 80831 zcmV(>K-j;DP)Lu&>zcm<7AogT-dAX6pu%7;Pr2%8H8Y_g*H4 zxw-#;fA@s%{TOEEIdh(84kMovU;6Gx-*}6cZI`R zH{oxeyu9=NSDqjC=~pS}2>`W!mpbcfq8U~u#1dc$vLmof>{fI?I^;%xM3G}A zr$e&%5jZ4=(`F(N1>kbB!$cK{mT7S2E)2a_W4!;EVMuuBAd{($x5-j)^<(;#IQm~w zw?dA5+(ul$qP5JU(`b-|%}`zoiL+0L+9wcBhakKpD2|8H;cE^QpF^?is^V{S}* zijPeXVe2?Gj%#o2QEW}xlI{jyc2dcQpoT#2z4 zgjWi@O*XF)a2?g>`y6bkWn=Bh@kK4F&Kv@m;E+O4d_3s+jE5il@aMqCd|=9oj$x@OW^yTZ0(?%F4>g ztLNvpPo+-nU(GY-Tx&nMVu^D{<~uf?1h5gIP3A#{H`8mPH!)M9x#az;|C*|nNHsQM zi1FPz6z1!oQ8t)>xyVUsKoz2w)~x9wPb8q&{1~+`U=nA5liJspz_K<=X`R$PseUra zUW5vbAQPtG(MAo|Pl~o!G6_`IW{Bp7RKez!OcfP2T|0Ns7q~l&zi?&fDmRV*P;dMf zOjm=#*nqo;IeA`+eU<6XoN)f2t&pM+V_cD6bBb3Ghb=|{QZL*-k30`{e*2t$3ivaa z!;kvrAB8zSQ=ebrdA^ME4mwmL=d1f`2VG}JOu`7-;SaIk#07F=Od0|g6fBRl#Bx}@ z;3^DLtb*XOiDbn^|M-QT=ev0KKSWrU z?5_A>K@jI={_~JQ0TF}#d|@%Cqa3`(5YH_Pvjm8<=5ZJGS{R2l4mQPcbI++@&BUgu z=BzMuRT}{<*sLoMag)4Ot~pMeM3c<+{sNOG+n-|#uJ?FtTQ@;3x2GJ0I^M8{+~v|# zhf9xBTG&O3WW9y}Od%mquKad22}j_$Dy6q-JJY%mF$UPE=!+Br_&)SGRdf)#vd75I z5X?o>aREzq5eDX_7WeuPpu6UVIKW~JeuVMmNpU}k4V2tOP5l70s#hQ+1ss%g|So0wSTI}&Vuc4iaKwYx*%mbK-+V=5d= z%VF8_yQuq*QOB9pX+R^utEr1*Lo{U$Q9>Qr-xaw)71C5u@Iheag8dxu)3*)gTiyfU zv2Q&1yVQr%fgpl&s7r!$0In_W2z=mYqH!%Uqx~dcQ^4)Rn+1kRDO>QS;0TUSK8)+5rrH|96s_rObG*3jiR>P{G2AaNNLs=aj;4}Jc3Zr*hTv)m(F7+kU3$EG zI_>CQyRIC3BJoSHofUM4CBK$tv~-R15HMg`a;L~LuS>NEU!;cG{2G!gjNandBl3`< z0>&dEDf*y_fDh<@qTdbtF@PWaZ9mRG_@N*C(eM7{5B2#*&O7Q9Smim^fr_Gz1azp9 zEZ~wNT?`b0mcXnGe5dKXYIhkgvL^#}@!~}f9IqRO2dOYebJCVa-{Y3fW#x{;j4IQn z2NN2a>N6>{30*UfW5*Co8eF9MdfbIA!@S!qUe6$|+qj2pn;I@HTSIVG`=^-9F=<|O z7iYpN^iE5t{dy}W9cq!g00du%}B$YAa%^(}y(L?!1a4;(I^BWu1`ggBk zdN*SZLFUFTg1%@A#>K!;P{mMr9)$1#9})B+pL`TV#W!y_=ltG3{+-_s{Fi*udwk5t z^nE@4I_CgYNkv%)&Y{Ypjum#OqKx7Mq(KzrJ#rVj2E=TIa6YN=s@HAw%mvuo!%1JZ z&bUZ5+FF=a*-3*9RUA#*(-yyfALBY`-B@~z7wsyuN+CP%o0}KHjt1s+X|vyJFL1T9 z&bzRDZQO0PXct5Fy&XY>qZqyd&pXEo`(H9%x~|(9R7`5xHI}VViyj+U9Q)}M=6td{ zQ-r-ooB9B8#2ELIAzJeu&pKL80+Tr=)vhM-c`qjEYTJPe05XSYk){Zsx3Hse#ZP@e zLGubAKL00xk3cG*JkL8%KLq@S!1K}H`ra?-^TWOSAL`IK3DW7Dw0F1qdk#^YL!C=U zC*oWqQ!`c~0g6o)SBX|r@2HuEt2HHdKcMZi-iiHW!W<>8ewh7BA3>s2>`Fn@AfUmU zYt$6Q8%k}P>;_N@WZL{~$#-q8mK;Y6(WyC^-0E)VXcPv5_TrqbimUkOB;z6VQ7{n% z5MGZ`f$qtqCU;D65vIJk>9hB3;b^HdIQiTlRd!W&SE&nTnB$5V0A53u(2K|2_GnJz zu89oca@zj6yVDZwE}vWqE4_GXPb?J$1VIf{pHiRa0fgs4AJFiDX9y(v#GAKr&M*GM zC-W0;Kjxd?`*iB->m3zT0#%&ww!zRja87Wn=$tGfC@Ctv)nd8e5#S(|k&`tyZuI0g zPR=;?jq~C-e^&rQ>?oxFk}X;uGQ{!M{EIzoQZk?>_Z*mz5mb%Ky|dP1xbQ)u7OcA z?L?tan7g$F#Fx+Z4o`cpy$760hc@rR?hMu8U7LbtyQ)+oNWiS0EDK!<2NC9&e!08q zK_hcJBOm51+xB^PT4+EcRkVpA+8}4?c@O|gK|}PB4xn%#$wvj~nVnb$=N-v6fArs; zPk-ARzRI3Y;k*MN5V|@-x8lnoj+~%_9Vw{lU`f%fAm76~C}*IV0W^BToZT&3rM%k* zz`af(8nwn@3)l0;M#;swCJ=D#;R{R3TnRXn`pEm^5sT7o)ejK51SmkWzxyux%+C_s z&o{LE)sr>jo(#TKs40ZA>1=TFrDtI7)k;|dmYt0;SLQ0WB8A{piNVBz2n=2i2typrP6L{~0C1W;A~-fP?_La#%Y9dd!4!Ad zk%VK;?(1F^0&K9zv?yAGshz!?5I~dt)jI={hRVA@NR{W>o+Ca04|rgPuyQGiLJ58I zN&Qb>_m*>hAmrYTsGzJ91s%Brc-v#nk&1#2r?RAqyj>?(NKb@#W4Tf!J(Xe*glr22 z2{?Lhso7W%Ky(E?z0u$VzFLPXz(GP4ulpVveZn#?4d4)<-nr&gFco&wY6MO18n8}G z0h?W_yuS|3Mynu+gaSv}c>O*GfJ^*ijsOZJ7z+g?_BwBYA%g+Aj_XiIlO;IYKn^50 za{xvQ+Bu~TrK(biKxseBbJ0=dP!h0HYq%scSWyS8&5Sd=(g%*ukxEdd8r$+FcqsAG zFq5Jwa&FD0&a2rW!fMP00T|*oNxSQtyB}Wf9x(*D3%sJLfTW;gsAlOyAP;avh3BD8 ziuELNWm_rm&9}V`zf7Qjf-8Vgat>9TBj*FQS2}i1TndDa6_FIiJ?`Z+`$3xqDhLp7 zMb66LC+&%#GVI6A~# zL4n>FrD=WhTj}#Svv1Fks)CAht_{AG;kXrI;Ls@w3W&-;YX5KbKz94c5J_tIj#>D) z!pF`j0OZ8oS>{`trX-l*7WYWNRJ#4te5+FW>Y3sgXvFoTnaZq@U{zF0FUK&V) z$^)GX5au%Gm(oascbb9s-|q#)Sh!~BL+>C(AJcrKbK(I45BMks1sq6F0OBNXqyj(t zE%>2v#qY{VLNFfI54i0ylob%2V%Xmk5hOM2%ZLol-B>RdiZFkUBTi?kiL~h|_+LKG zE?0J)$+2X9=b_Pq==YvwO}xD7Wd{}r0p*Lt&3)36L-8ky3K_%TrIo+4=(bUL8(TYbUElLO(PHJF_zrUF%}*Og{8Z_F zx7r#E^|W|QixnO4rS@TKMoN;a6bmIO^y2QS#z?0FfjF> zi1Z{OOi^=H(KiH6AD2J`Zo6MN$LYG8j~!YEns!Uk+IBhbJ!AxPJg1s&i3|DaE^L9P zCbNfTTY!ci>DT^x66+KBwSc4=6bhs@yundI+F$?gzifpPfm5J$e$ep2NArEh4VLc% zz7`0F68YOHPQwRBN9=2}aOuDab*?6tN1R7^1KtH2n+}&3EO-*a8`wC<%6i_+9e* z5|$W@LwbPmqT#>xPZcba0r38~Qn(vi_^gE9-B7vysfhD8JU!aO>=K^#*C+?6YVHN* z7rrj4O^?-j0;+EVqeL zFvQMY4A7o4vM67H#R_o-^O|Q66VSzu@-XeGVPZ@6i;DC4_QkXI!PRCrQV!9$Vk;@9 zWA6TMh`6DbC@nqnC+b>5P-k?E=3=n-Nq&-k-7Ns)y`8Pz-Lu|(4z>%(RAKwQwG(ya zn}41IU8akq$mj^Ne{Bd;wTe5??g$#`Z!s|3e0P;@W|PI?;uqFhBeVFT9-3cOCp-xm5WmwN&_1@{iSEurC(?1IpZluvkNBue%1o>{v`p&m{tJg`>oBA8IR1;rcr|XUQ;SyKFAoqkp0bXSuFF8USoX z!J4su5-0kTbANBdAzh8_+68r!u0OS|CyyfoJ{}VW19B*d@IL_}l!2(=h ziuim%>IVtcIdFcGmvdciWo`38YDlNi($*Vft7{Sg6F&L0qO%e%opjkq zuGQlmgfILSZKBmBfrA0)HGjyLgWi3c)VgSep3z=O0o~FAxhInVBWW=ReY48e z&u*Ejzo{c9YHY_EZUgZ2cc)L#R~rgYzCtL!4wnRW78>=Pf57r5Z#6D)Nz%Iq3C05H z$7Rl5pXDF)4!oVXIAP4tg>_7 zoG%rgYSgy^b+|7rzQ{tMkVS zLLcwP>KGkuO$8s9!UQ*}iNRsyKYIfsd| zH(oec80w9VlGStz`AD_CHSS8GHz@Rkg}Y>sDm-QhrLhyx zf&d>#e6}GmgV*p1dz9p?KmZB3Gva;tVuZ2oQ#IvH^2N`mM#*d>xgj+nB=0~+t~O3R zRN<-hoM?}prs+kXNbC!57Nj-(0Loj&ChcrF`3SAYBUr=0gCp&*fKf1A6m-Pw*of~ zAW~XvE%29=a#nz;xq*-5L!Uyq>}MyJ`}d1GLOo$*as;FlFm)5O{pTz{If|LYg|}v) z|NNi-_y7MZr+sVwS`PVH@XSYmc>HaGu9yecuqs^qtzX`^Sk^$RP32v7w-(!2Utg%k z@$_J4ngC(2%VL!*PxQQblX!kvRT|GSRV^tJC(4CET*ukqaxA+g*=|1o>SqrE`@&9G z*(MjIZBxMZu6#7wJfx0;DiC$1qDP5nWr8kEH!|VSjtoC#G|{ z#2n4mQ&zjWs=xV{wk;I>gSPGaSXx(X?Aq`-F;pXjo~oa*JRMA4L@IM%^Dl?(&aHOW z1$%5}tLDWWh2tCLWxeXc-a_U|Ja&hoKWi6SX+w+uW^LU&9o)2O+rQUf{r%JLUWwKE z|D^Y3=r;G>z$Cw!Fm;JF4<@XwOXoa;U{O|^V&d_3gdmsW+f58UMOyGF)lv*+mTliC zp_eUuHID&i-i~quHuesk$$vH2SKvDPJf|&}M-drCz6PcDx{%|IIz6^EU)dzgQr*J0 z-X|$(M~$s{TYo-Tm*V`jb@25^Y>o-GVE)03;O;}+%~mLljep?N^n`-)s990z1%`&- zji|dNV%-|`9XFZvqrW^a-MYLphAcaUl;hFQN z2Ok5P8u#A@<=9%&^k5B)_sNx7hvoYBe|f#y0&${_drRw|4xTEC!tN;PvI=<=J$cAF zA9z{y*WIK-K&df&_kuF*TUKpQn*r}>3)cAF@!*xm!&UfP+H@rzCV6_Z?6$TuiD^!o z5r6D9@}b**&4U(9y=R?Xhvpq$cdR{C`kR5xj={Ozl5IbGaV-%I6Rhcff?L>CW0#g? z`Ggm0R*~PdvFC%U9`>#Qe09OFwqT~beOfn|S`C9cNNk*9-?Z&hBcXNZ-!=ZU6M{{> z$ePtDZ~t7Wb!@wqmw9!wIG5>msoU+hO3~TLd2rlw{VKY}T~<#1p=dR+(^m*8w3U4% zbm^3XhhVPP24y%R<*stXf36i}I#Y_|*!Zew5tlLqeBN|6UT^XZKYXF8 zx;K`Itc5Bj(NgYSD+1?f46-S$+`g$HPd@+)6omv2D-|elb2k3L05i!zxDzgxS)M>_ zA3k*KP%S7>Csa6+&7t#|P)7H47@OI>;7cl)X+|}vZz)^ep(Pf!Aa;%Rg#j89`^PUm+WWHJ(nh^fMHws7vB9bb4(4n2{t9sQSe^?%YfKE2m@MF=o1;%4Oahn8ke zeY(6a2=`Z{)B^>}ORw$+O~=0e6RK|%_5;T+A2&FsTks?>g>okk)W2509o8>MH-X2a zz%t#2FYuPFkZXy>^IvB zQnvah$Oev33@eAw6yHr#15cWCw7pV!K?x044|4ZmYK@eQoG-LXqT9`wMi)R}3^-|3 zYK1?+cw3U_1s{F@$ii8V47dgOli6hW)fU0fN(Yn!c1&$plT<;kY?%4cTHz z>H;;6Q#*Xd9tR07UIeKz5ek{TlgBbJ(@x2Fs#Cbh=d zN~NU;535ZFh-rp6yX+1HVp>fF&|f`%vIaIWLY0);>{8r}0U^K~8Kh!+ASqw}E(L0R zS2-r=(Yr}o03`?WF7@x5Rmqy55Dkgo5N5G zzL9glxvbhtdg7@eZ^29m4wt=!`^d5GlfL_!eh!Altma&jDdd2vrb)PE-ZnQT4T|?E zOH<%E7~mejItk5AnApQ>!W!eSn_-~~K9cctvdQB5!p`q1X&yKP9=UDeIC%|Wg;P&% zVMM?WA4!V`IJgdg#ZqB##bx#5@pyqXu-bWB4Zn{^Dsi#*G;jjQQBHG)B*kq#jy0%X z$*-234h!MFOMQ2ec3%Rkq~VqSJg-_bs^-SiPv!UDemnW?SH`S7VWHHtCo0jjp#v;r@ojoO#kLx~(MRLJjqUvw%-XH6g%(2HTM5-LsgI znbhlL7@OJh>`_cq^W9ca2sGdd6giCXfUQwTPCn3YTS^}YXZ7m*+9#IBy&JI;2*9%s zWyd1`WdxNFfUy$PkEM-8pzZX$eo0~W@E-z9GqX_(xi6}=7>ik8G{tO@iq+y18{<5Z z(c};I{sPL<>pb&E-``i=0!x-zwk&WwftlbiW0HX+!*Q5~nVFfHIn2z=I5S~pG|C_| zNosXH_ncb)Is3Ps^{uK-F5jD-Xt~v@I(6!Nd%ydI2N=SVYe~o<=#d+b=lav0mFv%V zP6n7KZj5;+o-riZYw&dWtLC0u9efboqQ||T>Nu$&1gc+1*PYS)s8}O1t*=FM2&yRQ zO9r4C0ft2lh}IQOE_2>^E+V6GWJUp10ua+#uGsMy#v0u5>wr6A@<$$Cn!Bh3Rt*&; zh*)^uH8NAlmw0aKpHMTjpnh&h7AA0^3TEh#&<*x+pVuRKn8f090FefFlEP3|9v2rj z)tpD1M7(SDprm}SU6dq4=>=qr$%>2;Lv|es8+;l^-0^_JHIM>u)?SEuUNJ(GY5ZRr zC`KPECZyxAT6Qo%obX`Ui$K|7AcJK=l)^%679I8Ag))$@XJD8o9^=l3e(t>8U;Rkl z`JaCw@AS{UfS>#KKW(1+E8Z)oY|xaV$}X1#00HpM@NcT_fER-pesKjtX>EX+0HtxC z_+Q6$u2Gh>FLYWvE_D?2n(UyTjqEYZAxNZZWtjvF&K+1#{gXASml#!y!g{%YZmll^VEz zX~^iMs~izepS+4MC?#%l%OEq82%PVv$;4hTg;1YuRU!KR)=%n_eSYJ{-2G0!Y~Jx7 zefHe@J|8xBzRf$$ozMGu^R!>_-t!KB>l1j^Z~5@U*E}9?TBntEObcdqs5?Q7 zh18h@1DXBytC{UG*JY4e^Th23{7Cx_vuN=Ib{GjHShmND9@0&48sndybFM%2S@XO< z`cLPM`=3AOk3Kee;>N{4=Z`-&+Z|7u2Y=t+o;x0R+hcQ(79=$FGbL}lBq8cbUYj-D}nkCvjGCjt0$#^NQ zy)*!To(WdPuL3OT5XpG$#;9%gSap=48_ftJLdmOH2PXcMqgp>^26^nz&E*ES087ml z+(;06XLm+tk$3X-1wp7vA{Siu8K6h#+uV{J@=^|d}EEZs*$MHJM!)M1+b z%nn{TD}oZ50V1ebgT)->Et^NdO`B-CRa}hAbkzp!de$L4DMMs4-Ice88O#Svl8|90 z{?WeW$E51GUtp6x^ExeYCs>8h9sY$AfiLkWzU7J%s5^ccOJD~wSFBEb)1H~3zc{I6R@UUW0qe%nl9z1QGsK_cl28ji0?iJ=cLwcHB~Dll{&j2~5o2=1iW zRnx00jA2M>$W!M1jkspWVAl)ELb^==!`5?bU)@c*LcLK+Bm;)jdB<9M47IV7#B6YB z83kO+K?af}rC18PDRxT#P#(uKgTG(pj>X~}KxCsqoY(rwr&}$h;}!)Uy0=#>=<4}M z+k&}l=_RU7ugIiTR52Qn?LY|KOXz_CWeBXdq+A(H;-9F5N(Ni`y3ORD1;oc`lDO}8 z=Y#Js5B;HkI9$8#xy1fDKX>BYwhtaQWejtKe&GPEE`!l&iJ)%&n(AeNAw_ig$bzuo z#y*vzYxRYgg}~UIYb9N#S|%Y$&By#;x*44a)rN~Z8Uh6{^nPwysc)A8(`!tonv&7P z%?oCm#QAjCua~_bXlk_-=rPU^|BoZTGI8*kg`#%#;gTpS#S=Q6G@WQojbKwvunSHd zQ=SCcb^?$wC95^Y`Yw$@R!W^B(d*6vyseD8ZG4rtU^TBw5rctZE<3|Gk9+7T=|)=) zT;5N~q~mYTGS!neBw67XvMPloJ&N``gB$&$7bH2K?fUI`;P?DZ?tI|wFUs6YmrS?g zk9cDQH;8NYGL@Pc!IK6|BBn%*G=rmu1s#7FBZ!Hfx!s}&2JpsHcpt*_KJo><;7i0R zE>pc-4f#YjY@@qGCO~2)*08E}NNa-E@H?t>M4BjUgmQL2-7rZed(QG(acW#+(FbyO zw&8xUn_s69JV}yLJuLYpx<{|<&5(am)~KG=CPXgwW=d99bRWs9jAJFi%n-em>LgOM zA_MEKXp}y5Tn93WR9@O3J!>@Ev)D0lDut-qINzWe*l`4f-Nl`H+xDB(ct7M85lPJcLk zsf0#+yG9@p`UOb3%p5BVZ)3pzfcYB{?*3GlLPJekmAfC2i0)nu&`s9x%$ZqF6V2lt zqQ0LQGOjG#I%TNR%0(ODJofa-@_fiJdjR=>Nz;lp*(NrK=uK+`V4LEVEOd$f!p6)H z8HLtdzxyE0Me*5QA~L}l8}1_|kcK2=XAT5@yNyp9@Lr$jq4*Sb!ENCumDPLl&DBp$ z)VrI`U`v!l0&_LhZP_T7_4L=d{Ir(i3@-V`a0N&TF``VRcn(SF*Bf!09-NCIr)?B~ zcoUS30oGG`;&JYImtQ##{hq(&PW#o%z*TR}mWD4dLlWlzd-$=MSG7P`pD7D@qtvY` zo=MznNm?!4Xc%g@$sbA!$VQ#1_DW@4gvCh~AbzB~2Qn&MnW;TRL|L`blo8|(^esdk zosUd3AMyA>)^U@@6|;5X*d!xQmLQ*Qz{$5D`76{F7-11Q5B)QLD3ce3aI&{??j#=u!to2QS;h|p$!Z#RuQFvV3WJk6@*o2nDueG9M5XLxVnq%F1B$1 zN_RzZ5E=qzrOYryRh2hP_ws~b=mt6)7{83{65NN8yG6PGTeNu3DFb6kPJd>DiQAb8 zgqaCL1kxv~o}Fq?d)QZcwHnRU1^>(-OVc0dVejJ58glNONm z8?vNN3|8neDI;akBxufp9sFGGlXy@N22z=;uu)7hj(?e2x@e#Kfq!nE{4T$0;;HXd zD?Ws9GQ%@l-?f>BdjnYlDq=xZYrli5r^yoEof%>QWu(9p&6g~3Wibu|Xn`5pWgHq^ zcOJ_O%9%xtOa+b}?{!bRoTW~3tyyX=Dc);_Wuhg;Pe>X|nHHI>m82^_gi!!u^dy5# zk>c8|k=qVvN*GgxVE5U=Kzg2L&AoQicD0{L5u=k}9$uUAFHTJjDXJkxlIRVmYX+O0 z0mRBOrKeNmsE8J*X7NTcS+E)GtWDvIDy8&p5wOnrfd8U~G^&_}(f=!q)0|r4y0M=Go!4X8V^@0NLuqg`eWu$^%R%q6=`mXCJ z!vf>(Q4PxrhEi+~1c{;*z)-nsx?L+nM>`iWLU82;7MLZ*@=+zTgJCSJ>D|6_=ud8@ z&8hbH>^21i%)I%;+82eJ1DgGhQ&YiX2CcwbP_A?6qu3RR?tZT9Ocg zzR@QsLRmL^sfnuKfF)*5)OAXhP=x~dY2nl>QSd{Eg&;_-ghQyYpRJ=aB(p#7rpOZTu?`ttA61Ul*AqDuV=K6|r`N2X0r&{z=w6 ztktk=6UlLbX7qxWq>|+b-g=ClHwgSfH;IYy8#M{D1$N@;`yLx*_wv!SvelL;IWojU z(H%lN*L5f=CI@=CwD~5Zm0c&k9u9PgRVb?dlAOoXMnwTGreU{4>?(S8)(!AT9efbj!a3R8x3MjcCJiKsB-{n|eV|gaquG(}As= z&Conp<>)X>VjES<9|<72%#KnGf@bkZXWxp`{<4Ws{~g$yB7}8In59-M7*0p4Nbx5z zic#v{HMmtyw&aT)E0egGN_RY$Cy)=Q;6pp8TW`R)jA^eU6Jp1i2U!K0l*nE_MH4GN~S>2NCQdg(AJ`vuS@ zk@`3k?-M)$7ocu``rD<2Ta${9Z9yrDYM*-@veKqZCaH-VkV(LTt;62gSbJH*6Dqqw z$wY`-ldjw>e7bm9MB|i--Rtw-97^nz)qu5ja9!nA(vWam$w5tqs&_P~WwNISMsur6 zywpMpJN#W*G+S*qjC7!>FnjpD)8ErgXMasg=E5M75qc)-pNUcK85VK%&4#mCMT%l& zsOx#^!qURtE~|Q^twBKP0(Sn?M5T0IK*9gqB%jnpmK|k>s1eUm+wY+A@=ND|5Bn?r z=|7XDZNM5~Kq_KX`3Xa_g8Wu2sBH4L(-k?(Wnyi{^oOe`l4O|4fQ9@|d9i|8BYKux zCo~jSBIiL)W@xxr$)$SUzN4fanJki;mBgjDIf8q2j~(H_iWOHfwhkY zmB4+)>8VlTSnXm2TO+%9^dxaXaZ zd%2YFdeznuB>U~C)KjT%PK)lAu&!=3|DhXRh0b@~&ps6>#AVtpn!FyXre3>Hvu1jQ z!3Q%z#pBJJb9UEL=57AiKb>n&z4t=l`DeaFm|Sz!%goubPbR8M(4o}j%FtaRKe6PL zR8kwg20?0$`MvqudQ7H1UkhHiAJp@g)eWx|h|?TS9^rM_sX&qh5~=s-UBB3n-4gEC z_b2VI>V|$R#m@<_v#D`Pjm6|hd1VWwN*t%vNe6glhBAtF%L8Xea#kzupIqdYLQQs_ zzCl%8F*?DlC(oY7T>Gigt0(@?^z*1G96 z5F5&Bh~!fctbpuo?%TfAE?b3y!LTEw`I+*|+f8Hd>hqkS zu%(_Qg&C`I#ZBad&Hk!TCA@w&Z);liu!B%}#B_ClhHsfnt%^pJvD4;GCyilC8d?{R z0HdB@4=#=GAFKz3&Z7tXb{8`(D1ph^9$HLbh$VLXpdd0DF9JCOO*+)`D&v9AoYKXuNZc#NkXR9^0T!JnA(CvILI{0^Dn>%Qg7=jL1Ad}#?&0Qv&T z$`{>qKWEh$VBgBwY;V*B?~%WUoT!bGNO(pA4;WGc&g#plZmS~F9O~}RDvYphteAtJ zWP(s_jGg=^nSlQ{p$HA#y2rt3!#my4HhI(JhU)F50N?x1$>BVs;-MpZbp`< zL@*FF;bnZ!#C8KYWvjs>eQ*&8i15vQ#%Gt^Z`ZmPk*1GNf|rRML5$oOl$mFd7yaWwtCNoU;HD(*|oWHfkyv6ttK9c`RU0MY32nu3FMwg9|ic1v2aljR-f07 z7jwdNIv`Pz=yFI3xLbsH~ryELVrCjUs*F~MR1j1lncurNd%&nid>Ktp0oHixWzEPQ2J zjAIx{w)bQw)@Jw3EB^dX-|?<9m_yE=!n=I%ifni zfv>|<>%iE>RA0KfD0Y#GFOo6?HUTsWc+oQ?84?FmZ~%?v_Fq2?$n=*EFm~&U%9wwpEW%&*S{&oIUBu7v@E`J^eYC zLS8uZ{oeTf-!d=xxPO@0Hbz0<(nU9)@H-@l5iQQ5cll#Z)#yS|k0&j5LD>Ms+w^1s zkRC|3EMjZ?YV`+Va)#ehFS4S3`IV_X=%qd`Zek|U*L(8`UA70XL)dc2gx z&RQu*0vdMMHKj{R*DOH{0nc_?S^T7rAvh75Gf3+~N;!4+sIZxqkE*52Kr?wy-@5kd zEkT^(ocs(D=RDOVsF1CoCzBB7{5;S7pg%uP{Uz@?=a)fVwzIkUhS$ss|NGyV8?Sv8 zoSnJ0xO%xGA)lCbU+(5|D!Z0$VTP_OJf*;Ax3ggx)hn|Ro>a4EJilzzQtJT3JP3+i zjf)j8#86>xdW@42EMy>+>99-!GFmd8R9joU^!_^><+@E&UDQ||CG@Zbae<}qqRkkM zWLoL9XRatv=pm?V>!c_uA)~*HY4Vx$pc7z9Ne&k-LqE{C%)FwN;JD{z^6f*y5xupzh?h9Mit`==Oly-K3C-;NnuD)>SyF_2;gBv<6a|F zffRb9JSMTbT~rb4L|EF@So(PPoNQqUO2Zqi3cqk5g#Xb+%*_@4l?hI|fL5 z)|5WknfB|t2za+Y`=Ii25IEg#yK^R&Ltb9{zrSo={skY&oSo6?AXg`Xn5^RdUNc%U z%-VfUmN5@CGhvq=Y7kb#ipq5{N;I?#goo~_mL5ti@c9h!psA0`v{V9na(F<3#8N}X zUrQzf0~!1u!eB;`+al@4R?SH%)9J1Shi*Ca(g(KH&(T1}3&#%oz2eNQ+^OS@Z*f$= zVoEn9>T1!L^+Z&(@d|WvwU?9ulj+Q-<*cf=wr`WQ8^lkh*{SL|rEE`uqT>v`>xmvg zR8Nph+_IY_s^wibv~Mg^tMX9D^rEmpw4{95;*l%jj|+l}683@L^Ec*6Z}U!*OQycZ zDeq&ie964%qyEmt^q1|-zv`8%1?L*O)-V&y8d2%r5SR$2T%r#Il28xX09%zX&9~`< z+C+TOiY~$BhBEE;is~R&8`GxwjSIoU28RWmWuN@+=6N+#C;xZL`?TVmbu_c#2PADd z97dTq()dzf&Cp{zL-T(~hAQr(A65ejvmC<-6(4)m_VVpYHEnF1lu2RHfO4`+chvxD zK$X859IT-3v)1)eTL_aeg<>X;jaUwsrRqD5)i=UV~5;bHZ|z2$K12~*;~`%%wsI-yLEH}7KGVkZ8& zA+8xMJ2Rwrli3;3^nDrvM$e`^{Z!a4I$dE1vGOc#Fsoz`&Sa7)8_FsPP0yj#7Mmd5 zbJ*xXvFIVOj8-_Q17uiTbQsj+41obQvW5)qYy==loCSkUDF7?j__K}yuG;g2$xfd+ z^+~~IU!%PtaU`|xD^RUVPCc6=_r%bTIW{4RfgbZh$&m>C<~Njizxse-S@9%B}gRs zAWwpQ(FHf6!_vr-2hbjmoA_8-zdsByjx93C8!{wuY2#bps3D6~$%R8B!xEFk9O)8R zEl5Xo!d!`aR~;cQ*N!SLfBc`#ZO?elRk+=J&yKur z@y=-BRR`t<(t5+Sc({wnR6DbkCDH_!q|$BU>}z%{9mIOuNzdx{SFCT=w(k4fBj~m% zKx&Mip?wSy&(_N?mMS~@LzcK~IfK!BR*y#;dLNfg6y{a6eq78JwF^c3-zq&Y%P6EQPkX|*eo$mVK;J}KJK&n#3>oT6SxRhq2C7OnXEFz$yxcWz3rvhS| z_;a;BfNXhT2D$IIGLgz4--cmOFs%Szo2DSZ)yp;wk|eQ20v^w$XF80%g>5oj;OvMm zy64&18vL;?YzAKuw1O#NT;gqPGYq~>U_0H-itik$Zcq6o@4<6^=O3E$D=aO6dFiMA z>v_ZXd}Frjw|kI9lJxX!GFPq`rZNn;uWY7I)H0QDMf{kJ#Yf{{-{&O?Vb#~T<3#*f z3FzBK{p|x`p;DL)XZ@9G3ceai=9|ZDc{lw<`8)$|JB<*|Cj;}y;B!KpOB>QN{ApD;lN#!g`gjKt1pojW|xFwK%Y0wVX-S9yDi}h*20Fm|7>HE@G z*)U}q2MUzfAAZtc(KO4iB|9wZ-G0^)g>3|1h@zdj6-j1FBPEzv%Qm%IA2Bfy+BtyL zmN1Zd^@wD4J}Hh>e5S{EHha7=0pd4Svz8>LVyWA~=@|p$=FQyxtmn?#{Lz1MA#^&I z1V9c{x7U2*7tAX@|09R(%$ASNC*5AU#g(Dtx71f=**Q{j_o7$4f!>MMYg&8obM!QzOAgp_KzKy+yF0x(n)(}=a#l0_7TjVMN1TMMueTEJ3DF~{EU zxf8ZlxZ~)ia|>26DrFo@oJVY##L)B*;fYrRu5iv_@T9mJXkol?a-84?#;{XA!z}Fw zoh5vVQjCx$u$QqXdum+Gf}nRt>K3YbaUJ|nawiSTSs?Dp$4l|Lx(?aXuj^YDFxIQ< z9RZC?F|IO7FXIeTpKq1-ZCq|^t{xLifFv`_JotP5);#I??`V~mUC7JhuXyRa=wtr= zoIm!KV<{W}3v$qclUFZ|BIOayMUm{ z6F~vAOi;1hnpX?yqBeIsvbupDRbFMvKDflBe0-?ZVK)ej9H4;DI7{)NXotfB3Zud9 zY%>rhGXyMKOWLw*Jnbjc21rM|2xG?p(-J58u(X>M@38e$Q|bUJ2%~>|M}?-#Qry*Y z0Ma`x{U@wqMRop(-%9#Guuw%z;#!ABqs~(JWr&a%zPS-=ODTpxvE@-58&I9ZW{GRb zA%H84(&o_5ug<3ox14L zr8nmk>{iz16gRUv8zfQiUk zLTH@grJ)wqT=H-xre_uBwJ709WsLrE3}q}DwHE1XHLXjS&hmG)zcmPnLJ|d)-&kVt zM3I0=lfLV0Tgl%ri^?5p!qtLjK%4SRF9zcC9ZNwL2^jZmdbd=N1W*2ONu5G}8iu86 zML6nKMb3EX=`$d_mx;oYBxYV@WKT0%3VPmbefW%zUrJjrv&s}!T|Nrgg+_qX09lXsS`zPFS-`iZF@^XFN@?+n97<~MjOwK1O*oh=# z9Hi^CtCxNsi*$&c2Wf>XR?mw`)&+zI@Inoir#|=s9e@-TLCHWV{}z~53A(X{+6N6o zGt=Dov$RgXyE1?Stn-*86$egZp=}6j=#}K8vzc_}?75Y(%Ppanc4}#VvsP(tBB7PS zHvMP;ybJ;E5SLvHy}`xtBlGke{{k^TG|u5x8JCDEm6`4CuXCxhOp zssxt=k<75)NXcykl7VrD4IjmpU#QR)q0v%yFBSf<#9#-89e{Oue%xqZmztAOFwoRN zLxs@jq?4Nd#tGvlq1&3&n%}Ego181GKr@rLjR90fb;|kBeqiORMD$slqJj7j(og%h^SW>U>e+6)ofEZ;nF#1e6VP!gbNNbC!4@bv zMh-wSBzB+zpf$aW8t|ZR8F=*xCby}MJQtd#G2vw{H^y54Q!%DR?RSIh4tszm`!VsME5D?}Cya_`E zZ&7|~9~;5cNvCDc4PJ331Fn()8kHC(v=Mc3Vr|5pB;Ew78}+M5Lt4B&nc!5$(oA>a zQXLD2DlnekYerrf10|;1 zO5++*R8za~%r=%%B<`7(`nsq6B#yTMU|#<5dK4PbPS`2+R2Zn~hdAH{CD8*mCQOoe zEMYJrg+t7w^`9mYKiTIzg^tKThMqIb=E;JCdDJGXtIirN*`LlkIoN88bO$W`cVV}6 z8jqaxy^Y9d)!0TK#f%8rg+vJu1%Bc3{Vji(iKY76fu+iZ=zq(Tg_hlEeS}^7zT;Sn z0#s#QXFx=S;2};!o=f+udP9i=|0FcV8hHhD8Pr*cf5H7<)YPV#x&K4{!rb-tzhH9X zGL@IJxp63YU;Od^cy1m_V74>qGnKm)&2X5+Z;`or>6=t`mKk9 zmaK5eWY%Az$~5`sP@&Xyy+?U}#f{pQ*4{d@K$u4)Io_~ZW3y!pqz`^ffC zB$H5y*O2&O&cssHS1)7N+BKz(OvE=nL6j;=Q9Zy8pFMg2433^m7))EzYbz_2kOe?( zn%LG`Z@$^-+7C7fDdT9BLDoJ12FN(mFvF`^w2>xVqioyRg55MCnK%XP2548EeueZy z-&ni=%A}>+sC(~=1pFcTn&^oz&4=%?afNKmZdW^xA(b%tQgc8qp|%MmVD5ifX=oDn zbEsfg7L$TbOi5QK$+&LG5MTF{y&Ang3me`U0;N03DhHW~`g`iOl;MK9>LgGq2`o!>)DffM< z?BUiUAFb5l$gRj#`6s90A8Kdl*_bro356L$=|eWMPc`#|N)5|9D{jP^0_*R<7g+D{ zj(5PA&6q?zD4&=llVtV8!NG@^`8C-3pf%!A5o={B5`V^!?xw3p(c@(Fuc`U}rYBNg zP-&7!yz3-}?qYesFF~|gxT8PpPHC+{G+rSc+ zGcWKwb9TpF^SnRw_ht|BQh2{Z*7rRZ*8gsgP21hm zlN(w;8a$y|^zaD7%8v3*$qqRWk6*kX2ntKo{z%fT>7-jx#ZcrVwkdj~%g3rBdY?PS zl(b8d^o0T~d7mzG2dhMBOQlc37*}1>$=wSEs5!(Ik%#GC+f3JV1{+w#HpwYBJ>|#L zC$cM4YbryP^;*#j7@<4TV#~@irJ!okWvMP(lP8maYxm~pd5=}w3vd4ZPM27-YnvxS zzgj9ZjZg<#j~AV8!;lVQ)@$sI_+7!wz+!I&nl88P17wqNl5E)_VTE@k8i=m z$|>FAG&N(bdk+M_FfTm-g|gG?kH|BzX!6Wrl@M2J>aAzU2xMGhDP%FxC)&k=rub`J zL3Rt)VKWsbnvG@w;xOGa%xq590{<=g-g`=Spqp)zw#2W}mc8iY$aPvZV91q?j#BXKPOq$L>^_6R#^C<8dAJ65y0zq5w)sVcxx?^C zIVohZ^N}@$VA|ev3~-7Y%4`jz)Uz{nM?ad?KdMVFN0paf`0MAnAMj_dRCzfDyqA5} zf11~Q+gFF%Zl9cvuUX?d_1Xd&nvo={nRAQ75BDtND;SwnoL(2$)uv0~Cp@9(+Stpw z%NfIRVuhIa)DZE2PjYkUvXHP+Bvz1)`Dv2_3KcLcbcJ)R1`r3y66o%9R*vgu$zIor z8ssv?ecWk*nmg|p zj3>naEGU&OQ$mB)nB>rJgN zU%H*>!V8UsUOvr$hF^cjOcNXbu78e*jg zEjZ@V^+j=PpbXWw9ZOj%$*Ky0(}+r$j9dLZ(LpTr6mGcos(<4CH7*mXQ`oZIg&HX) zAGtMYTRT$S7k@x0pms)ezIF5Z56082gIhT}^zX)OxHJUMggw35kC+>Z#O~W1= zo2cn#Q?qFmF`%Vu8W7C`AO2V8?w|Kd=KM0q%XaNp@*db8e`jt!`sUd#ATNyvZfRn_ zH{TQxVduWF&sL@2;(Iw2}ldEu%?kTv}>+Hjlc3JbsLL}|E%7eCVt-7<*8lIdW zJSsQ@uQqbW1dJINnghNo(O>=4L@+H%=rWUqH$?#GmNp%tPLrXgDA#mpZypNj#E+K! z^Ly}E02ZIgLd-^p`b4k~c?n91xf-d?aEoA)0v!6C+9Z5`($YDa2cm+6=sUGJ`=Ap# zZP@#_G}KQ_GJ3W&`{r}qooDu@fUWn{@@8YYr>u7@<~ zE)@`um4N$0i~2tnB*x|hK`;PeU(p17V*tNC}WCB!4tE_LG3hzhvy-E z%UO4Gg0)#i>#=~N(zqQIa)w>K1q=;M!Iw@SQ40E>32Cp^n7C0^n(S?IhA5b#f_x9p zI>8CPh9Zj2&R{frD2jZX-MESnXplzA)1NF5oW^9*2>U$o#L>?3!1nmN$Eoh+ke377 zgqEMvq6C3`se-*kr_FrB|;s@63;K2MR|FGBaSh)Sx@i4Z}nL zk`ljfB_o45>@=@AT|D)YKCQhvAel)BWsIfZt>A$?w?)k~Fh3GmqTs*P-!~vKqCdpg zS7VP2GmuKR?zALHJZuA5cq_PC5kV>fR$bjryUG=B1Y+t-OV}O;#?O<6t*;xk0M}?d zpi@R6)+WX!)NGIDE;snrG1Ew2vlElVcV;VRGy!u4DNE8!^BvQ*7Ug0>GL&o!zy`J# zU^a$RSz0y%dasqvGg72hrX*~u?xY$(oj{g})aAn|ac~_{O|zVHv?$eC7fbFqZW6XltKf7yXg&`)a;q)Ja|>NpkNV} z_@rz&i>hgPW)qy9bl7SgCXUVwrepYI7&x+>uE5Nw?^{x+rFGXeWg+3~L#JGjOJ}f( zKeIC2RaBbdMVEtq2U;|WsivBxno+N>reczad~6LAWrOxQ+`;FLEtvDi-+G|B{gZk8 zk%!^zy1Lc<=#=2%DRv5@T)H5cTVC7}Sh~No1}7QIu%(~OE)ci-;k&Ec8rsT*v5=N; zYInx*B&U6Orr(8MIz2bRxo`GAtS*F`nXArS%gML-WLklo{@JO2YSAr`I9P3&A;A^9 z@de&u)!7B~Y-)ob7abk#%}w$Kt<}eaH-6A0r+>jHeetiDcKA{|YXv?giOZRCvcqYA z+<;x9BE3iB0Njuele>xF(y=hfKl}V1QVE0xW{IDl9b6bNIJF;G52Z0P3MQFIz^)zG zatwHPztb<9`#vlD_qf01N{)lO^ZpTz~4b z^EQ9zA8@tG%kA@q@BF$W<}VkJ7q(OBW*+QhCaj$sNVh4Hi3qP6R|V^44akxj@vjq$k!)Ciwj&6$V`EV|!WxOs}L) zrFV&WMwXrg$6vY~p-GBNg$LTYT^LFgo(rv%rplk6nsj0g^4WOLZZYsdoo8Gm_H{! zK`Uxv+Apy6Xr?r^iWAIg2uFC+9a=k?#N{HPaHX{9h8LsBVat!`^R^c%M|#@>AN=Qe z@;m*~D~*kAJo2*nsgL}-dE(&hJ8a$oG#Fe;*&%I&l_gKcPq(P;*p@J=I;ykLvgHNo zhy&5M%jEGUzU;BKBv4S9(rcU~4IfaE6u?~h3?429tI|7SY6YkQu94ak3EL)4PmxBT z)*dz^qA{g!-u~XC_o@?Mpaqqizh*6S^*tp(yD`ah1Eu6#m5$YBkdjn&l@WI+jyU|3e?Pc*NZA!B3tYDs~zx zQ!k~tS<3P$-Q>e*rm+%M`}H7bT@jMd*AEByYE+vnQc1=oc^16%a4Ee zMHvjOZN96JWGSrEC2*Qwk>*>lmp#@c1&m(OPMCE z6*^lq_9;8Bq$zpp0wj}N<$Ve!nZBtDI0g>fJ4sVw$JZ8=*~Hp$o(HvYZ4ByLe*>iUTG2%c-9780H#7zk+T;f zdSp#P{Ie6ciXaSVdYY^Wpd@R>%vWzIdqRzsc$Vl`z*r?(Ad%@`M7_4RD)sIc8pe{&$SY0Cc;swG;CcB zwQaby43L@`1KH26E2~kfG%099>=}cd-~5lCzo(}s%2={+-b*M0pDPnjvCTq^R7D~Q zJ~C>+#S?8I5!(c+Mj2L+CcA_X$2=(yD2$Y56*2FMl|4pLE>3-hVvs20sd70j@lm(- zib7r7TKGlBsV`KbtM>;<2QwvIp5Obe_zrh3^OPy*5mi&Z-ZCw5VUT5LSfAy^v|r*E zTv$BT45!Unpfm84Yf&*Z(=~&Lpk-vOwj}KnExx4#s0P$N=(`Iy0r$tF=ZDhwIluFd z%+r7EZ@ZN30nGV<;`XAC{s;5eD_?xk2kaJuAs#z;%APo8U-{SvpxK63ti*Ims?2Te zt7hSPk2P1;TU!!)U}29K#VsAJ8e1~-T$EETpgtfZOOkS@YgMlOjk;=eW|H{qrl`%) z;YynsIZAq?_lFvziiXAjQ(+-YL!_00*2}ZoA0f$P^Fw#^PO-G>9hcx!n(xv$vb5!0 zCh;@@irR&B4-Hn+Jd&}(j#HFYJ~2Gi$chGnczO4_ua^Lmgrvhmt9vy@E#`02>}&eq z#Ietft;BdqEJk3Ax1Oyb?u}{J@+otGFfabt>wEG$|BBrAfJEkT88RdwRwDO60^Os2ba*Q%F56+Z&X}eP%tll1e$iY(OUJ{B`MX|PFljNq|)}_ zh%L@cwP@MOVJ2!fr2&XBGXhVfm=Qv!s0fV36w{yq!wx+DDduwho^jj7pNbVw%%&?( zQ^T3E?-M)m_QMBF?-``gEg2xAQ;bDHo(|wx`{j=ly}ddjWZwmbq%k%Xfd%yzDdo18ir5C#^6OH2W&7nsA^D zk;%H+g=CUK?-IAIZ)GvW<1<$neyJGW(75E9oU%EP>w>CFHQnY~J<05|(u*lgtFq z=7ii!&BH3zF)-T>D8SZK&_S5qS((DK;LwGS?fT#}yRr*2PM16AP_H(1H5UV05wnLP zOk$05@9O$ZfR;{UFLfeBD%95B+s~kJ*+i1n>r{tQ0}~@_poRYWJ*p9(7~XJteJHwQ zr-6>SzPWks2mTrEK0;ouGB$e6%jbn3_4nq9H@!ZbUCZnkF9W6LR0)Z!RPFK;CM#($ zeiCo{Tb0AY4*MIMYBE)$?hThskltguJqb*l&Ld!I!!zWTnhDK!=|&hTH8 zTo9aUp+;qTQ$dlkOu7BALp;jF!om^INm0^*AQkPi@ON5BA6_O$0cP;al|Z%R1j=A? ztBq2WS0Q+HNye!V#l5dkHgG+03g1*bD2jva9UCP#F5NY3XJ!jhyO__Sa-0! zr54ly+xn84_zg6%&5SdcObR|A-mJq?gZ9CPIskSHY?xV%wwfhUcsylnUNt7wGi^lU z1bX1^A!uCgJ7{!<+}Nh(^l9LtL~@D3Iw_1`Fv?R~3|UXoJWTdH#LZJ%d7Yp%sz~F` zX@u5jd{yg<6=YfV`+3m*GO|9CW{<&qKfe?{HyOluHCoZ@>M* z*yt5s_%Ye8U61nxO|-E`U}9rAdT6G|uU1+C{@VL#ZfOy^`I*VUB&pXTT}>siQcv|- z4GU>fEIp9$+%}xv8F33eJ|Y$Xh@y)z!^1Mwmtj_;boVbD(>y_h1c9xkpR=8^(3ID( zkZuW{&~XsRimbNR-fX;pE!>z&VB4eVW5Qa1x_+wpy~%M&-=e;|u}xXd3}GhDYsbS9 zDI-{I!(DtfW;EQH8C4QXq#;R8d|-b*-NhIzko~S} zcG1iU12`>&ZQN{56DDehWoc6-%p^G_VKfjq8L}j_vOsc+7o61&ix8!vr8Y&f{YTB} zI*H^ZILO4PkR&)n)l8kFj4We%+Au?+fnADeC$B;?^#VguPqu7aO0X_ldfR&nTb*ga zP=~$(Jch@LtYhV;qzE_iRh1A+NrokvA(M$-%zcM_?L_c|+n31#in@yj!g~lhs*fumG@DC0c<649txi-1}QT zY@YVZ-fMCx-2w*V0!`Mh%SEAKC|F@JwC2YI!w-OB^OiV=LncKDxOrg)h~wBgQXx{k&f^_kX~jcKdaI9kRbyefcNP z>%R3XFMK`*Ta6aee$q7B;GvY4SK=H(+f2)~=GDu;QX}yF1r#v9@l3R`cZ?*9UZ6+= zfOoTDfK_=;K-4yvhHE9ASSIeA+O|qKJ?~RgQZ`_-TqX?w(${1k4Ce>0Tz(Cv8{K1) zT_YgfVc5eV^%3giAY&ZhjDahSx==M$!rqyriFroG;2xHQGuO-p%PvbaW=e2XX5vvq zsROmAS5Je2l>0lYHo3%nh%$R|%Ptc%y^^xlbd5xEf-j)=02WOrSdSuB%AGL7WC8;r zz%WVh9ghctH3n-HQA(jwhlpX$&*$vUyXSd-@bAs_r`8{bk*rk*iU~a)b88-L9joH_yc^{$TEEZEX3!n(x8P@;#5S3o z92?hSXZ#3`Ac6tR5L!K}o8A5Y2({=Y)w>CccC~8#3}GgT%v>hZ-VGoZ zL}VlBHW0p@)8nwyt3?F&i4Vsf*cNE&p9UNZDpgz*#TlGA>2bLa>lqa@K)T)}rc^{O_Dy z!@q2-PzB1%Hzup)AiP}0E(IaYzA#a!VJXssyG2_}Ch<2D-+1YeXk};Le}La#aAM`d zVmNdroUTYWUp3k|F~mqF7E*zY(Mnw-ZminiD@k^t9dWGfQc569jf=V$Y7fe!c~nNJ zdcf#b0G^n17sPD;7QcxQaKZ0|U#X_^{re^=XKdc$1@$&k34hDAw<=iI z0v$anQ`LeCm6xadl6Rly{Ek0(l(ygh;1n)j`l#W9k32ER9hfr%Om})}rQ>@KMXWoy?9~$xsTuyDIAz zxQa0f-3Ed>nvKBSKXG1(l8>lGV=tUq={7ulyd_Yq*UIv(NioNaAEXVZ_6uNk!Dp`8 zwq_Z*Ip3$rzgu*GdL=b_t;R>Kv_}U+CrtftQ$5P^>We{F-4kg5MXHBpT4w=xaF1$< z!!Th{?m{<|JL+ifb(vmvvXd4%N#vbD^H(HD+oYe>e$a-~&{AxLn>Xh6XFu-COM^@^w$FqcB@POC=WQh+IjWS4xx|?zaYCBi%MSmy(zi|4?TkN32T*riF zQk_0OrLcpt=6a)oE10L{fHu!OCW2^T!($1Mk z-pNxPV5swc5>MM8oaQcVlCm?j&Q2940DUQV;4$Wic{#OkQ(-9=5X!m6sAg1ssh|-wRevzve0anNc1lkS|A6y|Y zlO*k@;-;*7SYc9osso`sa+f+6pdMOleB%6u!ID>v!FM7;m`K-y6^yIot&v2~8}hHw zBve3i(+KOUVMCKIqVJWTSM&?}4Q4%!Orp=X;6kdBT4id})6<0|I^FVLz# zW2X3`kNF4l)))Qg9Npl|>to+&L=Jn|lVSbi!jw38kBTy^~@PHVE5&R+&)l&164d$ z$%n6gmB%iV+hh$I0@5?_>^AR`VtP;xR7zbH^^?T61GG7W?ituaUW5sqb0<@)Z6GH2 zH&bukVyGzCf^T$>&uY_EZd?jIK@_zdjg8*n7tRA8^cQB}N@Jr}eDTN6Yrpx+X1nc< zvd*9-kJ1g&eR*Ves;UR&72z2}TF^o;e zGWVX8Cr$~!34jyr_XYr^yT2=sOAFYi_i4A#?RMx4&`?TOd%jt+pYgj+yb2mpnD7dZ z-h)N1_RcM)iF=PqmxSA_gtPErZqZO-qj=^~O{kTLr0JHTQ#Z`Tnu9H;k&pVG=up*7DeCQ-kubzNW~`Cg z!WQmiagx)GlY>p zaYv-@UTOttT2}nY=6o_xuimoj<}*)BMqPHBA<49BW#Y{icO@&iEK*2qYKJm2PEyh( zF@vBx%QtUw1UujJF27=O;|j>j>tB5+eg9#Oke8#tsZ|b>EDT`bc%&(18Tg$3dOfv% zu>P#1OM`Wwo8){o3`WPi?ztEv4l{slt+NC$6rJUpgZ~a|mQ^QePmfds6+uprWn+p> zQOIU(Q*p#g4MqLr5;Z9Qj!tZRv!(@Xvsy2ixU+%#x7zY;mLV|)y% z|04TMY66+b)<@ZCLDSe4sBJ>qm85|G`h*=j7?MngO6`Os&S2d90?CQ0*()(_`6l#j zJCJnUVhVyO`K7`{Y5FJW;3pGRGVZsolz|~YJnr&=79_^kdT^=~?*^mBWsRoFC42NX zJv8K%bPRY;`{lo7p8Y<*=c2E7={h?*Kwkb$-thh3d=y1kzrW!OZ44PAcj@SqyU>qQ zFsNUG*5n^Fe~l#LALbks4b30EKOOPDb}CyVT7vP#ze5 z%e{&Z7g>!~ib@OOQY|8@cl4y#aT_}}1QC-NCQ-{PD-o!|P7+w5N9FOR#zzZ^v=R?= z@T)MS^&1#A_f5KY(pbF2VKw8GU7JwhU=!~+j9gUks&re!wiI$YiN9ih1gPgmbl;a0 zP^lhX4tnz{B)$GH4r+aa4?axYE=(p7-?t>R3zt?|d6gpZ96Ct`ml<44d2he>fjsyT ze{GI}2kq(GY>(G{%U8@RzxDrx#fpUYXY^LnE0e=$Jbw9Tcgo za;qgU)>Zao+&&oVM@8hD=2ueCv8?V3Jxs`GU9$avHedHQ^gymm$tUi_b{z+y-#}-5 zLY2ouk3q*SMOEp^tG#NV_60qJhz`SuMI86EQ=Ol*5OVQAYF8{hz&PE_g#P5~vsIcN zfmqBd1=PhtI{rduXW~}D7!TwOZeZ+vurj`G$|de-3WwKlh1i@j3wlz(7+Zdk9%k*} z5@UsfOh{bLV;Xt2K8+FrB9$YM%Jv9gMT4wQ!Gd8jStS~4Y78vd+OumHp1x0d-p{?# z*yyb<{qc*b@A>18U6jHMY0Cu$iP%8&`(2{k8koTnHI@1)wqO%4zjpLF-|7&gRMS~* zZA_{cS@Kq8%P8JEf{p=MqTEOjDGUokZB&4ry& z$@)1&++y?UZFpV+>W$xEmC04|4L&LX5@{;RUeSTm9#rmaDf1CtT1rvd>Cr^8H2Q#x zh*bX@rndks+qk%;nl9^cEZAXT(ppE=nQCP+WlUspXN`<~ZPehIl4b1>=Xt#c%YJ=T zQ*tp>hU3*z^vnR8@ublR!@H-fkugk|OvswR8GI)>MshGVx)3*g=6inNoZq-4_^};= z-lOy4kN+om>x++&m+L84@2HnqQmZ7BoSHC65;9B}CS+Iwav4A^WwT)XT&1wkKueOJ z4j39;eqfkJ=tu*k;N@W=nxtf^pfJya4sCD$Jd*n5{(@x_TVC!YX(AW!?`H=fprQQj%cICie75~bDc+G>t9V z{LA)lqhRUJU{6t}b#=T~3KJqQomI|vw8IB$$oW%Z01hfI-1T$bh3CHDPhJ@-xPDN1 z`La)$*Zi+9;aCQ@bd~ptE`Ks!RRN9qY3Un*NjH6P0BEZccCmomq9k-k@5l`7HYflW zZOW%norfx5I(JBk*eWDcC&k&OGdmmUj+l5oqI0BT&3dZpES_@9$#)Omu|eAV^-Z1y zLE*LyaBEY^q)UI551rJQs;b`Veh(yjWynUoiC&;`Fmr+nFl5TnkFlGK5Ffadn3J_- z#xlT(w>o$e(0#|&DjVzrQj*o>oKd2Do?^RU7%Qw6f~iVivtA_C9=rNWe6$rzDlw%6 zwPH6c+&PO3S*#&3Mzkhn7@YH)xpvo6^1MIzcjvZeJm+$O(*xw?%|H6x^O8^hw*d^R ziqCc;!++ChXYjQy%N)AxR7r>6LhN|ajyq%rd}Xq_a>TRVaSyPq#% zPJ!+`K(Y2%E5VZ?lXjD^1eSe~3MA_JP%Wxhw~YLbzH9VD$OI)CI=GWC-v0@b$ItX~ z48pok0&7qTeQuz}Kw~UwR2#)bVl*9#^pa-FEWS#=LEk*aI?3J1LJ5(ekRtuYWEnmr zZVh6B8yS2Vh9nCq?k$OX{48aZsn&oMR~qU>qY%=4MpOH7k>2LpC?}b z$OYsjIc4>XIpj>DJyHWEtKMXWQ>tz_6P=I+#L?RXvaV^1XS^@AyMA4R-Dh<;J}H^Z)z2 z;d}qraRN-5^Ri$$JYWnBs=9Z;*yHSJAGD5V=2jL>%1^8{8#{Lz zdRLFk7O348QPS1}a|7A@I$Gw`tFcef@=8mwtZ>S2AhZgGs-=k&3UwwG?ZFSG2OT~6 zFBBW6$PgHtx=*$kNLo?MfQDfA(uEf-s**D+c+~({0yv6)8Ne$)!6LKU5dHG0bmu%cD>C5J}rahg~5TX7YdDD1PoGiemP`G8`;?BZRb;7~iJ zgi%_RCY{v9nU}NZ<bw(4<&U$1TkT5bycuNX{Y&({#*_AkKCiPJ|zVA zns|olghe6ovfzw&1)YuT4uu2s&^>h~FCoyzxb+BZslJMBHwPG!;2_YcCm!7~UaoSf zu)U`R+bsaS{iG$J(>B+DB=L(UtPIex*oyZV%*!yiE*5mG1Qrh?shTz(^Q*dI86=Qo z0HVsEK=_g7dwP$z0i}6_B44&JxqIMlm|A@|4Gx{cCiDUZU4O3o@U=?MTn1bOks0uA zNkUHX-10=*HV=OIU!AKUFUL~%u~)rxUi9(*cy2!SRz!Yp@yGV_tpdeZ?(Pjk6B+eT z+X=znZgHWgNuV!6*Wc9(!(8mQ;E_gK+u~e^sM8!@QiM?I(n-9bioRzd+elQ0sbvj- z#sHF(1X(v+%{5#6@^p4?xHc_7>(5_9hDVuT5r2-1KPn`CBvR0mb5yyO%A#k}<;KX!rtb3}I;PYZlFJ3N)q8z7-y)o3~(0?w|u63g*r zNK}v}KAh|SB~^d*Dqk18NjC1j1oKt74}A&$(*mtt|yOfgS&@J`>P3OnvRXd z_*5gL%hE2ZvkDf7$LX1|4g{H5y-@}0g;yoWDm?W=b@=0w&cjr!t$D>jhkbBo4B?YM zJMIr|3M9SkBBlVALN3+x4$n_Pli3}W4>iHGU~}ZIZ?eR@-0PJ=FW&L)%Lv1e(2#Vb z{I|M;WTmd1Q?~HSP=@Vq*fZFdQ-^@3vl2;{d^+EK1tio_;enDJb=1GG0k525syM2U*D` zO3bOB?B7L`&T0!)E*YcblXMD^#5+1{P+e~KPr7;Bjn3UT2;)HFpI1ggHs3WutA)Q> zSNfE(&<~)lh!$#WW2Hio;*2pCIoMt2nX^0YoQDnqr?)@z{)W61)$RJc>4(2#9{%kA zw1g8AMJql00<}*G@vS`{e|(9}1yM6m?h#<~pbSv5RrE8@saNIx&8@HKdDqjdz)bzu zf&$ey2*S`6i*5|fW(iK``zQCx)GCd?wAzceaT6Ck@KgkD>^fEk@+=I3E6@ZTe;&Gm_t}`w+;Nl^`MYOxyN>y@S391x@f16Wbw`FoDyLDsUb%xdwD5kbw04)4Oe%#`u;@Li zj(ZlG!XcKf3342K%zf|s$L6WO=r>#mdAa%KHy%BG^Vq8%9?s5cmL(r#NfJpCDLoXV zCHFBo0(rzMwGo-e`$kFqLMmuMW65uINmD9o_6Z<~;T=X2lbJ|qd(_fxfN@LWwTin% z3_8k_c)t=QH`W`d0!(48E(`q1B$>%%!mu(%^R0c;=~8seqUgY-p`KJmug&iFT+%dX z9#Nzp*O(GJbT%iIs(6^wU#e^X(9y1=C^<_~s7GqUn*>7tbGTMh8 zCxT$@fK$4b{_62OHWMmWJ42S^50)e`|F)DkLVP2Ff&_`l4LT|)6E}4geBitSAk?Tu zp)hXErSDUZ#zw#W56<}$&iwXeu3q*z|2=Q~fp3A^ZtIbBJh8WQHK3s(@?bdgMABxN(2 z{2O6P2m=3m!cwhwGMUUqh%+I`v~FgP@Tx0W4qt``csaV$I{H>1=o_5qrG+OVT?g!F z1SY8ltOQw$>!|n zpHlI}+ft==>j`vyK3A>ee2?2XeP&m-jeE{qK3i#UROK(MI-5^4Kgzbx)@IlV@9L{71Ox>>^h&5M_=;e6DH*jSm7(QYJYFU21hpnJ#n^@7;XP)oalSeZmpobF1vB{tOwS7x9X(HYu(e^|!A-ZZTbTfsABU*(!=3xRgay6Sj?E z^MxJ1RMh;8qP>KDYBGK5LInn6>Wo`bK$Ji#bqcx)^h0$2Yr{2o6liq}(usIQ?i}Q* zc*d{$oq6VOI6z*WxEk{E;!pY)^X8xY0l0qMxtp)S_9ZW&PIl)7jf9SYV+vgQx*%Yy z!g?eQstk?e^Q}O+WPQ^Tr?e=Go592F5pS^`bcm zT4<&8>nz7&#G1=k7I?l6aWW0Uk0n*X=zhDXB`yG=Wg1B#(fy3%_VtY?Iy;;qxHjqxWyP>=L70YM#XaAi z6lPMQHZ*E1HGelXDPm2zU)m7RB#8@}(G=O_N-U!Eu4^!nomQNVS~7f)p;yFF*0Y=%jc6-o8ld}95Xt;Mcm3SKg? zwxwTy)x?8Ik~rd|sff54q*ArT&&Tu}j}I`gIllSuRn(eC>U}72a*BDbW3${FNDC-9 zMn#y@A|%wTQXN-ue78>e+INo(E!B}yq!n%`1@5=sg}=Vft@kg^x}9*xS*$fpRX*>k zfP~g}wGT_@cGdJC4c|$m%~f?#jXO)`UN?{uqTLZLZFCUU0AZX3Y1Dr{+Z|6j2%P@8 zqssH-{E15iPT%}v-!m`$^#5R2esxX#wlJlr{ZX>V9H^YX86?efEoG?q=BB}xViJ0# z$HHt|&L4dXKlNY!`uy0x`cw1rFZh^w)mMD_{N#W88}p<8HkLZ4!RT#RWW?xpvV-soBm_kC=!+lhW2 z)?!vKtNSDa&NPWkd9R#ak(`j%^bpvbWA`HT1~zfmG96?67??D-VS3*J(VbNwo`B;7 zF(|Zx1lIlZp+D7011|o+csT7#$uO45rO3Bk}lYot+)Ak9p1i{KEOk|9S|7 z|N2iK>^wj2`1>C(fqvEgmx3X+jwQ=^BaNV~!;?w5;jQWdV_&mkrSDO>g2`KWi9dSy zSr<=lIunJ1&loX~yxUNWT2F?1S>jB_TOAi1n^G{>76~PV{L{Z^$%a|U)lMJ9C=@@3 zYvb%)Z>%UI5_nUT-eWQ#=w)IVZjz=sXV@U;5e;sfD@8F3^_wz4l2iFg#wSneuF_)5 zj4PQ4duxZIoER4*k{+BuGF`qPEj*J&RTF#8QA3Q3)}ngbK-pJNmWF;JR8ej`I`_Qm zuj0Pn`G@Ap>F3$C1K9b$&g;JG8;%oTJrA$yKbG-te=*MBDFuL}lpeh(et0C)tZ>*X zI}oS@H`x8=IF!9-hkyD#C*9IR^AOYHwV9OcYJY`+%t@UT%8<&qa=A-mQ4N|}mx;4K zHbTJfaKXsk$D0YBt43ad?gFU~BSdzwu{sToNY^VcMhf1TgS@MU;X$C!&mPc&@rB1I z?j>|Wp=z`k=d|k5Y7+UudU+*X9@dRknyod-uNBb%Bg2&zEiJE{F4$uYKwQBW-oX70 zul6r69>y)3H2pAsfi!}(!3Ip5gwxuUt^Wr8zQkI=?#!Lv%x%xOFAp7r4z@e)KK}c7 zc^xr+Z}`ryKR{kSc8C{bhb?6o&4IN2~3XErYUefZQo7**2uFdU!lik1z8Rq<(wBTE}=o=n*E+gObs%h(*2c;RvK?r6NR|3h{aaBWV;a8lk*Kta!ksK^1kgX;F&Go?50_d-eG+)`CjWVoX0v zM{62)*E%g}544FpF%liF9YaPG$;5wW(ko-4b_t@|rJZ_sNfJUiE=%;W+2u6@>ej^8AHGR1=}Of z__e=to^>VUWt%ywynO1vo435^hmQR(Ca26KYzFVw1O_yDYW*ffXR5)a?D!PSWv}S= zQMX_AJKi+#mktb04X~UfE51#O-!UepCUzEM|CG8Q>I_zmJ4)d-nhgj9D8McKTl>pt zglW3(&9t0$2H3ZqwQgW$O|Ugg2BcFJ28n@d&frfc>!Qb*Y;{^BNV2pC*8o24Te@55 zMgmdlJGGA<6UdIp^fs-R9o;9IDXpx&74_q28*Cs74+ z9UX*?V^T$d8z(BCe8cn%1;FsWj9jEPh~P6C4W42$+te+|zAkYxnYe6}T>1hAKXD^Z zdgvW^=p+8hTwwT;ON9AI(93U) z8W=8lFNREe--{GjLp(b*3@{F6)}LUNbdh*P2pHM1devCQqXLBzS{gM`%_F9ZY3l+s z*-C%Yh8rl%g1t)2?v)CDArJ(E$z-p#v8Sdjc+x5>yItp|AS&IIc!og3BO%!;+HktD4?(f+J1t7u zy|HwgHNA=VF~q3tSebzml-A{NHLIp;_qrvYQ9bMGS?N+~EzCGY*yHynlh&J+3~2n# z=?^RlfO~?S-tOKKG37(6bZ#|VN_JiBC1GR%KTU=7vZZ`&O&zvj2}GtH1)WLS#{veV zmJ@%P_GwS-z%7XwGM;)dAXto3`lJPUhP3Q-WkKoFqA~VS(fQf$_2E4ISG?DpKYmH+ zdjWZQ(}C*tPv^#KUJhp`(61hH9gr3mpf;fRpA4B9f-8h}9Ou-D4MQeb5?-b6mr3ha z;_0x%Pa-wVZ6=e729#}pXqb!fnkGPQ?yn_S>7m^NL5w_z{9dX@?qF|(fI1>I!mPf4 z>(V4C+F>aNRpH*iX$LR*E71<_(T7asAI~c+Dht0*|xn|1_ekOh{_dk z{_D}RUd8FW%(x^$nk0$Sxl}4{KU@1)gp*68D|zTXA*S0nAT~^&W8d$dcl%Xy{|o+@ zNGD!4Haa`_di;-h-M4(@aQ%+4;r5JDLGODZ@oKgy3e1KSJjM-{Dyw6GP{IIeTWO}4 z>XlDRed!;^PKlMnHV)|3lE{KAks&pkCVo*1#M#kuV=EiR{gJF!pcofS?C6;FUIT-5 z_xu17mvY8nGlgYs*F_Hv(bJ9Htkn%lCPQg}8VO~m{Wwcs_e&P}Ze*ddvtYdcBXrIl zJG*#D1H>Sd4q<5)Q3jYz&!wlhHQDO6NKy))^`uy70owHXCDnMFq#B88&?qwPQE#$> zvEA?6ITt5t=|Ll!WA|dMpv~zGp*mVJ{6okD+%=B9^1vbnvpfBaf6mRDx$R(V^w977 zn{#&0(=LU)Y)6%s1J>{5U-WS^*RJ*N!#bC~n=sA0Cy`z{Y)e!rnN;j>Pb^(lTeddX zZnMjhu9jVpI!uQ+5(D1C?rTe}41YOU!KvUh>@s4YADq9ppKD|YIElrw9J6RMp zrC@4ULrY){elHZ8-4T^YOI2OH9q4DZAb^0$CT(KMB(3z`y!H+`WKi~&mdD7+f4UWz zYSbnF!lA|3uf>qtp9VOFIXW-%aWsC;WCWvNbs zWgF2m_hM44=mk_A+*Ae=8jZa%fSY9#4meMZF*>RYa(c*0C<^U*-y!q6>*u`l97GQ; zhrGP@6$hb%e|``;eZyR^9;hdnqGqO>{m@U=8DdT~xdF88<8?ksX3nLjDbTXCQH15| z_mdQglSUl$p7i6m+;YI*8Vrg(p9sASDS0@ky1bCk9;ZWEoj)xFp!z(fqAHTa>jU z=lia1PK^&d-+w=E{?YH5?Y2AAc8tv2RYPUUUe14Z@-rke4t~InWFx2z*MIIo>E+Xp!LKPWj3$l#w9Y31 zUkmEzSLE;wjcAU7WbyQ0^*-~g_jvz<7s-8l(rEo>MVz1Z(m~kI_^xN%wO@G_nQEZUAaoyycQ zEri)YwrLe{F^4?LQMIUsc$VQw%7OdH*?r)H{=(ex+_&k{w^d#aK<6Jh$Uy!33$4HH z?Ao$C26h;vG)F6fq^4zl@sPulLPNxJ7|{HQx&QtDVGXPo!E>NPXqHjF^y^7{bm?R(Ypzf-Ts2Ey7o%W0^zE3GfS<^T~kmMjAM9o(>Wj zHw(b1Q+5k>{Mb%iU*y5K{cD=YT zVZ22WW6oJPhMNavm!rtR(|`5*DERF|Ud|tT>%q|cpUhid_7g{2OIZStWN_am0;9&e zHvXjI-^rhURzEXL<}{?a`yGEN_x+APIGLIB`~BIq+o8L57-&83FxYy_PyGOozvfjl zM-`ha-3!^JC41$qS_QgFvlKys+|2?*IIueSm(9`vXrZlJ1cF*8i#kUd@Tv@mg&3CuWfPU9J4bC)W~OI1j3blbZB zNIUIiBBfn*kfmLtFO7Re#@7k==7nTAW z(XXHFNk>tJD^zKIc3H@FZhy{0bN4&_vWtw_;Qz!GU(c+F6hLEq9SjMlQ>#)By}qWi z=pUt6VuO10J0Zs5Nz9WTBc$n-##2`+7yCCt=N5*GxcUff6Asf}f!)>E?1!boG-j6= zcqN}eb1NMXB{~4-DcJ8!I@9a!S5hApg@#}N6sC5#P<9NP3=nKGWfgVYa7l_CS1f{L zBCcpoCQz;t6>ySv(sIAUvgD;cd^`xbe$O-V;D`N1ju}Ea0G6@Q8@~G+56*7?Et^C> z>+;rKNC4U!nK~hg(6Yr}qkeTlPD6;>pLPE*Kf7*qncZ`MI2AB)S!lqfnyyUCVO2mw zR*e;jHrSoMJhU;|cHfC3aZVz9FOv>pMr1V05YKY(sGNxflgEdr_|B6EMqDpXE883D zW&zJ^7IKgZAi}Ecwya@rRBzh9v%$C~Z?GYC>T4xQA7BR_{k}0C$e)eEPyzM?yC_yf zEU4`=7)lYo#sT#{F-!!MwzkaL(4e#Ae4hJ$e{$~rxxaXFdCC*e$EQ_gl&%3m#0$0%If`t}7l-9jyR<3*|s-oFnd$t=5_T2`x zxJp*Wz)bzox^4f3yvZL3Arl1!P3d$PDnUvQhqy?P2V9l#zVS}=_PhdI}K>cq#C z34`)0hhi}JZ)}s1rOPF*wk;_AGtDZJkX;NbY+bC@@ zxxnS-6WsaS=g)&5@)s_GNG>roKjQ3O^|haSG&VYx!8O#6U;=us6q9BEhQ#3FBDbqi z-JY+pXy(~94i0m9@hAN&9{TXVG-pq~XL8fHjsh|4t)9bi%U|1n|Np+l%-p=W-?r!l z7sIQUe&&BVfY)DjgfTf11w91GO(U3z=dPYpG4#_@DzZ-Jjo{PyxBSUiPENG6o31D1Hwd-T9=ZIX<>fN6<; zgAs&4*>1b@AaV8AZVmGClRq#o{gi*1N$iZ~+)TZimIPkUu*4dqtzqqbN5u&GP5eP0 zgWxN_{F4t8%^#d+946PdKj(R~mK+EYenxStzqh}?zou7*fm{DMl_YsFz7UQ$%{%UU z{@i(ZhDk1c6GyrJmwncMp4a^EFUxk+X><}cjT2uY%??~WM366$v&T!9?H_3s} z73JOAPaswX5$t_2vw%1sYs!iTB0!nSX)Wys8gEJArEx%Hu4&veFmt{PcNtrs!&nSB9vd5xV6$NDQHrr^j8V^}CUCl!V3$6XQD}pq_lKdB zLxw}1y1wWD_;uNZ$D+3QH#!e=}9CK zk|p~m_D&MZ3K20Eil%Gy7?Z>o7GL3p&gmBH~&DZI~gViHf0()wVMM>3!yWmL6W1I7Dn?5Kz#uxSd^;yf68Le>OMY`eu&e74kI}prwMJ@}QJb zoQULtSzz#iN*HURb~Kt8tZZdoMPv;x?TL-ScSVuL@68!?lKA|scAWN9RI?|9>=02x z$gJD^!DhG|XqBpNaSfHh1{WS>KY<_jCuN7K9&EOKy;AZJ|QX>=(tYfTT;tLGxQCcEvXOooF zeW?woXL8@~{3G+^10_nqZy)mV#@Eh^KlxwJ69<)-*|te~oA|E^Tj%> ztt#C>_o#~rWT|-Qzi6U4utagHJFP=VCTnAy(jnvUv94~3jWDs_v?r+LT%?ka+D`&0 zF!33e8pQcU-|uaB@WcM{aP10%Q%)4}9%U%TcjAC2rk**Oryj_7-Q>5aj6NpR*?B1t=F;h{SAWvDAfo&*CcGFZiaCeg3piwGtF z<%iG#&jnmjXQ>X+u8QRVbMr9hc-k*{PoDi=AG(`0R1iJzeE;3N>4(1KSYpa<#@Gb_ zGO#q=lT`IVZ?u-Y46+mys!@W0$tXckI;Lwi;>y(uk#=uZ`IJP}Z;9528Ic#L%O)A` zzm&LBf4!eMDlQ=@XWbbM#)P*>lJj#8!V@`W@OM1tp-Z3R(T?=dm;S`O=wtuM@#|o= zAPHqzIjUjm!a;)&Bc&rKYxyZ^mRDs-sW?n?J7Ij52IS`FZhO4lEkfHO)9bU=1XWkTUjEI`& ztRU1dN9?yeVA*R#Ic8EHN>Qzvx5jFLbfHWV44JWKa7$)^V_!+_msqRx2Uu#-2}*bL zNZt64^;jj&;Lb8ezGGc^tGpcWes|vgb0$44ES~RC;NT^n_%AOG7~9!Q5;jE*WvO<9 zhThC17NUjbNVf7dG28)(5ob5_l{6pLMUSUfu)(%L4=*yoQX9UeJf6}+Bdb_QVtA9V zW+K&lLD?ad7oJe~k#>XYWR-};po&pw%W5~*wh(u*8v%g&=pX8Hx~R}}Ho(cLM`lJCH3 z8OP((!46uE`Vm5A+vyj!L}c8X(Tf!(LBB{SHFVj(y0$4hfCzV~P|BDI;=x8*q=ufKSQVFg0aQ6w_W-_~`n#Jk6xouR|y?wOQ)*tFzwZh;VbUvEiHL9z^!W? zQA;zaU*E=VOkan3?y%^1QGZ8ZinM-Zb=F4ye-;05+U2F;ln^s9H!9*5oKv z#*+`vvM;fQyxe&GYvzR?`;U(_!CVBu*!j}!tNOgkEml^o9RVDOnoZlNFlj|mXU%D) z@4B_=@R6j+PHszz8Cux?819x#4Ngx=gc(xNdPqY1jGyNG%2W!$lYuM;s9bA?oD<4O z{YV~%lk>@BFgE4f<-sYKMbIQ`GZOo;oFdCA`LR@865Q?)(+(q{BIN2xQSSuObu6l9 zg?|-E2NhRqx%GmxmKL<;&b6JP@1w=f}A@b)NPxVb>c7z<}lkfSXDPydzgJF++^S*7o*zv+wTm0$Jg zN3n_$VDohhN-L-)RDp?W?~83TUgo3u0*4M1798#f;wSCPh<@}2qpNYI6XLd+e;9P-~I7dKRgeA_J5mK|IaT@7zTXm;|m#E55^h+%@}`!L!TCKD&CSD`OXKt-(TX+2j6~j1J34Q6ohO@_>(uoi-E8USh>)`I2SA zWShb5ej2_|sbV1`f;t+WqhFhDYKq5lesVJ+=eo3wzA;j7&f`ugNgJn!MW(i827+H_ zod0_KU(Qq{vp4zTL@a1qi)OQ_@Y6ZMfv`2plD+3-dJ~pdkQ$GrE#KC1 z<#S;0mujwBfIC9vPLbGFLfxwcgbS0k*NY*NaZ4R=_C)L^S4t(BpjobPMowE}bs9UM zpT>=BW425?^WidBTwqRPg#^QD$Vpgv7Dy?~`JAgsy(8k_IFs|RKCmPi*3SyQ1u`b4 z<5E{ot^{@3eK6*9DjFsrE}SVENx7TVZM?ReCDSH3mrA7gpfh3Qe4B8Tw7>VgK4hNs z(A)2;ywoZr`v0Fw#h+imMV@o;7=fiF=W)l9>!W9Rgf6 z90MMAz1_Rzfe-p~CkyjSO5elq>XYZS-~Kf@{CV~4(;2a_u&n_glrcdZMGbDVJhe8x ze4!Nz89@f2-B!@cFo-Uxl2-r+&A8+{BhG_#?c{8diQs)kDx3fff=$_do6LaCF5?eL z@_#V+jnI8U?UPN_e|D_W2EFnwv81uhgI_R?BI1tv4xwf6BSd(*p8@NG6Ui<{yzc>! zyA*>x`@`NTjl4FqzT>>`FUn~}Y(G2rWZd=qckV;;v+JsL^N9;~#hv#*KRNulti!S} zQI2OWGLuF+GEK`*fK)ps5i}>c$=O{`nFl}k&vEUk&$t5ea(&)-VCx+D+H7a-(b(}R ztz_1^5ri7|%J;_;^TzbG=MKVp2Y+UygYsMo_XI&j+!{{}C92uf_?>^zP;F(DQ^Q&l>@ z2<9Df+!Rz!Ev;3A*CrFDxwH@y`(udI^e#`-R+NO*hD98r-xa8fMjxP-Xv-+@8H&IF zY|PC!zn(`Q{;A`3XbV9Ec@ig?Fd6XR+&?_BqLq#DL{4Uc-JCkMgw-F&kW5RO3z(f6 z|EIHxl@3NYWFc(=!)9IFOlc}paTisyC8gh^P>)G>IMOgw* zGu?r~i5ID5GKmR=aq`hiyu+ye_f^j}$MwoD`{cRtnnx}U#?P)>ihjejvw6)oe&KN< z%yIhau^mh@nJ{SAmNh~@W10}tYME%Pa1404{oV(ULI)QrF3s0&m6wCL`MmNgK6Q?a zonjsB82zX58H(GD_Dapy>bpd1Ozim5Q)Yvh!IsqwtK`zH&Z=x;GGa;*h=Eu$Oge^< zTyIOkFZzZt*By`i$#k~bL}W{848jxPuyxHaMsGwE?EQ8ai zV-wfS4#Jf-5+fUC=KTD8fD8s2TLRjeeIAy>WX53SB%Rga;M->-@!xeoR73VOISP}R zZ~>j*O+Wfw^HU%7cjqX1fBU`nPcHS&?fFix{QvZH7&r)pzG+^05Px|27kzA)ja54h zefnJHX5Vno$fn?jZA%1;_@191`MW&ufqy1VcCr;i=)W+yq+u{-u&qY_w4;Y!O|Ro{)+*Gev*ZBRb`r_}(ws!0fE&DwY(_$I z%4TV^IYVQhTx~LJ!_@MS#Bf=wfL(L^&%oq-HTHl(BzA>BfP{cZu;1``F9{t<_#J=XK$4 zt{*alZ~3ac^+i9*fk2cOJ3OUGnbB`T5aNP1lvZq2=@rr`Mf41(J;;O(uc@DV zwU1GMP(|IY1IUi?_031$GH?9BZ_8VL;`>j1Kv6l}5cjzY-8nO+H=@pYQn_s=g`a#8 zj&fDc{w*IqPd|v9Uas=OMR0uaL9F7fFZ=Oq*KQx5_46c7UB~U1MR)Q%XO+0cY91s0 zVBEY+;&4OXlac{WMO6^HB2lZ)I{g+66_lM~+j}oqa>+M>@-3Ygd)Pn&nb?M!6#y1F z#En5n+7Kx|MAtIYLYa&=Phf@;9?U5rP8u>2$OTNMh3D{Q1+m(9?5Om6<69pv=8q(f z_?#qaB^YR2(?r5Eo~meYXi7v=!lLw1P+avpiVw9Ni>Z+<+?Rca1~an(`}4aR46=Kaoi1yz-1vIP8Kqj zgpXY){PV4m4-#aTX=VV!hYZ*TS{P5tNpsCR4T~n2_SIQ=Tyg}tj_VseGC(;*{E%Qx zq=@HFV2x|WqhoUwv+hHkcv80Gx>QLtw)W<8E5sG83v0t8K?r?7Kegz0=D22n?d*(P zlt>XMVJHTgP^MajrWNp-)@A7xlZOWyEYCnn$V}ga(?CCKwKJLnJxRaW^th->BMmAm zpU`BJZi_`;)&bWeOqlWacHpS!lyi=-r3Vkzp4aYq+7)b%+YTJx@0*u>&VOYt)@D3w z01Mxx==-%jx*=##G~&bS>r^S24ySEiDQD`wL7^k*3bx)d{{%d+!WzVtB5wnXLgJi$IO+JxGlon%Ndjy!UH6{ZSpS!Ir2(Wp@TJ z;zcGfWDtr>3Lt>&60{PQ8uR)2L8<2t&pq$*D<_w$yd0NNTtz+UU~86IEKg*A7SjdeJ`OO-ua+*Y zet8Iy#5yxZR0f|CVB4HE2LC)mu<=0$fFvF&nQCf7-KEx$nV~~;kMHo{$Q#SuS$AYp zp+6_qQIoZ*R+*u^oQbqdZSTTH-kSs{Y zkVHC-g16!eL}x=87x+wI*|k6h=7Q>W#QNR)n?Gc??CtCS;m`da7rn}hzMl^!66IXK zi-*|Z^q3>Gu~Q(YoMGKo;%SRve(AfWcC4Foadc$CR&H(56k-R(>UJZ?5p5V5v^ z=`9^W3*&}){KOc5&@tK<3rRP;yV>APDe2`c8ND`A%Z;0pCg13-^z|Zju2^(}6u?pu zUE+vp$MlpT2_fNxKLn^>X%aVK*&zO4(9Jo9uawjvkTkl-$+u*Mfb6KzJC&m{Sa2Lc8+f z5gRLZEr?k5QQR;fP}5vr-En%5oHPIhK2&&Am@8}XDu1AR@kXC)>tJaiCfd@do}^gM z>_e$=$=kp_8S!%KNWA zM!SvUDAdjEDwXss3niuJ>;G1_Vn(hxlkpoR?wmAcJSO$j;1Z`#yUty7KdA39ch4)2 zY$c{H1+mMUsgtedLwG zWToq{6eK+Au7l_BC<%?kd3JQsdlO(OWD8FM4lX3DAqzQAG9zK?g?6{JUBn+k*v_oo z59UAnvt-+f_yY^vkF$lf>GfuqO^J~@E1}uaw&;`0gkZ+)X$(Sqyw&xI~u;whW#b}VB&!94@Z9D{WTgkwGZwX$r&OtN-WoR{|9lk+5P zdY9bu??qlu~>*78*z5d z9Smh#=Vi|+DqDGbd(#jo&3Kh9>;P@4^s0tY#j_4D(+Kr478jWvCuuSd^t;4S7a&WD zpfN_NifvD!BZ}0*9jf*eKd{Ka?05{6S^JJ%2(*ih7v1dXV#3{Y0aT>c%W6-=eR*|x zT~2G!?aA@sj^9zrv8cU!z^#oU663pXMEo8 z{p)l4Gw;8`)A#zksE0;xR%mS z`MZGI7s{LM-mi9I*Yo}3efGww6aA|&X0ml1W=3OB)tS&)O)$j8(}7-~>V(aiM3hG` zbOQx0SNkg(%0W`*{SFjakQW09NYYpKBImh??CKY9WV|mtV zMP+9Mwakj$ww%I^h~1u+IsJfta+sxz>&~!$hw~>MKTLc-H21vguRQ+w63ELoHxKgs zFaG3zl^d_WfV}AAjE5SAnR51MP+aR=SfhH=IW*AL_d$r#ndzBvQ}Hars*UEW>qZC>}@M*(Qga0 z5UbX=HK9>a3?oCItTDbb1efX=Sk{0u1+4PdCwb!WJmu&AntAT;`~yvOtHo?**XH5R z|ERqFd%lUYBilonbd7TCdHj1PHwC^!gqotqEN+qftRrT(-m|f*27d zF_DruTW9cNgsG)DGI-)wa`fN@VJOioc55Ax%vzphPv+K^V5*xY>Z z+bUQ3ao!oo7#4wZn-ue~^<U}V_NZmjBU>^fw>7NPP28_M3HD%mi#NaL zBYH~$!h3-bQ|IU0_VnlQ;D`Q&IlJqgR(a{t_h4rJ)ra2SCvk9foA@GY5tmiqpx){@ z999nuBb1%;RMgQ>jY+AVQj^_)G(-p<0xtVIVDs*6l_aAsGJ`ulx+9ZC7AJ1YiXWOm zXDZHm+jy@~s)oPQ1ptwU(FpCMoNkz8TF7csA9c520T<0hIf=)xeaouAlD3%YsU*oD zdDSjs3m%b)*dlTR7n?MmM(?*+(iz&`Y~4XZDG)B#CC6nw2Z>O&iCvO3*tlUpgFmq9 zt){QxR9fGfkeZTdIv0{<&VD@n_rKqt$~*r3Pv%|!%@@u){+*9IvLHCS^KLGHz*xL? zCfJ9tvu`9Uod&%79cO5_V4E{FZ`igOntFkQTrC*$^pCVyF78?woGgi+w~gL zMWsmYgBaNBi`$(nV@ETtM@{=qCdHG2DYvhR4-WD@WF;joy(=GDcw44`j4hZ_OX3jI z9m+=QGETl`vf5C=;A zZY7>egA`dXoQ;_wk%k3$K$jTj+(dn(LW~|JV>Mc_Gg~LZgCSygv-^L|Ag}u-4Pq^J z!bSh<*$0!Rr~k6|I7m-DHWy0A$wgLp{@CO5%wPLE=9$0YeUF0yIwR zVI4Z0mjVDN6i^`(gafOK944L~hGnH7UxhGP^pV7NiRy#JQw2^bSa7|HqbBL0Xd;T0 z#*Qi?YKkqPHlqR@L%Ot0vcTVDK$03voY3?RL2VnAQLv$b4rg~iWuE*_zkJRs!*eqB zlz018$1JdQ&r(oD{7*}p#a{@TT85pV*)s9Fw)CwQY$}NH`rCk~fFy^~{;qlO!~P-% zWAm1`@4nwb<>d%@`ELmWY@=0)c*FG?Gy#e?SUfLdp}JxRVeFSD?YK=}v^cz%jO5Cw zwTfx<3apt4;I>q1%G;!bHU>j-(;7cI`ft->)$VV)RPfl==Od}IaiUSbg!?K%cgWiu zu&Tlhac6~;EZ|ZvjY*qwnsTJ-(C~pH5xtHesX-mgd z&VJ1=tg#MjCCet_YAGq!Zju@e;Do$Hank*z`u9i~gwcRhdpbw?_6yJ9eZK2O>377( z9RopHjFZ`8KR9^xaAFsTx~ggSg3{-Lm0%I5jS(==*Oy9E4l@_FmU;GjfB0NLUT$0o zc{x(u=JD6Q3bwO}x0Ido1t8WLUdK}y%pH()MGvi{0#0JEY4^jRa2TzeGBn=S7nbD= z`stEP_84lVf>)o9gF0^QQb*I{ zWD-Dl+X8#4nM5zG3o(<#Xi5zv zO$mc&I3miH2_>AwXU~&(bdo`oCc&(l$zK{%H*Y+4aCG}s-1pmm|D=O{Ew*d(@-O_D zdBgYquQ^VAh3~|red0Y1nbu@g_a|_EZGfSKxWFj`hws1zt zXbAx#1LZ+^eoy-(?bE+-iv^I=8Ev?R7DB9NtoXG#qz4GnVs!MU+hl;iOOZJ0>Fu9r zyhd1pkc-;*wC8G4!UiZSj!D0+N0mCnKQnSZ*k)q_iJHaD;I+7fvPIt_U94ZdIso%P z48Lyx>N!oAAdI9eME&qGE@iXSh`ToFOhZ{w86Vl0KOTvj!*C3E*Prstqo?nj9kSZ1 zR9;^H-QPGb|Kd+LPJrW*+qsd)L0Lcy!|GN)DoLZOfC;2(Sv1-tajP6QP?u!G<8lBVI7MIJ9q#l%x~Xj(y|0xbOtA;jbg)zf=gkKC6e+X(q=b7 zV&SXwF>N!9n~2TAaQd3AR}Iay->*4uMOkd(mmAvDxMe53_p3TYqgU4AbRNaJ2mS4-vL>R6@!Ts_aLmo+{o43nOJ z)yW$)Bx-#UVeXUOPLnDT3t2+UE@BvO9M9i}VI&c9|NH*Yx$AA;bR>IUmK(oncA^dfAylqjlzC(`@B`afts;3rrOm+n!D9x;y6|9H_CVg2eZ?C z;h4j)$@OUa` zMPq{^jellLQ7o!UN~$|;{R*L_j|2?e(c)`SPi60?fS2H2jgfqqyqnCJ_N1%;;*M_X zwKXEVP!f))ZlRbc(f}k3x(!WWk7fIAeWBM#L1g!Dt<@wW1I|2S+Zy*K8QcjM^(KNA zGlBxA#)A&VnIy+v-!p#oZ=Gko$NODQbz_^{ynwvSn-4}zT)UppqI_V043mLiMzHpU zhXqM`a#caD=(vUIu*!-x5O6j$q|9m8`}D?ZFQ80S@0uSnhVnp3sX41_v}Y?;mY?%< z3z3Q$HDFa>mzPPgKH6Omy{c4cm`pah5hdx)9y6lmd$ahY7wA)*$((vPZB`|GkS98- z|4=DgNtV<@l%N+dVg*Gc!9j>^*oxpUVOs-+Y3XrFjMgj`99RhLqTwdHc#EQ~~wQyIeA=eo(7zKfVN8JZflK<~_+o|kRlnAS{` zjtAuY37+)4pLf7D{<#Yczez4NHhT3pe&Ipo<%6;{Wbx_Ne*1G~)CAc-7 zPDxhA*NAuq@sCo{XfmZk0Tah9N*oni)Bdsv8*H-x5VEDcVj z(HU2dDkhVYu420AZI6B8L)fG^3q0rA8xIrXWN9Rn}W?nyF9*Ku~gT0X@qs6{qi zQlp-s8|Z_70HPf;<355LP^CD0dOPtQaZ3v<8Pfkjb#*D4V7o#u zNbKRNX+&6FnW>hQD&Lk$vJ6Ihns~S&r48$9B^hfJ7Q&k;Gb39HV%x!PD}Y#-`eod8 zYtxYGEyDCtiivmjEid=4@%w5`Sq&y!*GR3{H@yefJ`Z(=G}d0h0<%^_Y0>1u)AvK? zslWI)T<+=yb5M0YQr(V_mmEY7Yk zD_6QXZ(mu4lQx`DwN98|kLicnaOBACeagH1iUX(l4<3abF8_tk&gK<|iSO&b>l@~X zjZ9x+c3VhfzD^jDvFQdR3D}#&cc;Yc)TmTe$e@Se*b$O z{-TdPPJr#<8x&%!ueZjoSt6Z;)UXg_v@TF;EdS#y1r5(lFJOZiTsFoXJQ<4{$Y*>D zcy>}}tZHOUoAuU}YY1YXz~{4@C1bbg=Pcm{#Yx)|dv8}I4*Ecfg72gXhTWRQ|>eykRHcv z)&Nj}0|?kExO~q6gzvJ(pQJ?$kMt|RXlSIMWaWAdXv*17--_+TFg#;(dzsjJ$+yBN9L}d^G^H5Mv}I_@#=?<0;hBH&2N%C z{UmN#lqr`x7y|C?>8MdP@OqUbVTk8%!MmHCwvsQ7&QLSB8Pg2curA0>LkSG9S*XJy zfUbxn9cp-6oC&E3zwc*BX<9iDxt|H+VZZIHa6?kd_?sR`xcD%+5zl{nWr+Wn>BYEL zZrrDQEKJ9x+XC#_OSUbizA16AGr!oC0_k>EQ^q^s3B28vDJ0Fdf_N&Xun_`<-rPtc?3%33#Ay1d9pb^dL4g= zwh8lh-y86mvC=E5#C3sE&3-bWC$^u8qb+@gbUh)s3!#yjDlL zqNTxU7#Q9JvW`)iT#-8g3V_OZ6fOKq_e7UC{a!}p>?^%1PL=7L&c;Tv_!tFsu?VAI zHAy-sMW-)a&I64e=&uL^n!m->fuE-b_WpDu`!`9?XYk|zI+519uX8Pe_le}4BolC{ zzN_NFOrn^SR{6PV;XuX&x$3uM65u;1cwq15kvU?{YSfs;zRva9sp(1P=<4>wJn4bA zJx+c1Auq-DI6__?`TEbBv+K97X>DRe9~f7M4d?w?Bj$|lAR)HQ!ln?4h9yDWhUm4zj!WeV9`sPGS$5tlz8jNB6Qg{(GGrXolYlhbRv$Fr zFhcSDV>o8Is@oIA-=utwaW?^tp!W+RLdqCJ3(FGuIt>%Ouu%16EE*X#GRkgPZrOH` zQcR`b6Lr|@C&X1t&=~#%0kSX$&TKP)q=f?8NYr2qf#D=5V}aA60C{9#=)=-<6QszN zXI``yh<6Pc^3xpx$8`MqdB(6Asv)cqj793Y-D=j26=W)2x=WRe*HO#x{tx_9bH{TY zy2{w-koEo00rK)6=HlzvTw~K3jAjGP4AEFC6LVR8Nmh^2?)%OPHq4}xKKUf=(pms_ z2>?_=!3;DFOXB=PH$p`B+t_dk5B-^tl6b9toN$gz7`(r88X#_Xl4>*ok*h^k>Oy55 zWZaFRQcH(fN5GXVZu79V{Ucasr=7wdaj0TdFXJB85C`_eQ zI~t;-mBOxc39CX!_gah|Xf)B zin0+D1)MwEyr}gvTk%~zBKuqSOEwUy0*$xCxZ#)Ht2GK0JSDLtZc<&yQMp#IN?VT?b!7P?J)_d8HJWgI5)T&6}&wW{(d@Pvmk7NpJYs|EsljA;_e zxHm&frh^j%rk(tbiq7}E<1e23e&-*U3ze7TlBw_YdBvA};=JxVzwY>FoOwE>GBd>> zHO3`y9PGmmXedHBd+)+zc@yaG`=G{nTT0wjF6j(}q!8LQ;Prr@V0ozhaBsGa~m?U|)O;}a*GS1H?($wU?^l036R z8uDHa*vk;29|24N02&=+I{=2T;&jrjJxc;^OksO7+|m#@6EYa@Cp8yILK!LKR4v-` zaTb~hXb8qOuuU`06K5v+EOK)Wke7MzL;w7(L0-P^n-7qej~lkL)-1JIKQoyS7)%gI z1~LSP0t6Ir6jW;f?Q&;(fpG?!j4R;4HcBYTB(P0)j}ik1Cds63uO4t>466N&jdT{t zw1qI0`A2^xG)9Qdg7KSem%I9?r0eeZ?3yy--k*E0maAYU6#UdGF56%W*k=Y;5JBqjzsGH4U+E z)aBr3XiPJz{7Y`f;Vk3cW71&97<-}>6HzFQkOHt@z7+nYDtHQ6)`bvwX9aHkzU z0x+MfyArj4dKAcv)dDgHu=5|9yWZ~SUv6x)oz0Ecyy5_P`PVrLozAwY%F^$;dD7rt z$k_QIQMAyUJK0gP1JrNJL@*mgbH^4l-BE!Eob<0ya)}e3ZW>yZ%`3i@qL_$&q_y08 zYTWoeXKuy)hsk-8w3P`Hl_s8~+5)gVt3$O@OJPI0I&ca#=@JFDbZ@M40(XDX)?^yw5iFM2E7isskU(Sm_+t1$X%0$Ztd_X)p6a_M z@d^_^cpJmkp$zE0ru&U;ibP*DZdTpWYEu)jKHPt_j!`6Whzj~@S2aw_>ZPhoYtK~&I1Ij+ z^sAc2Z6N|t92l!#cYn2qI3@NWB`G_RwbHj%aRaIn&CuihRk)=;HFsVd-KrhkK;U@9 z+rqO(;p_*UeHSJn8QRBpdZ_4BB?Tu*rS&kC>qt!upWz~3u;@%y)GD4&PO8xLxr2w3 zME<7uv+>8JAME~a@BJ>}^_tqCQu0em-C{x}U>878(_N|iZmrR+6F3pLNhULagh2sR zAMiR*nTY|_!z~6sZ>|lQ`;x5OT>>IgN-c?5A+~Te{?wI}FEBP|H5>yTcOIs_NB26; zu3zfud!)C$`kTHeullM_57%yU0Mp~}h@kqfOisN%L3@&{_ug=^=4Q93O(ob9q`1u( zf2Wp{H81=Dsoj$7^};$fZp{W^ck=JRj7ob6wu*VGq^;D?+XuT^S{86%b-ozOjuHH; zNQSce%bmu#ZB~(P%VZr2sr>A+o4Y?TELvANg7k8*Erom z2{B~f{uHh_!1miwzx6_RK_jp84_~#>G#CCv%L|FXMGMy0E_VE#IHSx?1sUjo=MJ+UM4vh z8_gZ}J?~1$%UfUcV+Wyw|HM%Z29P?B`b*-nV(dwhnSy9Q#$N+{Q`V_g!#KJnTAr`n zwF2^b@E0hyU_HfF`$pY__?PH`WT9c*&PY7w(u^)ky@|z7WZVS6d|~^O6+S%EreVqQ z4_B$332lG1JEEOPLTue@wKnB#B|(_(qgMj1EYnwb+Y}m)-guh*@XSD*JPN@($q-*j z!~PV=Xio1)`ud-AncUOe-r$96^u|l#jpJ+1SU@sVrWVHIvSzXLfmyyB zt*fxR3WwQck;TL<5WG(_^|jCl>#XKAQc4x^QAeD zhdD_}uw>ZJSW}LCG!$rxf9%iCVglaWe#pM!`*oKW0!3x@!dr0?CL(P++=R)NOzQeJ z@~*?S`$#Z55vExsOq_7JN6>MwMOb%N?;RoPnPnOmCjQNEMd}*@h!g%~&!sC(;_lW< z!`^(n)wBsTH__fQ7WhwQ;)#2=?wsfwX45LfQdex7#GmG}kjJjmpSKJKvL@;~f%O-S zxmTOWZ3ICk4t>Ae_4Yr1?)&Y(uWY6Ev%gn;&1WC{J-&$}^P}1zb#^suGIK|lo;HH# zb+G~{VCu&7TSO$1Si0W@IX)MCm2_WRvx;rQC(*5rJtXH<^!L(Ji&|xD@<>|5G8)6wz4j+vjvhgQ&R*B1AI6*i^ zB{b?Fr8@czBJMfqfVpyM<(C;up#rL}mL%?99n0HH3@l8kYHK)A@&L7JS6NTi&_9n? zVTs8}>R*>~rL%Goi?YI4EiG}c-%!@_?Rd?kWO1+gzc zf0jwq`9uPvgA#-0hX_lInINkx`gWD+?zQ6tyyGAQJDjEOgI~)jJbX z#M8bm?fm1A0T!_deB*G(60pv~11SwLp!$wdW^!Wdg2hcoHCbx)83AzS615YCHR50W zT{>A1i64k)t|5V$xRlJ038tSNSA2BDoUKnW8#|B_^9!fg2U;{KVdq~f^#?tUGPc4~ zAt=^V11# z)$de6RJg%nzO`0Ln$*^NacOC|m86Br;GH`boo&7U2wmEGm_3~|+ACmd52o&x8S2c@ z`?zcIP586!HSFpe#C3T{Yxh_M(<=T+7PKjDOEssK=m>>DCO8(s9V>{4V$lA$&bl-~ zjeBU@VrXQiEMgy?RA6O>a&^IcFMmJQe(d1YQ!+D3%r6`{3RxE`6#f>&nHeH5(_FpO zxDpbbbpO7aRJ3wQXZ2lSOF~RSv~v;gZol`zdEkTo%n{OZrOL}|zV$1Qy}yed;K-p+ z!ZC<#nDV~2xPi6rIxq?U=SxN%Uq}mzCKERHX!hyhA#BVH#w?D-KN!?2aZszE>d~K| zN4W1&7Nq9Ua)Le4NCfNJ`v{T=1DRk|Ye!K5S_eZ(2Di^rcZvd(e$Q5?Y9W`X64Wqj z(~0sjT%z+m3yxCPP?!$AEO)cgBX#%4nWl>cu6Ux%LTQi zBHphznHme})VX7t4xR%+Wkui%0mF2Gm9VD*)%-nhZ+wNz75;%SIWj}ckI{43%8#e-5NzMt* zVk;LJZZ6TU6G~^qQ80GY;Y+SoNd%s6HFN(wJMG8QdUFQB`2Og11j*{z0syXmy`4@B z0cp?}K}IHVf=v6;+oC54Ll*=IL#FHLq=UUBzHxh9aHJ$6x1-)XWF`~0E2S_iFuR6D*@_l}(tlg>F>SUWoN%W$lOl%GQ^-lb-O4l8+ySg!LUB|)8Ui)ZZ&?mYh!xy| zU^ZaTa~r`fSltgdGgU^c7Ez2K&5+6Yjd|v8_+9h#U-lkXc=~byiFwf{|C@^-$hB)^ zO5e`z?t>77? zio4$SUFZJa{YPeoDQ?iLIp8bY^1o+D;=o#cZ%tWDk?}yHAA~GWN5d zxt2b_VtO<+k%|=(9BoClCBeTq2F7#7do4JGMaF1JpP(Vl_;YDWDdPfRsq;dIzg|k{ z65k5@{V8pjv{$5r4o8k9B(Q&@S?I;(kb1tmX;Ma2Q-h)lr`tbFI2yh5Dt#%Ajszbw zlXM@iAYDKSn+iDTUDM*={=UYFDut{_uAZCj0AD1~uo5au3zjh&FVb)cw(?LG2I8kX z0k%NjiHaCyzxBH&$@IiM`-l(N7$)6K$jzI%e)m)J;0OP?x%Sj&>_cAE_j}V1eaAff zc^^45WY}h?bV(rGq{jeo6$~6&8n~EyUSQj;v~E=A)}?Pp{{)v1I&&(iWhRZ2C#!vt z!8r~AFfTRHTV-YLK|Ssr9xPz?m8dionhGb}{B$P;Rd)6s{4~ks~7l+^)XiYsggj%bk=HIYrJ#G&=UF{ zawv!-?K5JP?!^0@Z4I_v=i!KIpt}_!ozAufQTK&Bd#tR_L0<{M6{`zA^MNFB;u#w- zlE~7r!=yYR$e7Na&Tn$x@A&;Z`RDzjt5sfJ|C$SR=L6(rI6I@0>KTkP*L2!!n`Slo zUxXfOQB5?IYOTA%jrw`|dHD`b^`TPELY3|~JFzkivV&>+U*l(;@ht`OB#7nGs@-E6 ztg$pX{gbo+vQn^u8bP^ECYekIaat;+nC$l-_Ww2Gp|iW<+3KRtG#9m=y0a#yJ0atK zg#p$8CF+q$2LB=@Cg+DLc)0e4qF>9sFTrpcTG3ZTe^|ZCO>uDi?qvje^#JO}X(2{d ziHlQJQ?duo$P5ry?{zt*159J@ZfUVcO29q-BIZ{WiO+Ux($Y^PHx8b@zu?y%R9;@N z@9O5YZS(Rk{MdQpk9_A6IxsxWwYK!ssP_uEn((qN7uKg^G6}I2wFO5jruSz^dQdL|VOf$Fw#+VuAUESxZAKTobFY#NczJl}rki zT#LP6WibLjceWlBR5rfrBGz?v^?7zQ=rvK3j#!uggUeuWKQRn#*K4zBryYL4nBAxz zP^o(3i5^znsFjKthM&X+&XZdRCS{tVY``bMs{o^Ael#U_>%p-b>U8&*+&T%40?IdA zvXykG5f+@>ZS?*s7UU>caN9GVdnkSX>}+@3Im!8@rSI#%^XumoU-{{?UAunDe~cw^ zFxj?E2SN#v(}F(9QhF_?sj*)iRTZ`FNQMT|?}(A==d`sfMRKT4x%Em?{XF{x`%F65 zr1u|;mY%1*ix;&s-Kzxu`UvbFHD|qNN);2yBb4ZIh}y4RR*Al$mW)v+TAPoh6-Tq6 zok=x?OGp4?p{k+=NMZ!Mh|yWVPGc1NWF(0(lmMy>GmLB5bRAl}s)-ydBD+V4UHYv^ z3xn2#h%MRDQbf(Y#WO|2S|D|1DnX*G(b6<7tL7B>vcOI4;(x2FouTQxO_%^-(J3pj z4e(1^^)}}m-Q1r0yZ;b(KJd0zc={exUS9T72cd(1dyz2?n;)|y3FA+Q$~anr-N7$$ zzrP1rp68{4lMlOwKV{)(YSu6bnHXYvZa8(daj|}RJPVn+`;t_vo3RR4eBq{g=9yq_ zX%UMf+jxHlkz&1W+hBOEtiW}4ElpnxLDTb?#Je2P?W7%;3FRc|t-HAg;`7azQ`_WR zny76auXwcbIL`Qbx3gNLv?kjTf79|Ms0n`x(3FP5 z_y8DZs1cigIfJp131o(eV;!3Z8_HzVQo*>`PG4~EoGcQGdser&U@wEM#hz(aX+vzi zqk>dQ&JTg?-uHa}dFHSC?N=BZaroM|9we^*gln&c$o2F)s%rvAKyA(U@6>pxY$EpBX-D)q@{$L)ZG zO#EJ%#IoaF(UZU>un|_)KHTR5-ueur>ce6gAc3^j2ZHSN(g>rAZkXYi?cMb@?>Nu- zZNIlhQ~N>(k9^JN%YL^~&cy;H1HvB+a0z|7D!If{%2MspJXa+1-pIgTU^Y(4B8=cG2un zqX~$|G;z1GGu5;Pr=vYk>tlT*|1ybd(Gw^S;O zN$$h+&+czKYG1v4{$lATJ-E>BHc6KK*`$1D23XLtqsq(O_goko-S*UHUJ7|R4m#fa zCOVLm)PdBx#KnZg~n{7+ZtBZ&#@M$CtZg^7~dn_fK7Lv+d z!?ZCbrTo+un==IPcJAqT2D%uUrh}a>G_yj?S!WW-%!1_)0!N!NQ1^@cHe)b~ZtR7opZ9sB&1bj3fXSDwYe4k0aeNw#!m7pHNoS?ugM909yb4M964Nk@IAYy7BUin!P z#VCKozA`#)6%~$Zk!Pj}f_tl4?zTn;H!;zjNjuIp$`s08QY)h+Md5@n6oFFlUN9;7 zmP+2sQ!cRNRA%9ua@zDcy^u$Eib29f=}hQtSHsTzjzv|qj*#;(x=CTJIGjni`%g=T zU$v4U43lZlJ3Wzhu&}(PqmJCaatL^N+An#JLje5X$rCp(pF_hT^ZV#|;{o)1m;jeG z0_~IBq$$7eRvWDa=DSMXiOvG!V~5~%nWV;=i)Y)=D)A}YEde}lnI_OGVrvqhzN$JP zve~F~10o1XSZy8cK<3}|0swT zWME_B`2%)n3{-2gWJDWTEveYFo@a!?IF-QJd1QhRkRgt}R!dRh&uO5LA(-`I`Q)Cx zYXh{onmj<28j&=hmKb%*_(_9+(qKD3rN(Z`I^7-^3M3x8z5pg@;~hcwY+y}_X(O@p zRUDf>%@|%2b@Dxkj}Hy|hcuIcR06uYdN;$6^r%hBgLH-y|L5VPxtNb|C~o3e&G@Dd;-bpcU3sn=v$o(p_;K$)MTlz8J}LDcZs2PCG4K~ z>64vFJZOuyW@+`7x;FAF*E7s`sbjsZ{owcPV4+gB?0GZZixk;HvQwv(qVo+NyzN}{ z;0s{bsM#vkHl&AA8_*_2JF0n_2>MEJgz9cs7-qI%XktNyH^)b8-LO;erd17EprOau zTI_SncD}m}P)OC=dV!n(stln@jz%zhzO^E)U)w;b*kvR05{ST=xaRFar6kW(YMdW}-<=Pu7FYk@v*~^Hrr0mLKs=IcNR51H zwr8ZB2^a{CKG4$VS1x59ZdTLJ)7EybugPb926~S5co`byoM-Ew9#*_+> z_evj;j?U7<3GI-nrhhXZE@@n(|F)WZ_3eUJB}@ju#cbNSwLO8ONEz48qkow6z=i=Q z3~ArnX2s$lpt{r5b^Md3Kb^R!iGqtLuK~Cmd9Trua|R}HyO|PUl%KE%ao!}Gm_rz* zML)EmC*HmpBK5o!Ut_=51zAK~GMb1u2S+M@RyTGyj^n@YleX0dBK3(vWX60|!veFC zst9=2jdWqNQdhqcGa%L@jWHQAinL zsM}>xCXX1u``+gx<|*&?D<_v48*TH%o8NeVy!_kTc zzl+R7uM=TJXYWGYW4yHRq(r4p`$ez4GE`N3i4q2Egnrr~L4<624(rH!g8bIUqGRBJ zGuuXzNsHv#lLZY}DLmrI0WfLAMvE_MI_b}bPWw8fQ|2Gm;;i}MfKe1Gy^7w9rocww zM1;F&oS$SuoW`>*VOcMh#$63kcU@N+P~D$K>@Sv~LBTsyws2ELAfBLA`{ri#om&x6bO`*LtWwiQY-jWxUV8W=K&BBQJz z5_b=Ya3LoLusm@iN0pa*-|ItWu2gwBo0osl$IqL7^t;)v-#$q)L*mfp%wM&A911}e zb!y;@(JHth9O|QKBR8|bsuhvU=siFRpL{CWn8ggVz?iY9qK;)|68(Ja`|63VL~tWj zzT(4Tjyej>D)-&9PQY1x^g2Ma?-|cf6KSZH{$?$h^r7~o8w#tDn6Iok4Ho`p2FIu{ zH3c3cX(wndQr+$;j!>QR_FQZ|_=ZLFpHoyqH7B(P9aS^s2vmUZWJl>E9U+rRcQbp5 zTJ6S7;XryK%(R4AnvkZwf^&nE+#scGl}U=ORG+8&KHX&D*1^zzP4uioV;Fn-uZ@+P zH@WTU2gu9&{*gJm<4Fy9X|~5f+4&pim0$T87d=1)F1uQF!C74@a%F?LwbWH0dnJi` zcRqi3FDAXAG7Wrbc^BbJoA4GNMs$pvm6~EJA4*2 zUo*=5%FeDOweS=oddG1?rFq<81O$z_(+vy4Nx){d4a+B0>@ZoE1FZn8e{ZhHG8IA} z06PMW{zcRw)~BWT?B7lkqNd9E9kY4{lgUWPQkz5(mUM~fn_j?B%#w)@Vk7xac z-^J5^)q7tJdAWIjynNPwowvUD$HKMi0Q-tFJ%`cC)G|s(DU%Rn2HLBqNw9}?G-#J- z2N`CSw$tV|u@(=O;@b66sS#Re^K=|KV)(fGZQqsqe%Bwe zy!Y-pV*Fn9b)P%0{-!TJD!$-@>cHn{W&(Rj(G@T=Q7UXtk`+*s;999DCE7zuBZKR@ z=9gQiYYgQi@em|ARoH};LDiU=87XuL`O;c_@di*(Km%2aE>0~Pq2gq&?KmTK7 z!lq`rRtj-ZE%9!ynd;#L!Q$S45z6>JO&{U{BrOSe{1B7)KxKj<7_lIL7a@jOf%mf+ z7ftMTa|ruWSMSnV`+iSo_*shOlrS71$xW|TH%HItDA`?5}-*G9v{IvYh6?-)fh z&#;nCvJGSqT0MHuQqhMI8M%m_PzPmT6H(UpCoUg5;X^g5Q3h4wpmfI%*}p&jPTRo7 zfrS=6v&Pg834OAIS>sv8)N^7hYppASDkUVhb;ke8cpdDBJT@5XB%xiCTp+Za2}!i0wS<?#Lrk-f25EA7t4yH6 zh{+9Uf#%h(+$PT64Rs)k)QLm`g!ii!W~-2^bZ)sOgPFR-(p%Kv^HjH6+_G?mW`cAj z5Mb~mC$W%eS9Xl|dy<6my2V6cV?5n_P7xD))A|`r%iFDKiGNiqVMf1mT)hmGu~ph0 z*V*Nj3u1+3y|5pZQzC9Y{#c&;j=zLwA0aO}zXiz4$ITnQ|J#l#FUGr_f;h-_S$(f@ zsR~j9EJdC4)R;(MCq4qM3Kr@WHGw0`X z+cTcSgCF=OXS@B*%RPN>yKUa^egAu2`4yi&Ts!kzCN(D&g$KI3;(Impp{&3SFj@C_ ztr%#VV*lv1F}r;#A;V$B!cXmoT}v+rHRc7!)LNA82nz<*O&2NMmvnf$#HmDHLc3fA zXB+lhbz5@Xdun%Xt>O$j;r1Mq=O`Q=O-YaT#WDRBa-z zrk(3n5Qx3Tag@*O%Mh?^5(kSzvw_^vZ*lb7BXpL>s(JWo-QWMa{@~p8bKdz1$jjM0 z`ihqxjE(+XZr*rohHcu+sD)h9m|^3N2ANBH-}EpniTJ@H{u^wQrBf_nQ{ie1p$(pl z(gtwxyNn`GRSyi?Jya&Q!T?5QvknhCDJ=>bNR%vzlSphSRh&)|c_bP3Kk;#j-}N`E?70C+x+HTJtRK}{cZyK#{=inyVB^GUfkJpo097b*BNrk9pISBEiB8odYL+55 zRzs<=0fNIwcBN&Eu&KO$v~F$$U{$&1V|gX|8KIxROo3Co6EZUw4sFl)mG3prxFyKT z%>&Q(f6QB7^5YyPz)LAjNFRnd9c~ZvF;y|XGDBg9e4dT0PZDe_iEj0(Vt@ktm$sq3dm$@M4Hxm={d(fcrx01=&IycS#z1&~^d>+$qS z)O&A9;-%&TI-D|zYD*j&e7N1-#k*pZhlNbaC2PzD28I-`p;b$|6A^Xd6IC8MtBw3- z_jRBH)fk$ICn6HO#HczgPb+s<4cDAl#)wW%Ll1wPMnbqaS#EM(aEf6YXFGH84&D(0#<?|QWjQp|Q)npQ5)YRzF>-Obg zh7zfQT^L>OGy5uTt6>HAhiC(cw8h{)3)P`C6XqCn@HE3UT zN|USuOt5?ybLH{{B$RmK6djUeyZw&2|9$`H-1f9*waQCFUXEGcMfP{>7qXER`IH&C z6lvA{^>s-q4N^j52f_f3SqVOjP*eI=4C{yh`6{>Y4sUl1x@sPxMA=)ovw; zx4~W(Lbr)qjjeGqy$l|;NMZ_#>$|sf?5Y>r^o4<}aa54AS*yRb+*&a7wX&wh@Mr7} z%xq30PQOuxxPK0$o!^HZR?rra9!H8ipn5X{YUw4JE(Ka{VdVsuq<^l5Zx<@YhfMfn@Je{9ZU=usj zZ3_3vnPiq$tzfmDgeD0R30*k`jQKU^u&6fSv#V$!)GZL1$2 zcOxMzc@=Dn*+CkHtcsv;M$M^g>jM9guKpWUYBO;wq$HPU`ZEwFgEl{tT`D^iK40oR zoR%OVs{E181Q-H)3LeU^qM+Yn>L4w3s6navWKh&x|Xp%2&Ca}*0KY91sL}sDuUV%0?Q20Q?z}Bu8!}e z{>Zdz%bx8d>bveM-xkb1sk?$ugG2w>!P!yPDjW@1^$m5^D!oPgi{)7AUVqBdFN}@O z?tV&R{<`!%IJ$k?yy~kzd+Gb>^N?kMhh}4ry;ho$k!~4z0?3IsOBQqx$_}$Wm{=Px zrgF#my=zZeaH7wmKS(3+bo%6D4n8~a@18!D*sPwRv3s>jBwg5jp(!2FdYapxznidL z5!`ygic9^a%fS9j-GMFzTq;Jv>;%YNjn1vWD`M`?B683ff!hiZ<|c;Z z(N+&^LUZO)lW=hthLfQW{phUE(PYCil}Q-D1y(wG$y z?~BdOyk@~!{R`)W3}M1CUJqI-^=pX(IAm#n5kJOK{N3teF~QQ@VE3zfGhMmDjL!E( z>6<4#^bT|X@BYKba`y7l_xeHQ5{@H!1e%Hn@?lu%)iKUnKhy z8ohioF~hX-x+OOv4Y~F3T*C`@?43aKRDD@GwGH?S1T;1ChVgV^e>J4QSw(V`spo-% z!0GMx-hTu%UsC#BKWh8U!(Z@mlk;<@WlAk|j#gy1$eK*Rm(EJa5K2X_T2%B7X1W?k z;^BFk1nmW>OFQ;O>1r{=8CNy0i$XL~l3ZGt)t|0SS^6JY17Da3D2xK6GZVi)-AJQV z&X%88FN^h_L^AsRu#QS?@X)$ULtzbFGn*E)->8eKsTo-n#1pS=GSQ19MaWM4WBjZL zbmPr(3-m=5LQz(Q2;8;iiLgX190^8z4tv|F!vl( zUao??oIm!~qoH{ofAz~}&dy?XWLOi|fQ4bPY(av-=}U4cOTOx=esdV?Wa8SA(`~fT zMJdyfw4<9!E72`m6j%nc@oQOpb+L|Ml1V0o7a*}gCBEb3HMFhHfO4MG?7+fl&Kfh` zEOD5zT!Q)QGUW?MVM$_48Ao@lvJ^eN$o??Q5S?A==fzcS7;fi{(Z1a4`(Hhw0&TmoOF-JR39+`y(<`+0OB@umU#}GB%dbo9?o*{HED}Y)0oRw zCI%EA*&ey)9e*iDXSY3(gC2am;wwIFUjIG+>!|Vq%tS|gLX?IT3`7ttlpJ9tmO`lX zjHrY>YWNKsfOY#Vp-nq;r6%YnvPjl;3Nt+o>kE{HJKpskVM-LGwf59Q(iyM!*6LZ0@W`+$)V@AZ`tdq*b z1*xY*+fKTVo3!jBx+BXGA|0D{Fpv9bkxN>A_J#>3_R1~NG+mECvp{BfQ)YG-3_T02 zUvVF&9rsOWL!RnwwLnl!;R~>Dl1jYrR|pX&As)7;=%b`zvpRl zEPc=JzGvnNfzvm9?>EoOzx0!G`17f8E}$=1IlO~kGCfkq<3nbwUjcwQf5z#(*9LA% z#{5bqY%oP1l5MfEZbZAVs2f@AIK8g0DJ8Q$G`e(kRJ!lC{r-YT@|O>chVm0KY{vQTLg!UTn51O^NlQu_dZ5y6GN-^_v(==~|H~IE6I9V< zYBgLXUGKR^Xc9$@1lMS${s(8SU1J8_?e!>PEnaYlGt8jC z2y!|Ryja_}&HP0R&K_f_vU=~2K+~+zs9F;U>zGNCdiJ@nl!$Pvy&(*z;nMCS(8vsH zMmS2ljp&NXF3z1$d7pX3n;*h3ffz1@puMq~eN*|Xykgu-2TJSF0@c)%9w-gc^8SZu z_W!o1zcl8Js8wIsl2lb9Zafz7*BGyryR?!%DmuUBe}DPB<{Q2^$3HWqa+$bc#0LAqngva8L~9RXB)8JrSnUoC zoc+>71GJ`Ol@je}seMlWdz+|-ZJUebpNGwc1tJvWQBz#CcAx+bWTL=E1MK%@f{_7S zG!9V+sjgNnmO11yB`km&mV_;8tSO{TjiT?E_}Lq-SH8FvquRrVxI?trRl#qd^TsH2 zwndNr41H5{&lSvX^z?_)HZXewNm*k}FDs$j64U^=RI9hn6Qfn-1N-!}Os}T5wO?qx zjV4#5Ke>JbUTyU51u#J`=&`$c*Mjh0wvzMV5XkO-!5^7Bp8a5}ytJq9LFMHq4#q|w zowzC4Qoki*+jVa5qpnMF{IdndOo1ahq&6Hx#yN$FYnXFC^M-$1A+e)owq8{uXa!ld z0;1<6c*^d3!k((VvOCIfq(YZY!}fZ1-6ryJtEP5&VRKzMx`!sDl@VH~Ag2Q8PFu9~ z659a3yYHgH87*M(#U=g@niDr1$Mg0~EtcVVHOdA(;4GA-@gKO3m$7>rIcMU?03FHb zs>L9NT9pjt< z@3Y?hcju|U=-1Er6PG|<*bZg#qX(6j|2&Vr@?{){AeO~Gb#NV7U;;rHv7urMlg10% zc`!pdO%|jy<3(k1Hn(qQw9ZU+0MM7PttJIduPT71(fO#CYDzI;QuNn_E0x8jeq_y$ zLL3vcC=I??{d?N&>JfT;^SE(q?V<>T-Q;BBf>PI5-}O#zymDA(ppdvhTxS_)hjsX@nP~?@jIXmjU7@BK2&%kJ?}3dHC1I$5I&vQn zs|A>)4o}wcf=ncQ0w&;z;AQ8$NWLqXIIl;UzORkMeFUlv+A?*Z00>>!JCDiub9L<+ zu#8bx&WSZxY1&RZjZV5()f#NZ`Kn!!GUlQ_ty2D%;e1*ukNwjK28QnT0j5%%)*ISQRlF7Nxj@dv+cUUn#b z1vU1*GnOxziK+r$D*J$>q~QWYXtKDKQP?>;&~;nFR6HLrZ&DQz)qjh_mHIrvpKifE zJSk?Ayp`#A1;R@Km*YL{fn;)$`l12+TJcA-waTcl`Dsz18JfUOPpX0+JHwQ^OMQ-+ zn7U#25Lt*B!`Z*Pn{9iom7o}pnpE`=Mx$)0o|OQkYn*A)PsAUlRcj3TSVgPO|+gDaqAs z;#C4ow+MuGO|`e!&gRA&UpFuP>?7pm%>&zxDhVX*R9^k8F5H_Z-$}d6*Dg)_g*ui6 zO>SE`%{Zg3ATp&MiM|rQi{RTnshQwPo{~Z)=%Q-z3qus!qe0pqb5Fe$;S=!NyucAEsFyjxM|TF)qUbj zusCyY5KvE~{JGT(FL1h1{w?O&%2vp*IuRm8zjfc90c^SLj3O}}TUTI8hRWICWMCi> z-vD!tJGbdTUGa?F!Au5X4Z_eH+j$IDv6T18>hY?5{WDxll7qDUN6wpm{QEAz&t48v z00npH;FYPhYq?tYV*N|{Wzw+FV&H(GCJ|z*dw6AEmdfb-@&JKha)QRhc-AoL=EdZ0 z5)=OgANFS%(Q4#RwobRVhR;L06O9T9p|#k%a{*g!W|DP4l=-S;R!zD9(Ii7mF3X@#=}p+(=3}!*DI5->yV6QXCn} zZfT`sNB7OJYI0ZWawl!orhPPVS;FFz+qIuMeM}jKl(xP<`j+&!7GeWq9TUKJiQ6$j zTf2t_NDwr>I64RDca*TJ;fVnte%IC8FJ_SzK9@!3@Y-7ERmimcMocFCX7@LfgOn8w zqohojA6*|_bOWI~pW1n8`ODD~q^C90-5v5xNq;}lclHI)Axy4Pdp(*~_@eHRme#Lc zS@z;5UCT_^VTpn9!^}^6!4VR3;p+C-t6m1%Sz3$|-+uJ>Ppl-dBcC1`-IGQNdIMC6 z>amEw&dcJRKz)r1zlLR+OmW$TvlzM5Zkg#A#2 ze;u>e6RpN^y8Hr-p?Ml0Cv-TuY0qjt9H; z3-KrK&Fa2D)R#$;U=*(do(yS+joI&M$5bPG%?lgna?1>XNm-FzL4~^<^77T6Gq3yZ z|Cxg>UKr!8 zAT@n4Ih~E(LfrsGgYXZli`RpqQf){NvI@>ajIPf_h>gIwFP`NbNN3YftrqqIvV5AM zn^K|@Zp<(<=j!n}gNX%~hg3zl8-yKspg^t@?J^{hk}(y|Re~W#6|L}1(bbcQls&m{ z+0beX%{I#PO9`O zDSnBt#+-*V;N0gUrEsxrhRnpZcqUx|3Z-}DD#*(Zf7d+x#h;Mbw(iC-sMpzQ)$Zk_ zbW><9W*b@8v|K;YW{2zHNf(h&63Y%K4?+DNWHytlKPZa$gHh&lG3(u~`0rNdhNnG<(fDF>SeC z!{)4ftz%Gc?Weccwz=_!Bjn}3&CNHz5w^23R@sXNsfjIvy8c1X*jC_iKB9QHd)JVO zv3L#=uwNY&Jg~FvR?~KTtgJ*|ZO~NA64eiqGj&li&yy5-H82eoBFVr^0@;74czBSd zp%4s7PT__c*U}Z1&zeMVcDo!d>s{U={Y%=soqe7^bXxCK$$JzjtFm?3L}qi(y@DjJ zR%CK6Ag5_R8;Mi?&UBoFEk1if5>dZBM^pe)rvR({>jn&_2Q=xHA#sUqp9{OMS&11B zaPTR&9$_+*bhe9UKkeL{snuIZEd?eCYFOz~R$OklgEPd*&X41#uM4;xJbgcL-t?p2 zd(`+#jTGWWJ338f??$uW^;nXI%D84`V}Jrg#(la7W+AwVS9+6ocd*}d+gRJLcF8;` z7&iYE7*Qeu+fY^p~)T=U$%X`tB(L{wy*9cFlnJhTD`gw zlxn)$Fx%N?^b}P4NZ5f>nlnjGvBm?vME6u{ueo1qEzw(BT)Q@}`PQ$RSANZBA5~ro zbY41+st~;x%GtSbs!ju|MD~}O+FirgZQF2E9tM#|N|&x6Ton)t(vgL%rPWG8se*X; zH|9Jp4e%g*tde~1ss3#DY`LT1dgMG8{d*J8&U~etQ^_Qo{hMT1q0dD;_!;r#wJ0QJ;QfIPiL0FqGfd(K>Oe<_j~znJ$K^> zm+%1qkCK??@xeDn&H$|mkA03t8{9_Fhcq5r}mUOw;i~V${WQ1Y{jeS zib!k`3)EC7T|0%rpKBt6czv2#`D$4MkAgA>MXFNhF zjFXn9$VvRZf&dx}6Vnf=OvXW6xnmX<)!AX^{XU(mJsQUSBJ(3n;qw%cqH+2u*8I={ z?6+i#36mR-9(lfVG&W*8JDoJWn@sa3VESMs(j;k$k751Y2v*jNn&R%x$gMsnenaAf z6JrD`xhk=ReZeTt%dBmcnKWs!A$nGFa~Ye`EFe3$+Lg-%ToZwQwp_&HDR&5yCf2ln zdiuC^PmYuYqjn<`&p;LU^tH8^=IsD6Mu$GeJ~6#0h;RWE_n46*N`g(c>$U&yrR_$_-uW0Tg6BLIOt23(bZqTtx59_e};F z{c=))34zhB_eFY|Er~-<^zc(2OXySe^6y%1-Yvm@IS$5C-&Y<~US9jHUo&&3h`k(O_nqY0=8^wX)X%Z9~`8 zOiBpW(P-7h8CQ>cCAVkmdn&xN=z|)i!vC$e-2mwAqvlgVLM zE-jeR^uR{Ah-JS0TkZ@R|>1rm>We)!OUIo)Efp%~Br~qJ3N4_N( z9j%RANm8OqKvGPhHmNFtdy>Kdxf*DUkuC*kWbBu-O{6f8rCMk@s>1YJ4_Buo#pX`9J)t>>a zDPa|%34-ys2y3eOK#~)#a4D#+e%V35Ag}+vZhcooflsR(7mxMo>6bx>^9pS*oqcg$wBpYo87VFBGyZ3a>2BE8ret)=a5Cr zXKJ2>gH7B+iOBvTGnp}<*b`j85oK)5cu$g~+L#Tt-}(r4kaJS*q?s(@R84IiI-6XB zKe484I^$1co9JAq`+xMERQ^l@5h266fi!c+0q|VVh@JuoJRiJ0yV$K?a=$M{J8$5j za{KC4?A2md02%N{8Ww<1qWPY{rfT)cAh#r#2zR4QJ^(Ix66s(jnIs6|Z>@<4xYqLl z@s^|Qv?kz75hSVR0|jC-4Ip9M3qAb~*RJI?|NATENB+%UoELu5znxcn*{9BnKH*=@ zkNoRDp99tHI00^$YQwP@Y9mye0B9b3gBsm4i-#KBEufBzl-swvv+7S?mds zV5Q?e!K@+%QYPtuH3vc*L55*s?Rfck2+rUKFQIR1gC#<3 zv@#Q}P*iRz0^Y{`q1HFj3b~#D*Fa$?i;$kC4@u-XPf_hnSUfvBvO6w>4(9m3+nG}m z&;OGwKB-}8zF%9x9$LW-C&Wn))ic@cWBG2ZJ3=gl3^noBH3xVD#pySJIH8N!e$#%y zDqy-y5zLDj3ey^sGvnWD-|^2vqz#w;zp-^5$PU|33>0+#GiOsRE)Y816OVq8$VXI& zkDd3s^A?&b?Lkeg_AnfK*G`I78jAoM-D&^(KKq7M*1{BTN+#zuh+fmbvHo)K*GIeP zcLW~(>14CU!rClRVgd9*CXH^ooZ5I7hXuze3b!*)XZWZjzf7KxEMv3;Avuz%ucosP zjP7<2ht=ot;1$`g^qDDij8PELLeg#{&&7Ktw7AUOsyfCe9|k#wU@CCO$U8^P?lpF7L)hM(){Nny(7UlUU#6tg0)MsIyCL zJ9j0eu?jnURedjR_fm;iZWR#{$^^ZzcjCb*2}9s$vyM)U2mIal#%C=CN1yTg9Z!02Z3`XS7ou*1pI#9!WltaSWc32 zj)5Kg^BlI=!!P<*RdK6Z%fp4kSGH0=2G2<}<`4?@11% zBwMXm*_e5y5e7aJ@3p;LPcF#Q$w@AEVN;fc>7i96P|Dz8Q_`0KPtqN~s$cx(rIaU+ zoOOf$5o_)Ekhaplma{+oyXDeX0HQ zdJLDXP1=ut7f-YUBt$do0e^j6Q!z=Fshd+b63A77Aq3z_5kDB}`O)|ApkGaDaaC#@ zllPfpHN=7pv|XD_@XK=kIUr%5-jTe#rDabaQ_lW5Rg3|D`tH2+#>$JlvY?1Ds*{u@+wMDKAI9j;IGkEFS~BU&(G`sp@z$mcsFL z#xX7lv{3uR`6u9rku(aLjdZ2E_hjQkZyzc-0cHVj$(pY{Qd_zy)rb>&*fQX{<|6QM zKKDTQKEwId*Ul+Ksj$)P*UZrqvO` zfAe&}xDiFx7TUXsb9p#hvVawE_szYDr+G15OES&w%GXFTNmSF-aaeu-q01 z5xF`CHf~{5Rudz6zyK&DsKBJi+nj8{luP4%^HcxClSw=>9a4r9yeZSi+oB8W%aG1O zFl4H{7M$-aZ5}tdZk}MM%fA^T^9tBxeQaM;Rm{A)32i!`&tbvZJAv0t=Lp?qqV1hn zpK}88UtRR{)$LnvAgu9pyzAQIa#{!W=qy!n4h|bPzN>b>%}zXLJ-IX5n)H?C=cjq3 zH%qOF;XIfJ@E%; z1`cpu3SMi2+`aMvG?teMB9mkq7F0PvW1MVuq?hIQcS)5z&c`h6{1HZ`@2vxGasX3PNx zXh_~mBGI42v~?4Er)q*18)ck21Xe{!2G9?n!JCd#)W)ID?CWacnYmoMdg+)K1@NG* z*SY}FO;US2g23-18r(u}r2%+7DVsY)?h)>bULWBlc3tDHPNdU8C)V-;7*~o#a*oJ~ zD@DjEoHwP)G{`zRZSjD^Ga;9i@lx-FOC1}V6VoA9$A*`P`I;MjW0rLEL-G1W0^qVF zy+6hYE>d*L5Kjk&7_4<74v_|Z1*mZ?LGuna)i21K>P`Nt6hs(3WwJdZc!v<|bO5ly z8ZNoP&3(%fty5v@DD6{?^B%X1TPYDo2^6phr11u?pmu9wEMC^sc*AE6{oraC93mP6 zd);?mgyb!$jKHH1Z>{kfW2XvYQ&jHC4fdbjxBUq0VqH%ZM|OcxN)?g3`i>7w(oP<7 zSLNl_vuWl|Mfp2#O(P)wMtlj>tv7BFuOdO%U?iznpv3cS(V5}Nq-JEm)QiM%1-DTw z9_Mb9X*^HPdn*d|CXEVdI%SCCXU-?IO%e79=F41CAunr={wetMvx%!GofMKDJOR64 zIE!E%5wTf6E!{}6g*%(BlCtlWBr~nk&&m1xldm(!lHj<3D1N@NbQs?M0`lc*IR`yW z$inL%4=uJta+a#8$_2@|dxi?ugV8uD!DLUm40`dBKK7OlH`0Rsjq<%P3!ZE!A=K9 zPsI;zl=;b|!cGUev=lg_!jTs@Be9GfCMLZ%Xwg*$Pk{{Wu~VFGj|r^4rKnblh~>`; zVikcmHDI+T;xZ@w#*<^xR?Zy7QOG#(;`Y8MLO~%Z@n#Y-^W&|+LXyBTSALGq&@}zWKZxQeY_Wa|GjWuF%Jq?OLu(dsrfBxGIBhbt`jF-YO6#?bvrP zwL*&Te#dzRRFHL>Fp1pF^%mD>dVy2x{9`VWK3o8)wnnW1z_dTw58Dvn#RnobTC3oV|$( z^(gw{SU=|Q1oU%i4LDk0AvbQ>+|_wIyPToZaTMf=WmSsO=9R7xS$BG!v;??P`dph{ z+yXT>lI$BFB|QMW|GsfTknvi!ZLIU0o$n?o{H1zDo17q~eBbN_4j781kUg^*Y5 z@s}`wAK(b<(*}#B(g1Ii%|f|Ai=A^h`!Mh3nAqds!4#<$aC23KUcAE+T+KMaVJqnd zK^QR@xo8X&?}Gx|xyf{rx+pwmhu9okIwjSfL+cY4d+a}IFeDX@6<-MBE`n=CxL&g6 zzzN_*F|5gE(eZjk`&mM=fZI1H70nfpAgjeAM8@)Al* zim=ZK#W500Q&32DqNH{WT_-6BmE+|GkE*u=KXty_EQ}uU`#WJKzj1%OY-JC}=qfx# zeXXC(UMcgatNFz30QBcp7RmBqqPuuZ0ule5&L=i`7knhL`Lhoz0$Fc{Fv+Oqm1w&_ zq1i<*-IrH9lOlId>W10wE!2iC6L#w&?BZ3&gCuiYVwbQa2VcC7#(F#zkLhp{!dpuR*A_%Ykt-!hMKA zmabk3k+-K+7*)$rrxP*bK3;YHq7L+OlSL~-fahA6D7*WaU)#Zqv zx|Lo?;9;LE?1i^L=ryp=@GZ@4&cWppfB%O?^5cM*bNl+gjyi z15mZ_uDT3NoPORL1dWel2<9Sg!HP@l&?@e`i9&rdfOe870Mp^^1NDX7Gw?d4Ndjh)$ zJRSfl*~H_M*B8L06+2IsmS~sZ&+K)~gGQlxj-}?vOzkmwT>Bk$y{^<@ZJE^J%_2J` z@u1Jma4^x0fQ|+s2dvA*(zYloY~`(qCG~sZS0AQpZbmb;(h=}Xp2+fB+`c)An8Ji7 zrc769#tz9GvM&3M&oH0Z@3`h`0kg}WE89$7U^vbRDUw+5k%?>{#!a)s*%J1irWMr5 zGEf>q6Nk{u9(NAxUvo1jD@7`fRKn(Gi+c9^cK&xTaL=waLoA^lNb;FKy^`l3>A~6~ z>3I>V0dFZQ(6;z&@Zt8!HL5F z$?pi8W+uGnIKZ|+0Nejuj?asAI{Zt47IB_q^E>MnOxwBuYih##N9|aQFY>s!$*cRM zS6+mum$B+&CTIt_aH{PqTReWZuZ0o5(fZ-|^%6Hld&rtgBEXv&o}x$Gi{#-zt9=Si zZb?*|#GEkiQ=`WPXw3KSWx6$53n1`$F>w%>IB+VkBX)Y8_36aXFu)B;%bHhC$2})R zw1-qk`ZUjdUbpW)C(f5<$l8i2#YHnQ_B9+D!9%o$z8~q>!!Vp78W2y5LSJO1*_{U+ zG#qf7*gb;V!DrL+N20RJqG+fPu_M_Ezw-wEn*>(?p4q+2Bl+Edkt~^vR_|5vH9VSw zZ{?!O!nu`JWf$>TJN+%^0i_)_*;Eq`*y%sF_7>2hB3qTbO zRyM-$WFU$26Vh1HKw6JxJ}r#8&YJuPzjQ~YJ1tO$YN#BLXxCfLp46YJqM-loj#8?4 zlm#IEX&>Fg+_4I7`4G@X`qVs3BIPHPRf+;Bt7iqW_ZWSm3>eF|+|IE?t?E#I5b51-0OT;t45WU z+zls|=4B?cFubaFP-^$4|Jmd8jGS9oBZ(=vKq4D?clXhLS*=y^*K(h6N$_S9*!g1- z^A0RzWUAm8ns1JQy+tIr-BIj8(Grc=LIWxV(63n@>xR=OgJ48@oK+e(H0vRUhu9Oh%ema9nICFRw6vx<`NxmB0!v`v_Un=p2p$?ktBXSMhHrJn{`dwZQQ4i7+ zDi^UY`hZ6HfBt0VKQsKHd>I9Bu2ll_yGJ|5^<|pCBMA(X{06|vJiE~o58f&)=2YIR z5cN~YAj-dcJY9R_7;Sj}s#9_SWj%5 zD{ZTU^-6!LC8KN}>6}KfkHM8r77kz`AoA>{2DzxF(Sbl;QeX>^iWX(SsWpy3^LqaG z9rSdG&MKtMELYhU=iutuGTN}}x0}4(=ElFPS|nWl`g7Z=2b8B{UEmM?(whZ8`?eU9 z6@;4EfPjak=+Y#PTg*31eBqx5h`C+5LhEDT`=LH>Eb zX>%gkvnd%Bam2FWMl+g6^_W1%mRDaYF=8&Hn;z4r-KAv@2czq8Nxiq(1SBy}726TG zE}y(&H{)PcjwQ;~B%Tcq@ zWu?f;6wJ)Id$;bcu3jbGT~~gSU&LzjZa&91SV_0_B$zhbibEWbs zGh<~vqH?KY#CmM2xy+#0!NTGei-;03I|d8H?y$p=PfXF4p&W>`za{nC7-^A5epmt8 zwzTP+#n9;vSW$XQ3(vdd+^&t#{!wWwD5IHp&u8H|Bety0baVgjJx_r^)Hsx7E|6n@L=i=)LNReUV|lt(C$U7%kFe(7O>5@95O%Er;=7910hTr3+vIjUe(fO)2k)Pb)>D9UIA*VLA5_%6p4LHkUMffix3UuIHR5Kw|0} z(O1Z4QC}uZ5Xriuj8MfhN+BIX%V)rw6KdcHqp^hFQiZqY+wW1E<$a7g2+tbhev;l( zS9;*H^qZ5*oPWi$e|)!U+|B#bO|V&#XUUd5B`<-f!?VIB=90_i0j+Vxie=o`Wmy$~ zET&R#*v*4CbF@%H!^iMsBWoR0K+Du;6FaPT&7~X>3|JXA;ug2`y(6QncWJY$9+|K* z^q4DV&||xTQRq^EC7)&=ci+` zqpuM53rOOa%!9pe3BfubK-l|+HohNKyPfR)=YDtj%imejKT3}{BT2f_pZ;`5@BMz; z7ijJ+sqAS@Z-P*gJt+ijyVllTyU?qpLW5cGg0?<^mRZ@YJrV^cQQ~UDD?ysjhJUXQ8S4*HrDE>HRs&+>Spjt2O~> z=kvu$aRm$}oor(ctyGB6A;X6Nz?VKMJCG>@(rj=aV+?xJT1Zx9{WA2CHvBcx2la|i zIG;20!ug3-M2}b3%tS;p)Gbg6rv5@qI^UWQfQ=A@D1Ir4JoFXE zNbxiks1ESO0{7S2I}yFaAuK%nB~dm+60CMj_z)UFn`y@FB&E?iB4tAd%-=0N8hTI`5dI6Cy^ZqX4K(zF9IsM+fJu-ebImf>XBVPPQgFuF8M{==pP|p4QN7XZmop~TDG`AA!oFeqA+FYdf%IX zSG&s$^cBrUiLTQpWzOpEX5JN7xORk47?vOuC5@_+^HlrO5A6PN-a{n)brpBjyFEvw z(~IV{<@xT~?QZUKr(55>v9LV1-*Rowywt$8t%(2agvqV|KG;sChiQ>+5}UT%q@Ps{VqX-7B_bkO-Z%5A;#FHN}DL$ zAtS4)`?y1b>R*PececG=tf#LshZ4D>O}&4)rwkoA&Qtk-^;PzUeB_eBGND?N_xg&d z{bxEpO;E!1rUw8Cq&HNTRjr_C>=(Am#be`6&XYaAdETiWBuZRYod%qCr{sI_px~y0@%^gDcSP$uJ>t^k{_|4kzb{Jmz!{iiF$rQ1U-cZ0(N&1T#sw+SANg^ek`{(($-wnOY)3?{y)rJVCY{dG}L(AX&9ypa;VWmhje38dM zre$5Vv?6E>QE1rXLetDB_{Ej0KtT0Qn4rvv8neO*T`35(sEyR<-Zg0I;)ko` z8Gdgc)rLs4bxMiQgMTAWw?1_-G(C7hp4NOCv9Z;qL))32NF6G8BLwk(;Sl7iF{H7R z=iZ~n2R;2tNgop^sjhh6XXqGRbYWfekF&bV^n9eGTUU*1Y^t!A^CK_(7reeXh^ej; zdD!xyKOsS5)S?Vn$1t5+bAM-)p0*3B#xY6^xIGLTN>VmHQU&$uu5EKGms3C`s!N=Z zSM;}r&whPgs}1VNK7>-~<@^dtoSorC1&?hr|JI9=(!))~HYb0prY4@0s^h1=?LEnP z;6Sh9cItA5{Y3tdVSRb+xX`2BAnoq^q{7ju=P@#&-JskYhhr$8C=cAqL#Wp7kTF!A zzi{cZL*w^Kj3P;Hi-6tL~LPAvnhy{FKH^mfLSsqSRqK3F#xozLmewkXGBIJhq1OqX&Z_ zJv-+UjYmnNB5dGeP5S?{sm)DotEDi!5jbp002ovPDHLkV1m7r BDb)Y~ literal 0 HcmV?d00001 diff --git a/apps/dokploy/templates/linkwarden/docker-compose.yml b/apps/dokploy/templates/linkwarden/docker-compose.yml new file mode 100644 index 000000000..05ffb8a0a --- /dev/null +++ b/apps/dokploy/templates/linkwarden/docker-compose.yml @@ -0,0 +1,40 @@ +services: + linkwarden: + environment: + - NEXTAUTH_SECRET + - NEXTAUTH_URL + - DATABASE_URL=postgresql://linkwarden:${POSTGRES_PASSWORD}@postgres:5432/linkwarden + restart: unless-stopped + image: ghcr.io/linkwarden/linkwarden:v2.9.3 + ports: + - 3000 + volumes: + - linkwarden-data:/data/data + depends_on: + - postgres + healthcheck: + test: curl --fail http://localhost:3000 || exit 1 + interval: 60s + retries: 2 + start_period: 60s + timeout: 15s + + postgres: + image: postgres:17-alpine + restart: unless-stopped + user: postgres + environment: + POSTGRES_USER: linkwarden + POSTGRES_DB: linkwarden + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} + volumes: + - postgres-data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready"] + interval: 10s + timeout: 5s + retries: 5 + +volumes: + linkwarden-data: + postgres-data: diff --git a/apps/dokploy/templates/linkwarden/index.ts b/apps/dokploy/templates/linkwarden/index.ts new file mode 100644 index 000000000..860250356 --- /dev/null +++ b/apps/dokploy/templates/linkwarden/index.ts @@ -0,0 +1,33 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateBase64, + generatePassword, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + const postgresPassword = generatePassword(); + const nextSecret = generateBase64(32); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 3000, + serviceName: "linkwarden", + }, + ]; + + const envs = [ + `POSTGRES_PASSWORD=${postgresPassword}`, + `NEXTAUTH_SECRET=${nextSecret}`, + `NEXTAUTH_URL=http://${mainDomain}/api/v1/auth`, + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f9..44d194653 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -662,6 +662,21 @@ export const templates: TemplateData[] = [ tags: ["open-source"], load: () => import("./vaultwarden/index").then((m) => m.generate), }, + { + id: "linkwarden", + name: "Linkwarden", + version: "2.9.3", + description: + "Self-hosted, open-source collaborative bookmark manager to collect, organize and archive webpages.", + logo: "linkwarden.png", + links: { + github: "https://github.com/linkwarden/linkwarden", + website: "https://linkwarden.app/", + docs: "https://docs.linkwarden.app/", + }, + tags: ["bookmarks", "link-sharing"], + load: () => import("./linkwarden/index").then((m) => m.generate), + }, { id: "hi-events", name: "Hi.events", From 1294c2ad8e176c802f1e9ad87a46e906048d3d3b Mon Sep 17 00:00:00 2001 From: Nicholas Penree Date: Sun, 23 Feb 2025 12:07:54 -0500 Subject: [PATCH 101/126] feat: add Pocket ID template --- apps/dokploy/public/templates/pocket-id.svg | 1 + .../templates/pocket-id/docker-compose.yml | 21 ++++++++++++++ apps/dokploy/templates/pocket-id/index.ts | 29 +++++++++++++++++++ apps/dokploy/templates/templates.ts | 15 ++++++++++ 4 files changed, 66 insertions(+) create mode 100644 apps/dokploy/public/templates/pocket-id.svg create mode 100644 apps/dokploy/templates/pocket-id/docker-compose.yml create mode 100644 apps/dokploy/templates/pocket-id/index.ts diff --git a/apps/dokploy/public/templates/pocket-id.svg b/apps/dokploy/public/templates/pocket-id.svg new file mode 100644 index 000000000..0ee89b14b --- /dev/null +++ b/apps/dokploy/public/templates/pocket-id.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/apps/dokploy/templates/pocket-id/docker-compose.yml b/apps/dokploy/templates/pocket-id/docker-compose.yml new file mode 100644 index 000000000..f93851430 --- /dev/null +++ b/apps/dokploy/templates/pocket-id/docker-compose.yml @@ -0,0 +1,21 @@ +services: + pocket-id: + image: ghcr.io/pocket-id/pocket-id:v0.35.1 + restart: unless-stopped + environment: + - PUBLIC_UI_CONFIG_DISABLED + - PUBLIC_APP_URL + - TRUST_PROXY + ports: + - 80 + volumes: + - pocket-id-data:/app/backend/data + healthcheck: + test: "curl -f http://localhost/health" + interval: 1m30s + timeout: 5s + retries: 2 + start_period: 10s + +volumes: + pocket-id-data: diff --git a/apps/dokploy/templates/pocket-id/index.ts b/apps/dokploy/templates/pocket-id/index.ts new file mode 100644 index 000000000..9a9faa2a3 --- /dev/null +++ b/apps/dokploy/templates/pocket-id/index.ts @@ -0,0 +1,29 @@ +import { + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, +} from "../utils"; + +export function generate(schema: Schema): Template { + const mainDomain = generateRandomDomain(schema); + + const domains: DomainSchema[] = [ + { + host: mainDomain, + port: 80, + serviceName: "pocket-id", + }, + ]; + + const envs = [ + "PUBLIC_UI_CONFIG_DISABLED=false", + `PUBLIC_APP_URL=http://${mainDomain}`, + "TRUST_PROXY=true", + ]; + + return { + domains, + envs, + }; +} diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f9..7dafd8565 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1093,6 +1093,21 @@ export const templates: TemplateData[] = [ tags: ["identity", "auth"], load: () => import("./logto/index").then((m) => m.generate), }, + { + id: "pocket-id", + name: "Pocket ID", + version: "0.35.1", + description: + "A simple and easy-to-use OIDC provider that allows users to authenticate with their passkeys to your services.", + logo: "pocket-id.svg", + links: { + github: "https://github.com/pocket-id/pocket-id", + website: "https://pocket-id.org/", + docs: "https://pocket-id.org/docs", + }, + tags: ["identity", "auth"], + load: () => import("./pocket-id/index").then((m) => m.generate), + }, { id: "penpot", name: "Penpot", From fb4b5072501ab98d295caa27762ac33e91613cce Mon Sep 17 00:00:00 2001 From: sondreal Date: Sun, 23 Feb 2025 19:03:46 +0100 Subject: [PATCH 102/126] fixes typo outline->getoutline #1352 --- apps/dokploy/templates/templates.ts | 3112 +++++++++++++-------------- 1 file changed, 1556 insertions(+), 1556 deletions(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 31668a6f9..6d17532d5 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,1562 +1,1562 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ - { - id: "appwrite", - name: "Appwrite", - version: "1.6.0", - description: - "Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.\n" + - "Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", - links: { - github: "https://github.com/appwrite/appwrite", - website: "https://appwrite.io/", - docs: "https://appwrite.io/docs", - }, - logo: "appwrite.svg", - tags: ["database", "firebase", "postgres"], - load: () => import("./appwrite/index").then((m) => m.generate), - }, - { - id: "outline", - name: "Outline", - version: "0.82.0", - description: - "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", - links: { - github: "https://github.com/outline/outline", - website: "https://outline.com/", - docs: "https://docs.outline.com/", - }, - logo: "outline.png", - load: () => import("./outline/index").then((m) => m.generate), - tags: ["documentation", "knowledge-base", "self-hosted"], - }, - { - id: "supabase", - name: "SupaBase", - version: "1.24.07", - description: - "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", - links: { - github: "https://github.com/supabase/supabase", - website: "https://supabase.com/", - docs: "https://supabase.com/docs/guides/self-hosting", - }, - logo: "supabase.svg", - load: () => import("./supabase/index").then((m) => m.generate), - tags: ["database", "firebase", "postgres"], - }, - { - id: "pocketbase", - name: "Pocketbase", - version: "v0.22.12", - description: - "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", - links: { - github: "https://github.com/pocketbase/pocketbase", - website: "https://pocketbase.io/", - docs: "https://pocketbase.io/docs/", - }, - logo: "pocketbase.svg", - load: () => import("./pocketbase/index").then((m) => m.generate), - tags: ["database", "cms", "headless"], - }, - { - id: "plausible", - name: "Plausible", - version: "v2.1.5", - description: - "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "plausible.svg", - links: { - github: "https://github.com/plausible/plausible", - website: "https://plausible.io/", - docs: "https://plausible.io/docs", - }, - tags: ["analytics"], - load: () => import("./plausible/index").then((m) => m.generate), - }, - { - id: "calcom", - name: "Calcom", - version: "v2.7.6", - description: - "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", + { + id: "appwrite", + name: "Appwrite", + version: "1.6.0", + description: + "Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.\n" + + "Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", + links: { + github: "https://github.com/appwrite/appwrite", + website: "https://appwrite.io/", + docs: "https://appwrite.io/docs", + }, + logo: "appwrite.svg", + tags: ["database", "firebase", "postgres"], + load: () => import("./appwrite/index").then((m) => m.generate), + }, + { + id: "outline", + name: "Outline", + version: "0.82.0", + description: + "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", + links: { + github: "https://github.com/outline/outline", + website: "https://getoutline.com/", + docs: "https://docs.getoutline.com/s/guide", + }, + logo: "outline.png", + load: () => import("./outline/index").then((m) => m.generate), + tags: ["documentation", "knowledge-base", "self-hosted"], + }, + { + id: "supabase", + name: "SupaBase", + version: "1.24.07", + description: + "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", + links: { + github: "https://github.com/supabase/supabase", + website: "https://supabase.com/", + docs: "https://supabase.com/docs/guides/self-hosting", + }, + logo: "supabase.svg", + load: () => import("./supabase/index").then((m) => m.generate), + tags: ["database", "firebase", "postgres"], + }, + { + id: "pocketbase", + name: "Pocketbase", + version: "v0.22.12", + description: + "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", + links: { + github: "https://github.com/pocketbase/pocketbase", + website: "https://pocketbase.io/", + docs: "https://pocketbase.io/docs/", + }, + logo: "pocketbase.svg", + load: () => import("./pocketbase/index").then((m) => m.generate), + tags: ["database", "cms", "headless"], + }, + { + id: "plausible", + name: "Plausible", + version: "v2.1.5", + description: + "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "plausible.svg", + links: { + github: "https://github.com/plausible/plausible", + website: "https://plausible.io/", + docs: "https://plausible.io/docs", + }, + tags: ["analytics"], + load: () => import("./plausible/index").then((m) => m.generate), + }, + { + id: "calcom", + name: "Calcom", + version: "v2.7.6", + description: + "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", - links: { - github: "https://github.com/calcom/cal.com", - website: "https://cal.com/", - docs: "https://cal.com/docs", - }, - logo: "calcom.jpg", - tags: ["scheduling", "booking"], - load: () => import("./calcom/index").then((m) => m.generate), - }, - { - id: "grafana", - name: "Grafana", - version: "9.5.20", - description: - "Grafana is an open source platform for data visualization and monitoring.", - logo: "grafana.svg", - links: { - github: "https://github.com/grafana/grafana", - website: "https://grafana.com/", - docs: "https://grafana.com/docs/", - }, - tags: ["monitoring"], - load: () => import("./grafana/index").then((m) => m.generate), - }, - { - id: "directus", - name: "Directus", - version: "11.0.2", - description: - "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", - logo: "directus.jpg", - links: { - github: "https://github.com/directus/directus", - website: "https://directus.io/", - docs: "https://docs.directus.io/", - }, - tags: ["cms"], - load: () => import("./directus/index").then((m) => m.generate), - }, - { - id: "baserow", - name: "Baserow", - version: "1.25.2", - description: - "Baserow is an open source database management tool that allows you to create and manage databases.", - logo: "baserow.webp", - links: { - github: "https://github.com/Baserow/baserow", - website: "https://baserow.io/", - docs: "https://baserow.io/docs/index", - }, - tags: ["database"], - load: () => import("./baserow/index").then((m) => m.generate), - }, - { - id: "budibase", - name: "Budibase", - version: "3.2.25", - description: - "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", - logo: "budibase.svg", - links: { - github: "https://github.com/Budibase/budibase", - website: "https://budibase.com/", - docs: "https://docs.budibase.com/docs/", - }, - tags: ["database", "low-code", "nocode", "applications"], - load: () => import("./budibase/index").then((m) => m.generate), - }, - { - id: "ghost", - name: "Ghost", - version: "5.0.0", - description: - "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", - logo: "ghost.jpeg", - links: { - github: "https://github.com/TryGhost/Ghost", - website: "https://ghost.org/", - docs: "https://ghost.org/docs/", - }, - tags: ["cms"], - load: () => import("./ghost/index").then((m) => m.generate), - }, - { - id: "uptime-kuma", - name: "Uptime Kuma", - version: "1.23.15", - description: - "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", - logo: "uptime-kuma.png", - links: { - github: "https://github.com/louislam/uptime-kuma", - website: "https://uptime.kuma.pet/", - docs: "https://github.com/louislam/uptime-kuma/wiki", - }, - tags: ["monitoring"], - load: () => import("./uptime-kuma/index").then((m) => m.generate), - }, - { - id: "n8n", - name: "n8n", - version: "1.70.3", - description: - "n8n is an open source low-code platform for automating workflows and integrations.", - logo: "n8n.png", - links: { - github: "https://github.com/n8n-io/n8n", - website: "https://n8n.io/", - docs: "https://docs.n8n.io/", - }, - tags: ["automation"], - load: () => import("./n8n/index").then((m) => m.generate), - }, - { - id: "wordpress", - name: "Wordpress", - version: "6.7.1", - description: - "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", - logo: "wordpress.png", - links: { - github: "https://github.com/WordPress/WordPress", - website: "https://wordpress.org/", - docs: "https://wordpress.org/documentation/", - }, - tags: ["cms"], - load: () => import("./wordpress/index").then((m) => m.generate), - }, - { - id: "odoo", - name: "Odoo", - version: "16.0", - description: - "Odoo is a free and open source business management software that helps you manage your company's operations.", - logo: "odoo.png", - links: { - github: "https://github.com/odoo/odoo", - website: "https://odoo.com/", - docs: "https://www.odoo.com/documentation/", - }, - tags: ["cms"], - load: () => import("./odoo/index").then((m) => m.generate), - }, - { - id: "appsmith", - name: "Appsmith", - version: "v1.29", - description: - "Appsmith is a free and open source platform for building internal tools and applications.", - logo: "appsmith.png", - links: { - github: "https://github.com/appsmithorg/appsmith", - website: "https://appsmith.com/", - docs: "https://docs.appsmith.com/", - }, - tags: ["cms"], - load: () => import("./appsmith/index").then((m) => m.generate), - }, - { - id: "excalidraw", - name: "Excalidraw", - version: "latest", - description: - "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", - logo: "excalidraw.jpg", - links: { - github: "https://github.com/excalidraw/excalidraw", - website: "https://excalidraw.com/", - docs: "https://docs.excalidraw.com/", - }, - tags: ["drawing"], - load: () => import("./excalidraw/index").then((m) => m.generate), - }, - { - id: "documenso", - name: "Documenso", - version: "v1.5.6", - description: - "Documenso is the open source alternative to DocuSign for signing documents digitally", - links: { - github: "https://github.com/documenso/documenso", - website: "https://documenso.com/", - docs: "https://documenso.com/docs", - }, - logo: "documenso.png", - tags: ["document-signing"], - load: () => import("./documenso/index").then((m) => m.generate), - }, - { - id: "nocodb", - name: "NocoDB", - version: "0.257.2", - description: - "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", + links: { + github: "https://github.com/calcom/cal.com", + website: "https://cal.com/", + docs: "https://cal.com/docs", + }, + logo: "calcom.jpg", + tags: ["scheduling", "booking"], + load: () => import("./calcom/index").then((m) => m.generate), + }, + { + id: "grafana", + name: "Grafana", + version: "9.5.20", + description: + "Grafana is an open source platform for data visualization and monitoring.", + logo: "grafana.svg", + links: { + github: "https://github.com/grafana/grafana", + website: "https://grafana.com/", + docs: "https://grafana.com/docs/", + }, + tags: ["monitoring"], + load: () => import("./grafana/index").then((m) => m.generate), + }, + { + id: "directus", + name: "Directus", + version: "11.0.2", + description: + "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", + logo: "directus.jpg", + links: { + github: "https://github.com/directus/directus", + website: "https://directus.io/", + docs: "https://docs.directus.io/", + }, + tags: ["cms"], + load: () => import("./directus/index").then((m) => m.generate), + }, + { + id: "baserow", + name: "Baserow", + version: "1.25.2", + description: + "Baserow is an open source database management tool that allows you to create and manage databases.", + logo: "baserow.webp", + links: { + github: "https://github.com/Baserow/baserow", + website: "https://baserow.io/", + docs: "https://baserow.io/docs/index", + }, + tags: ["database"], + load: () => import("./baserow/index").then((m) => m.generate), + }, + { + id: "budibase", + name: "Budibase", + version: "3.2.25", + description: + "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", + logo: "budibase.svg", + links: { + github: "https://github.com/Budibase/budibase", + website: "https://budibase.com/", + docs: "https://docs.budibase.com/docs/", + }, + tags: ["database", "low-code", "nocode", "applications"], + load: () => import("./budibase/index").then((m) => m.generate), + }, + { + id: "ghost", + name: "Ghost", + version: "5.0.0", + description: + "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", + logo: "ghost.jpeg", + links: { + github: "https://github.com/TryGhost/Ghost", + website: "https://ghost.org/", + docs: "https://ghost.org/docs/", + }, + tags: ["cms"], + load: () => import("./ghost/index").then((m) => m.generate), + }, + { + id: "uptime-kuma", + name: "Uptime Kuma", + version: "1.23.15", + description: + "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", + logo: "uptime-kuma.png", + links: { + github: "https://github.com/louislam/uptime-kuma", + website: "https://uptime.kuma.pet/", + docs: "https://github.com/louislam/uptime-kuma/wiki", + }, + tags: ["monitoring"], + load: () => import("./uptime-kuma/index").then((m) => m.generate), + }, + { + id: "n8n", + name: "n8n", + version: "1.70.3", + description: + "n8n is an open source low-code platform for automating workflows and integrations.", + logo: "n8n.png", + links: { + github: "https://github.com/n8n-io/n8n", + website: "https://n8n.io/", + docs: "https://docs.n8n.io/", + }, + tags: ["automation"], + load: () => import("./n8n/index").then((m) => m.generate), + }, + { + id: "wordpress", + name: "Wordpress", + version: "6.7.1", + description: + "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", + logo: "wordpress.png", + links: { + github: "https://github.com/WordPress/WordPress", + website: "https://wordpress.org/", + docs: "https://wordpress.org/documentation/", + }, + tags: ["cms"], + load: () => import("./wordpress/index").then((m) => m.generate), + }, + { + id: "odoo", + name: "Odoo", + version: "16.0", + description: + "Odoo is a free and open source business management software that helps you manage your company's operations.", + logo: "odoo.png", + links: { + github: "https://github.com/odoo/odoo", + website: "https://odoo.com/", + docs: "https://www.odoo.com/documentation/", + }, + tags: ["cms"], + load: () => import("./odoo/index").then((m) => m.generate), + }, + { + id: "appsmith", + name: "Appsmith", + version: "v1.29", + description: + "Appsmith is a free and open source platform for building internal tools and applications.", + logo: "appsmith.png", + links: { + github: "https://github.com/appsmithorg/appsmith", + website: "https://appsmith.com/", + docs: "https://docs.appsmith.com/", + }, + tags: ["cms"], + load: () => import("./appsmith/index").then((m) => m.generate), + }, + { + id: "excalidraw", + name: "Excalidraw", + version: "latest", + description: + "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", + logo: "excalidraw.jpg", + links: { + github: "https://github.com/excalidraw/excalidraw", + website: "https://excalidraw.com/", + docs: "https://docs.excalidraw.com/", + }, + tags: ["drawing"], + load: () => import("./excalidraw/index").then((m) => m.generate), + }, + { + id: "documenso", + name: "Documenso", + version: "v1.5.6", + description: + "Documenso is the open source alternative to DocuSign for signing documents digitally", + links: { + github: "https://github.com/documenso/documenso", + website: "https://documenso.com/", + docs: "https://documenso.com/docs", + }, + logo: "documenso.png", + tags: ["document-signing"], + load: () => import("./documenso/index").then((m) => m.generate), + }, + { + id: "nocodb", + name: "NocoDB", + version: "0.257.2", + description: + "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", - links: { - github: "https://github.com/nocodb/nocodb", - website: "https://nocodb.com/", - docs: "https://docs.nocodb.com/", - }, - logo: "nocodb.png", - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./nocodb/index").then((m) => m.generate), - }, - { - id: "meilisearch", - name: "Meilisearch", - version: "v1.8.3", - description: - "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", - logo: "meilisearch.png", - links: { - github: "https://github.com/meilisearch/meilisearch", - website: "https://www.meilisearch.com/", - docs: "https://docs.meilisearch.com/", - }, - tags: ["search"], - load: () => import("./meilisearch/index").then((m) => m.generate), - }, - { - id: "phpmyadmin", - name: "Phpmyadmin", - version: "5.2.1", - description: - "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", - logo: "phpmyadmin.png", - links: { - github: "https://github.com/phpmyadmin/phpmyadmin", - website: "https://www.phpmyadmin.net/", - docs: "https://www.phpmyadmin.net/docs/", - }, - tags: ["database"], - load: () => import("./phpmyadmin/index").then((m) => m.generate), - }, - { - id: "rocketchat", - name: "Rocketchat", - version: "6.9.2", - description: - "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", - logo: "rocketchat.png", - links: { - github: "https://github.com/RocketChat/Rocket.Chat", - website: "https://rocket.chat/", - docs: "https://rocket.chat/docs/", - }, - tags: ["chat"], - load: () => import("./rocketchat/index").then((m) => m.generate), - }, - { - id: "minio", - name: "Minio", - description: - "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", - logo: "minio.png", - version: "latest", - links: { - github: "https://github.com/minio/minio", - website: "https://minio.io/", - docs: "https://docs.minio.io/", - }, - tags: ["storage"], - load: () => import("./minio/index").then((m) => m.generate), - }, - { - id: "metabase", - name: "Metabase", - version: "v0.50.8", - description: - "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", - logo: "metabase.png", - links: { - github: "https://github.com/metabase/metabase", - website: "https://www.metabase.com/", - docs: "https://www.metabase.com/docs/", - }, - tags: ["database", "dashboard"], - load: () => import("./metabase/index").then((m) => m.generate), - }, - { - id: "glitchtip", - name: "Glitchtip", - version: "v4.0", - description: "Glitchtip is simple, open source error tracking", - logo: "glitchtip.png", - links: { - github: "https://gitlab.com/glitchtip/", - website: "https://glitchtip.com/", - docs: "https://glitchtip.com/documentation", - }, - tags: ["hosting"], - load: () => import("./glitchtip/index").then((m) => m.generate), - }, - { - id: "open-webui", - name: "Open WebUI", - version: "v0.3.7", - description: - "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", - logo: "open-webui.png", - links: { - github: "https://github.com/open-webui/open-webui", - website: "https://openwebui.com/", - docs: "https://docs.openwebui.com/", - }, - tags: ["chat"], - load: () => import("./open-webui/index").then((m) => m.generate), - }, - { - id: "listmonk", - name: "Listmonk", - version: "v3.0.0", - description: - "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", - logo: "listmonk.png", - links: { - github: "https://github.com/knadh/listmonk", - website: "https://listmonk.app/", - docs: "https://listmonk.app/docs/", - }, - tags: ["email", "newsletter", "mailing-list"], - load: () => import("./listmonk/index").then((m) => m.generate), - }, - { - id: "doublezero", - name: "Double Zero", - version: "v0.2.1", - description: - "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", - logo: "doublezero.svg", - links: { - github: "https://github.com/technomancy-dev/00", - website: "https://www.double-zero.cloud/", - docs: "https://github.com/technomancy-dev/00", - }, - tags: ["email"], - load: () => import("./doublezero/index").then((m) => m.generate), - }, - { - id: "umami", - name: "Umami", - version: "v2.14.0", - description: - "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", - logo: "umami.png", - links: { - github: "https://github.com/umami-software/umami", - website: "https://umami.is", - docs: "https://umami.is/docs", - }, - tags: ["analytics"], - load: () => import("./umami/index").then((m) => m.generate), - }, - { - id: "jellyfin", - name: "jellyfin", - version: "v10.9.7", - description: - "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", - logo: "jellyfin.svg", - links: { - github: "https://github.com/jellyfin/jellyfin", - website: "https://jellyfin.org/", - docs: "https://jellyfin.org/docs/", - }, - tags: ["media system"], - load: () => import("./jellyfin/index").then((m) => m.generate), - }, - { - id: "teable", - name: "teable", - version: "v1.3.1-alpha-build.460", - description: - "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", - logo: "teable.png", - links: { - github: "https://github.com/teableio/teable", - website: "https://teable.io/", - docs: "https://help.teable.io/", - }, - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./teable/index").then((m) => m.generate), - }, - { - id: "zipline", - name: "Zipline", - version: "v3.7.9", - description: - "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", - logo: "zipline.png", - links: { - github: "https://github.com/diced/zipline", - website: "https://zipline.diced.sh/", - docs: "https://zipline.diced.sh/docs/", - }, - tags: ["media system", "storage"], - load: () => import("./zipline/index").then((m) => m.generate), - }, - { - id: "soketi", - name: "Soketi", - version: "v1.6.1-16", - description: - "Soketi is your simple, fast, and resilient open-source WebSockets server.", - logo: "soketi.png", - links: { - github: "https://github.com/soketi/soketi", - website: "https://soketi.app/", - docs: "https://docs.soketi.app/", - }, - tags: ["chat"], - load: () => import("./soketi/index").then((m) => m.generate), - }, - { - id: "aptabase", - name: "Aptabase", - version: "v1.0.0", - description: - "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "aptabase.svg", - links: { - github: "https://github.com/aptabase/aptabase", - website: "https://aptabase.com/", - docs: "https://github.com/aptabase/aptabase/blob/main/README.md", - }, - tags: ["analytics", "self-hosted"], - load: () => import("./aptabase/index").then((m) => m.generate), - }, - { - id: "typebot", - name: "Typebot", - version: "2.27.0", - description: "Typebot is an open-source chatbot builder platform.", - logo: "typebot.svg", - links: { - github: "https://github.com/baptisteArno/typebot.io", - website: "https://typebot.io/", - docs: "https://docs.typebot.io/get-started/introduction", - }, - tags: ["chatbot", "builder", "open-source"], - load: () => import("./typebot/index").then((m) => m.generate), - }, - { - id: "gitea", - name: "Gitea", - version: "1.22.3", - description: - "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", - logo: "gitea.png", - links: { - github: "https://github.com/go-gitea/gitea.git", - website: "https://gitea.com/", - docs: "https://docs.gitea.com/installation/install-with-docker", - }, - tags: ["self-hosted", "storage"], - load: () => import("./gitea/index").then((m) => m.generate), - }, - { - id: "roundcube", - name: "Roundcube", - version: "1.6.9", - description: - "Free and open source webmail software for the masses, written in PHP.", - logo: "roundcube.svg", - links: { - github: "https://github.com/roundcube/roundcubemail", - website: "https://roundcube.net/", - docs: "https://roundcube.net/about/", - }, - tags: ["self-hosted", "email", "webmail"], - load: () => import("./roundcube/index").then((m) => m.generate), - }, - { - id: "filebrowser", - name: "File Browser", - version: "2.31.2", - description: - "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", - logo: "filebrowser.svg", - links: { - github: "https://github.com/filebrowser/filebrowser", - website: "https://filebrowser.org/", - docs: "https://filebrowser.org/", - }, - tags: ["file-manager", "storage"], - load: () => import("./filebrowser/index").then((m) => m.generate), - }, - { - id: "tolgee", - name: "Tolgee", - version: "v3.80.4", - description: - "Developer & translator friendly web-based localization platform", - logo: "tolgee.svg", - links: { - github: "https://github.com/tolgee/tolgee-platform", - website: "https://tolgee.io", - docs: "https://tolgee.io/platform", - }, - tags: ["self-hosted", "i18n", "localization", "translations"], - load: () => import("./tolgee/index").then((m) => m.generate), - }, - { - id: "portainer", - name: "Portainer", - version: "2.21.4", - description: - "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", - logo: "portainer.svg", - links: { - github: "https://github.com/portainer/portainer", - website: "https://www.portainer.io/", - docs: "https://docs.portainer.io/", - }, - tags: ["cloud", "monitoring"], - load: () => import("./portainer/index").then((m) => m.generate), - }, - { - id: "influxdb", - name: "InfluxDB", - version: "2.7.10", - description: - "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", - logo: "influxdb.png", - links: { - github: "https://github.com/influxdata/influxdb", - website: "https://www.influxdata.com/", - docs: "https://docs.influxdata.com/influxdb/v2/", - }, - tags: ["self-hosted", "open-source", "storage", "database"], - load: () => import("./influxdb/index").then((m) => m.generate), - }, - { - id: "infisical", - name: "Infisical", - version: "0.90.1", - description: - "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", - logo: "infisical.jpg", - links: { - github: "https://github.com/Infisical/infisical", - website: "https://infisical.com/", - docs: "https://infisical.com/docs/documentation/getting-started/introduction", - }, - tags: ["self-hosted", "open-source"], - load: () => import("./infisical/index").then((m) => m.generate), - }, - { - id: "docmost", - name: "Docmost", - version: "0.4.1", - description: - "Docmost, is an open-source collaborative wiki and documentation software.", - logo: "docmost.png", - links: { - github: "https://github.com/docmost/docmost", - website: "https://docmost.com/", - docs: "https://docmost.com/docs/", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./docmost/index").then((m) => m.generate), - }, - { - id: "vaultwarden", - name: "Vaultwarden", - version: "1.32.7", - description: - "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", - logo: "vaultwarden.svg", - links: { - github: "https://github.com/dani-garcia/vaultwarden", - website: "", - docs: "https://github.com/dani-garcia/vaultwarden/wiki", - }, - tags: ["open-source"], - load: () => import("./vaultwarden/index").then((m) => m.generate), - }, - { - id: "hi-events", - name: "Hi.events", - version: "0.8.0-beta.1", - description: - "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", - logo: "hi-events.svg", - links: { - github: "https://github.com/HiEventsDev/hi.events", - website: "https://hi.events/", - docs: "https://hi.events/docs", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./hi-events/index").then((m) => m.generate), - }, - { - id: "windows", - name: "Windows (dockerized)", - version: "4.00", - description: "Windows inside a Docker container.", - logo: "windows.png", - links: { - github: "https://github.com/dockur/windows", - website: "", - docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./windows/index").then((m) => m.generate), - }, - { - id: "macos", - name: "MacOS (dockerized)", - version: "1.14", - description: "MacOS inside a Docker container.", - logo: "macos.png", - links: { - github: "https://github.com/dockur/macos", - website: "", - docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./macos/index").then((m) => m.generate), - }, - { - id: "coder", - name: "Coder", - version: "2.15.3", - description: - "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", - logo: "coder.svg", - links: { - github: "https://github.com/coder/coder", - website: "https://coder.com/", - docs: "https://coder.com/docs", - }, - tags: ["self-hosted", "open-source", "builder"], - load: () => import("./coder/index").then((m) => m.generate), - }, - { - id: "stirling", - name: "Stirling PDF", - version: "0.30.1", - description: "A locally hosted one-stop shop for all your PDF needs", - logo: "stirling.svg", - links: { - github: "https://github.com/Stirling-Tools/Stirling-PDF", - website: "https://www.stirlingpdf.com/", - docs: "https://docs.stirlingpdf.com/", - }, - tags: ["pdf", "tools"], - load: () => import("./stirling/index").then((m) => m.generate), - }, - { - id: "lobe-chat", - name: "Lobe Chat", - version: "v1.26.1", - description: "Lobe Chat - an open-source, modern-design AI chat framework.", - logo: "lobe-chat.png", - links: { - github: "https://github.com/lobehub/lobe-chat", - website: "https://chat-preview.lobehub.com/", - docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", - }, - tags: ["IA", "chat"], - load: () => import("./lobe-chat/index").then((m) => m.generate), - }, - { - id: "peppermint", - name: "Peppermint", - version: "latest", - description: - "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", - logo: "peppermint.svg", - links: { - github: "https://github.com/Peppermint-Lab/peppermint", - website: "https://peppermint.sh/", - docs: "https://docs.peppermint.sh/", - }, - tags: ["api", "development", "documentation"], - load: () => import("./peppermint/index").then((m) => m.generate), - }, - { - id: "windmill", - name: "Windmill", - version: "latest", - description: - "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", - logo: "windmill.svg", - links: { - github: "https://github.com/windmill-labs/windmill", - website: "https://www.windmill.dev/", - docs: "https://docs.windmill.dev/", - }, - tags: ["workflow", "automation", "development"], - load: () => import("./windmill/index").then((m) => m.generate), - }, - { - id: "activepieces", - name: "Activepieces", - version: "0.35.0", - description: - "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", - logo: "activepieces.svg", - links: { - github: "https://github.com/activepieces/activepieces", - website: "https://www.activepieces.com/", - docs: "https://www.activepieces.com/docs", - }, - tags: ["automation", "workflow", "no-code"], - load: () => import("./activepieces/index").then((m) => m.generate), - }, - { - id: "invoiceshelf", - name: "InvoiceShelf", - version: "latest", - description: - "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", - logo: "invoiceshelf.png", - links: { - github: "https://github.com/InvoiceShelf/invoiceshelf", - website: "https://invoiceshelf.com", - docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./invoiceshelf/index").then((m) => m.generate), - }, - { - id: "postiz", - name: "Postiz", - version: "latest", - description: - "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", - logo: "postiz.png", - links: { - github: "https://github.com/gitroomhq/postiz", - website: "https://postiz.com", - docs: "https://docs.postiz.com", - }, - tags: ["cms", "content-management", "publishing"], - load: () => import("./postiz/index").then((m) => m.generate), - }, - { - id: "slash", - name: "Slash", - version: "latest", - description: - "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", - logo: "slash.png", - links: { - github: "https://github.com/yourselfhosted/slash", - website: "https://github.com/yourselfhosted/slash#readme", - docs: "https://github.com/yourselfhosted/slash/wiki", - }, - tags: ["bookmarks", "link-shortener", "self-hosted"], - load: () => import("./slash/index").then((m) => m.generate), - }, - { - id: "discord-tickets", - name: "Discord Tickets", - version: "4.0.21", - description: - "An open-source Discord bot for creating and managing support ticket channels.", - logo: "discord-tickets.png", - links: { - github: "https://github.com/discord-tickets/bot", - website: "https://discordtickets.app", - docs: "https://discordtickets.app/self-hosting/installation/docker/", - }, - tags: ["discord", "tickets", "support"], - load: () => import("./discord-tickets/index").then((m) => m.generate), - }, - { - id: "nextcloud-aio", - name: "Nextcloud All in One", - version: "30.0.2", - description: - "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", - logo: "nextcloud-aio.svg", - links: { - github: "https://github.com/nextcloud/docker", - website: "https://nextcloud.com/", - docs: "https://docs.nextcloud.com/", - }, - tags: ["file-manager", "sync"], - load: () => import("./nextcloud-aio/index").then((m) => m.generate), - }, - { - id: "blender", - name: "Blender", - version: "latest", - description: - "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", - logo: "blender.svg", - links: { - github: "https://github.com/linuxserver/docker-blender", - website: "https://www.blender.org/", - docs: "https://docs.blender.org/", - }, - tags: ["3d", "rendering", "animation"], - load: () => import("./blender/index").then((m) => m.generate), - }, - { - id: "heyform", - name: "HeyForm", - version: "latest", - description: - "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", - logo: "heyform.svg", - links: { - github: "https://github.com/heyform/heyform", - website: "https://heyform.net", - docs: "https://docs.heyform.net", - }, - tags: ["form", "builder", "questionnaire", "quiz", "survey"], - load: () => import("./heyform/index").then((m) => m.generate), - }, - { - id: "chatwoot", - name: "Chatwoot", - version: "v3.14.1", - description: - "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", - logo: "chatwoot.svg", - links: { - github: "https://github.com/chatwoot/chatwoot", - website: "https://www.chatwoot.com", - docs: "https://www.chatwoot.com/docs", - }, - tags: ["support", "chat", "customer-service"], - load: () => import("./chatwoot/index").then((m) => m.generate), - }, - { - id: "discourse", - name: "Discourse", - version: "3.3.2", - description: - "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", - logo: "discourse.svg", - links: { - github: "https://github.com/discourse/discourse", - website: "https://www.discourse.org/", - docs: "https://meta.discourse.org/", - }, - tags: ["forum", "community", "discussion"], - load: () => import("./discourse/index").then((m) => m.generate), - }, - { - id: "immich", - name: "Immich", - version: "v1.121.0", - description: - "High performance self-hosted photo and video backup solution directly from your mobile phone.", - logo: "immich.svg", - links: { - github: "https://github.com/immich-app/immich", - website: "https://immich.app/", - docs: "https://immich.app/docs/overview/introduction", - }, - tags: ["photos", "videos", "backup", "media"], - load: () => import("./immich/index").then((m) => m.generate), - }, - { - id: "twenty", - name: "Twenty CRM", - version: "latest", - description: - "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", - logo: "twenty.svg", - links: { - github: "https://github.com/twentyhq/twenty", - website: "https://twenty.com", - docs: "https://docs.twenty.com", - }, - tags: ["crm", "sales", "business"], - load: () => import("./twenty/index").then((m) => m.generate), - }, - { - id: "yourls", - name: "YOURLS", - version: "1.9.2", - description: - "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", - logo: "yourls.svg", - links: { - github: "https://github.com/YOURLS/YOURLS", - website: "https://yourls.org/", - docs: "https://yourls.org/#documentation", - }, - tags: ["url-shortener", "php"], - load: () => import("./yourls/index").then((m) => m.generate), - }, - { - id: "ryot", - name: "Ryot", - version: "v7.10", - description: - "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", - logo: "ryot.png", - links: { - github: "https://github.com/IgnisDa/ryot", - website: "https://ryot.io/", - docs: "https://docs.ryot.io/", - }, - tags: ["media", "tracking", "self-hosted"], - load: () => import("./ryot/index").then((m) => m.generate), - }, - { - id: "photoprism", - name: "Photoprism", - version: "latest", - description: - "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", - logo: "photoprism.svg", - links: { - github: "https://github.com/photoprism/photoprism", - website: "https://www.photoprism.app/", - docs: "https://docs.photoprism.app/", - }, - tags: ["media", "photos", "self-hosted"], - load: () => import("./photoprism/index").then((m) => m.generate), - }, - { - id: "ontime", - name: "Ontime", - version: "v3.8.0", - description: - "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", - logo: "ontime.png", - links: { - github: "https://github.com/cpvalente/ontime/", - website: "https://getontime.no", - docs: "https://docs.getontime.no", - }, - tags: ["event"], - load: () => import("./ontime/index").then((m) => m.generate), - }, - { - id: "triggerdotdev", - name: "Trigger.dev", - version: "v3", - description: - "Trigger is a platform for building event-driven applications.", - logo: "triggerdotdev.svg", - links: { - github: "https://github.com/triggerdotdev/trigger.dev", - website: "https://trigger.dev/", - docs: "https://trigger.dev/docs", - }, - tags: ["event-driven", "applications"], - load: () => import("./triggerdotdev/index").then((m) => m.generate), - }, - { - id: "browserless", - name: "Browserless", - version: "2.23.0", - description: - "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", - logo: "browserless.svg", - links: { - github: "https://github.com/browserless/browserless", - website: "https://www.browserless.io/", - docs: "https://docs.browserless.io/", - }, - tags: ["browser", "automation"], - load: () => import("./browserless/index").then((m) => m.generate), - }, - { - id: "drawio", - name: "draw.io", - version: "24.7.17", - description: - "draw.io is a configurable diagramming/whiteboarding visualization application.", - logo: "drawio.svg", - links: { - github: "https://github.com/jgraph/drawio", - website: "https://draw.io/", - docs: "https://www.drawio.com/doc/", - }, - tags: ["drawing", "diagrams"], - load: () => import("./drawio/index").then((m) => m.generate), - }, - { - id: "kimai", - name: "Kimai", - version: "2.26.0", - description: - "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", - logo: "kimai.svg", - links: { - github: "https://github.com/kimai/kimai", - website: "https://www.kimai.org", - docs: "https://www.kimai.org/documentation", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./kimai/index").then((m) => m.generate), - }, - { - id: "logto", - name: "Logto", - version: "1.22.0", - description: - "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", - logo: "logto.png", - links: { - github: "https://github.com/logto-io/logto", - website: "https://logto.io/", - docs: "https://docs.logto.io/introduction", - }, - tags: ["identity", "auth"], - load: () => import("./logto/index").then((m) => m.generate), - }, - { - id: "penpot", - name: "Penpot", - version: "2.3.2", - description: - "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", - logo: "penpot.svg", - links: { - github: "https://github.com/penpot/penpot", - website: "https://penpot.app/", - docs: "https://docs.penpot.app/", - }, - tags: ["design", "collaboration"], - load: () => import("./penpot/index").then((m) => m.generate), - }, - { - id: "huly", - name: "Huly", - version: "0.6.377", - description: - "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", - logo: "huly.svg", - links: { - github: "https://github.com/hcengineering/huly-selfhost", - website: "https://huly.io/", - docs: "https://docs.huly.io/", - }, - tags: ["project-management", "community", "discussion"], - load: () => import("./huly/index").then((m) => m.generate), - }, - { - id: "unsend", - name: "Unsend", - version: "v1.3.2", - description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", - logo: "unsend.png", - links: { - github: "https://github.com/unsend-dev/unsend", - website: "https://unsend.dev/", - docs: "https://docs.unsend.dev/get-started/", - }, - tags: ["e-mail", "marketing", "business"], - load: () => import("./unsend/index").then((m) => m.generate), - }, - { - id: "langflow", - name: "Langflow", - version: "1.1.1", - description: - "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", - logo: "langflow.svg", - links: { - github: "https://github.com/langflow-ai/langflow/tree/main", - website: "https://www.langflow.org/", - docs: "https://docs.langflow.org/", - }, - tags: ["ai"], - load: () => import("./langflow/index").then((m) => m.generate), - }, - { - id: "elastic-search", - name: "Elasticsearch", - version: "8.10.2", - description: - "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", - logo: "elasticsearch.svg", - links: { - github: "https://github.com/elastic/elasticsearch", - website: "https://www.elastic.co/elasticsearch/", - docs: "https://docs.elastic.co/elasticsearch/", - }, - tags: ["search", "analytics"], - load: () => import("./elastic-search/index").then((m) => m.generate), - }, - { - id: "onedev", - name: "OneDev", - version: "11.6.6", - description: - "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", - logo: "onedev.png", - links: { - github: "https://github.com/theonedev/onedev/", - website: "https://onedev.io/", - docs: "https://docs.onedev.io/", - }, - tags: ["self-hosted", "development"], - load: () => import("./onedev/index").then((m) => m.generate), - }, - { - id: "unifi", - name: "Unifi Network", - version: "11.6.6", - description: - "Unifi Network is an open-source enterprise network management platform for wireless networks.", - logo: "unifi.webp", - links: { - github: "https://github.com/ubiquiti", - website: "https://www.ui.com/", - docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", - }, - tags: ["self-hosted", "networking"], - load: () => import("./unifi/index").then((m) => m.generate), - }, - { - id: "glpi", - name: "GLPI Project", - version: "10.0.16", - description: "The most complete open source service management software", - logo: "glpi.webp", - links: { - github: "https://github.com/glpi-project/glpi", - website: "https://glpi-project.org/", - docs: "https://glpi-project.org/documentation/", - }, - tags: ["self-hosted", "project-management", "management"], - load: () => import("./glpi/index").then((m) => m.generate), - }, - { - id: "checkmate", - name: "Checkmate", - version: "2.0.1", - description: - "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", - logo: "checkmate.png", - links: { - github: "https://github.com/bluewave-labs/checkmate", - website: "https://bluewavelabs.ca", - docs: "https://bluewavelabs.gitbook.io/checkmate", - }, - tags: ["self-hosted", "monitoring", "uptime"], - load: () => import("./checkmate/index").then((m) => m.generate), - }, - { - id: "gotenberg", - name: "Gotenberg", - version: "latest", - description: "Gotenberg is a Docker-powered stateless API for PDF files.", - logo: "gotenberg.png", - links: { - github: "https://github.com/gotenberg/gotenberg", - website: "https://gotenberg.dev", - docs: "https://gotenberg.dev/docs/getting-started/introduction", - }, - tags: ["api", "backend", "pdf", "tools"], - load: () => import("./gotenberg/index").then((m) => m.generate), - }, - { - id: "actualbudget", - name: "Actual Budget", - version: "latest", - description: - "A super fast and privacy-focused app for managing your finances.", - logo: "actualbudget.png", - links: { - github: "https://github.com/actualbudget/actual", - website: "https://actualbudget.org", - docs: "https://actualbudget.org/docs", - }, - tags: ["budgeting", "finance", "money"], - load: () => import("./actualbudget/index").then((m) => m.generate), - }, - { - id: "conduit", - name: "Conduit", - version: "v0.9.0", - description: - "Conduit is a simple, fast and reliable chat server powered by Matrix", - logo: "conduit.svg", - links: { - github: "https://gitlab.com/famedly/conduit", - website: "https://conduit.rs/", - docs: "https://docs.conduit.rs/", - }, - tags: ["matrix", "communication"], - load: () => import("./conduit/index").then((m) => m.generate), - }, - { - id: "evolutionapi", - name: "Evolution API", - version: "v2.1.2", - description: - "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", - logo: "evolutionapi.png", - links: { - github: "https://github.com/EvolutionAPI/evolution-api", - docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", - website: "https://evolution-api.com/opensource-whatsapp-api/", - }, - tags: ["api", "whatsapp", "messaging"], - load: () => import("./evolutionapi/index").then((m) => m.generate), - }, - { - id: "conduwuit", - name: "Conduwuit", - version: "latest", - description: - "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", - logo: "conduwuit.svg", - links: { - github: "https://github.com/girlbossceo/conduwuit", - website: "https://conduwuit.puppyirl.gay", - docs: "https://conduwuit.puppyirl.gay/configuration.html", - }, - tags: ["backend", "chat", "communication", "matrix", "server"], - load: () => import("./conduwuit/index").then((m) => m.generate), - }, - { - id: "cloudflared", - name: "Cloudflared", - version: "latest", - description: - "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", - logo: "cloudflared.svg", - links: { - github: "https://github.com/cloudflare/cloudflared", - website: - "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", - docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", - }, - tags: ["cloud", "networking", "security", "tunnel"], - load: () => import("./cloudflared/index").then((m) => m.generate), - }, - { - id: "couchdb", - name: "CouchDB", - version: "latest", - description: - "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", - logo: "couchdb.png", - links: { - github: "https://github.com/apache/couchdb", - website: "https://couchdb.apache.org/", - docs: "https://docs.couchdb.org/en/stable/", - }, - tags: ["database", "storage"], - load: () => import("./couchdb/index").then((m) => m.generate), - }, - { - id: "it-tools", - name: "IT Tools", - version: "latest", - description: "A collection of handy online it-tools for developers.", - logo: "it-tools.svg", - links: { - github: "https://github.com/CorentinTh/it-tools", - website: "https://it-tools.tech", - }, - tags: ["developer", "tools"], - load: () => import("./it-tools/index").then((m) => m.generate), - }, - { - id: "superset", - name: "Superset (Unofficial)", - version: "latest", - description: "Data visualization and data exploration platform.", - logo: "superset.svg", - links: { - github: "https://github.com/amancevice/docker-superset", - website: "https://superset.apache.org", - docs: "https://superset.apache.org/docs/intro", - }, - tags: ["analytics", "bi", "dashboard", "database", "sql"], - load: () => import("./superset/index").then((m) => m.generate), - }, - { - id: "glance", - name: "Glance", - version: "latest", - description: - "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", - logo: "glance.png", - links: { - github: "https://github.com/glanceapp/glance", - docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", - }, - tags: ["dashboard", "monitoring", "widgets", "rss"], - load: () => import("./glance/index").then((m) => m.generate), - }, - { - id: "homarr", - name: "Homarr", - version: "latest", - description: - "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", - logo: "homarr.png", - links: { - github: "https://github.com/homarr-labs/homarr", - docs: "https://homarr.dev/docs/getting-started/installation/docker", - website: "https://homarr.dev/", - }, - tags: ["dashboard", "monitoring"], - load: () => import("./homarr/index").then((m) => m.generate), - }, - { - id: "erpnext", - name: "ERPNext", - version: "version-15", - description: "100% Open Source and highly customizable ERP software.", - logo: "erpnext.svg", - links: { - github: "https://github.com/frappe/erpnext", - docs: "https://docs.frappe.io/erpnext", - website: "https://erpnext.com", - }, - tags: [ - "erp", - "accounts", - "manufacturing", - "retail", - "sales", - "pos", - "hrms", - ], - load: () => import("./erpnext/index").then((m) => m.generate), - }, - { - id: "maybe", - name: "Maybe", - version: "latest", - description: - "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", - logo: "maybe.svg", - links: { - github: "https://github.com/maybe-finance/maybe", - website: "https://maybe.finance/", - docs: "https://docs.maybe.finance/", - }, - tags: ["finance", "self-hosted"], - load: () => import("./maybe/index").then((m) => m.generate), - }, - { - id: "spacedrive", - name: "Spacedrive", - version: "latest", - description: - "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", - links: { - github: "https://github.com/spacedriveapp/spacedrive", - website: "https://spacedrive.com/", - docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", - }, - logo: "spacedrive.png", - tags: ["file-manager", "vdfs", "storage"], - load: () => import("./spacedrive/index").then((m) => m.generate), - }, - { - id: "registry", - name: "Docker Registry", - version: "2", - description: - "Distribution implementation for storing and distributing of Docker container images and artifacts.", - links: { - github: "https://github.com/distribution/distribution", - website: "https://hub.docker.com/_/registry", - docs: "https://distribution.github.io/distribution/", - }, - logo: "registry.png", - tags: ["registry", "docker", "self-hosted"], - load: () => import("./registry/index").then((m) => m.generate), - }, - { - id: "alist", - name: "AList", - version: "v3.41.0", - description: - "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", - logo: "alist.svg", - links: { - github: "https://github.com/AlistGo/alist", - website: "https://alist.nn.ci", - docs: "https://alist.nn.ci/guide/install/docker.html", - }, - tags: ["file", "webdav", "storage"], - load: () => import("./alist/index").then((m) => m.generate), - }, - { - id: "answer", - name: "Answer", - version: "v1.4.1", - description: - "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", - logo: "answer.png", - links: { - github: "https://github.com/apache/answer", - website: "https://answer.apache.org/", - docs: "https://answer.apache.org/docs", - }, - tags: ["q&a", "self-hosted"], - load: () => import("./answer/index").then((m) => m.generate), - }, - { - id: "shlink", - name: "Shlink", - version: "stable", - description: - "URL shortener that can be used to serve shortened URLs under your own domain.", - logo: "shlink.svg", - links: { - github: "https://github.com/shlinkio/shlink", - website: "https://shlink.io", - docs: "https://shlink.io/documentation", - }, - tags: ["sharing", "shortener", "url"], - load: () => import("./shlink/index").then((m) => m.generate), - }, - { - id: "frappe-hr", - name: "Frappe HR", - version: "version-15", - description: - "Feature rich HR & Payroll software. 100% FOSS and customizable.", - logo: "frappe-hr.svg", - links: { - github: "https://github.com/frappe/hrms", - docs: "https://docs.frappe.io/hr", - website: "https://frappe.io/hr", - }, - tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], - load: () => import("./frappe-hr/index").then((m) => m.generate), - }, - { - id: "formbricks", - name: "Formbricks", - version: "v3.1.3", - description: - "Formbricks is an open-source survey and form platform for collecting user data.", - logo: "formbricks.png", - links: { - github: "https://github.com/formbricks/formbricks", - website: "https://formbricks.com/", - docs: "https://formbricks.com/docs", - }, - tags: ["forms", "analytics"], - load: () => import("./formbricks/index").then((m) => m.generate), - }, - { - id: "trilium", - name: "Trilium", - description: - "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", - logo: "trilium.png", - version: "latest", - links: { - github: "https://github.com/zadam/trilium", - website: "https://github.com/zadam/trilium", - docs: "https://github.com/zadam/trilium/wiki/", - }, - tags: ["self-hosted", "productivity", "personal-use"], - load: () => import("./trilium/index").then((m) => m.generate), - }, - { - id: "convex", - name: "Convex", - version: "latest", - description: - "Convex is an open-source reactive database designed to make life easy for web app developers.", - logo: "convex.svg", - links: { - github: "https://github.com/get-convex/convex", - website: "https://www.convex.dev/", - docs: "https://www.convex.dev/docs", - }, - tags: ["backend", "database", "api"], - load: () => import("./convex/index").then((m) => m.generate), - }, + links: { + github: "https://github.com/nocodb/nocodb", + website: "https://nocodb.com/", + docs: "https://docs.nocodb.com/", + }, + logo: "nocodb.png", + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./nocodb/index").then((m) => m.generate), + }, + { + id: "meilisearch", + name: "Meilisearch", + version: "v1.8.3", + description: + "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", + logo: "meilisearch.png", + links: { + github: "https://github.com/meilisearch/meilisearch", + website: "https://www.meilisearch.com/", + docs: "https://docs.meilisearch.com/", + }, + tags: ["search"], + load: () => import("./meilisearch/index").then((m) => m.generate), + }, + { + id: "phpmyadmin", + name: "Phpmyadmin", + version: "5.2.1", + description: + "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", + logo: "phpmyadmin.png", + links: { + github: "https://github.com/phpmyadmin/phpmyadmin", + website: "https://www.phpmyadmin.net/", + docs: "https://www.phpmyadmin.net/docs/", + }, + tags: ["database"], + load: () => import("./phpmyadmin/index").then((m) => m.generate), + }, + { + id: "rocketchat", + name: "Rocketchat", + version: "6.9.2", + description: + "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", + logo: "rocketchat.png", + links: { + github: "https://github.com/RocketChat/Rocket.Chat", + website: "https://rocket.chat/", + docs: "https://rocket.chat/docs/", + }, + tags: ["chat"], + load: () => import("./rocketchat/index").then((m) => m.generate), + }, + { + id: "minio", + name: "Minio", + description: + "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", + logo: "minio.png", + version: "latest", + links: { + github: "https://github.com/minio/minio", + website: "https://minio.io/", + docs: "https://docs.minio.io/", + }, + tags: ["storage"], + load: () => import("./minio/index").then((m) => m.generate), + }, + { + id: "metabase", + name: "Metabase", + version: "v0.50.8", + description: + "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", + logo: "metabase.png", + links: { + github: "https://github.com/metabase/metabase", + website: "https://www.metabase.com/", + docs: "https://www.metabase.com/docs/", + }, + tags: ["database", "dashboard"], + load: () => import("./metabase/index").then((m) => m.generate), + }, + { + id: "glitchtip", + name: "Glitchtip", + version: "v4.0", + description: "Glitchtip is simple, open source error tracking", + logo: "glitchtip.png", + links: { + github: "https://gitlab.com/glitchtip/", + website: "https://glitchtip.com/", + docs: "https://glitchtip.com/documentation", + }, + tags: ["hosting"], + load: () => import("./glitchtip/index").then((m) => m.generate), + }, + { + id: "open-webui", + name: "Open WebUI", + version: "v0.3.7", + description: + "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", + logo: "open-webui.png", + links: { + github: "https://github.com/open-webui/open-webui", + website: "https://openwebui.com/", + docs: "https://docs.openwebui.com/", + }, + tags: ["chat"], + load: () => import("./open-webui/index").then((m) => m.generate), + }, + { + id: "listmonk", + name: "Listmonk", + version: "v3.0.0", + description: + "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", + logo: "listmonk.png", + links: { + github: "https://github.com/knadh/listmonk", + website: "https://listmonk.app/", + docs: "https://listmonk.app/docs/", + }, + tags: ["email", "newsletter", "mailing-list"], + load: () => import("./listmonk/index").then((m) => m.generate), + }, + { + id: "doublezero", + name: "Double Zero", + version: "v0.2.1", + description: + "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", + logo: "doublezero.svg", + links: { + github: "https://github.com/technomancy-dev/00", + website: "https://www.double-zero.cloud/", + docs: "https://github.com/technomancy-dev/00", + }, + tags: ["email"], + load: () => import("./doublezero/index").then((m) => m.generate), + }, + { + id: "umami", + name: "Umami", + version: "v2.14.0", + description: + "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", + logo: "umami.png", + links: { + github: "https://github.com/umami-software/umami", + website: "https://umami.is", + docs: "https://umami.is/docs", + }, + tags: ["analytics"], + load: () => import("./umami/index").then((m) => m.generate), + }, + { + id: "jellyfin", + name: "jellyfin", + version: "v10.9.7", + description: + "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", + logo: "jellyfin.svg", + links: { + github: "https://github.com/jellyfin/jellyfin", + website: "https://jellyfin.org/", + docs: "https://jellyfin.org/docs/", + }, + tags: ["media system"], + load: () => import("./jellyfin/index").then((m) => m.generate), + }, + { + id: "teable", + name: "teable", + version: "v1.3.1-alpha-build.460", + description: + "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", + logo: "teable.png", + links: { + github: "https://github.com/teableio/teable", + website: "https://teable.io/", + docs: "https://help.teable.io/", + }, + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./teable/index").then((m) => m.generate), + }, + { + id: "zipline", + name: "Zipline", + version: "v3.7.9", + description: + "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", + logo: "zipline.png", + links: { + github: "https://github.com/diced/zipline", + website: "https://zipline.diced.sh/", + docs: "https://zipline.diced.sh/docs/", + }, + tags: ["media system", "storage"], + load: () => import("./zipline/index").then((m) => m.generate), + }, + { + id: "soketi", + name: "Soketi", + version: "v1.6.1-16", + description: + "Soketi is your simple, fast, and resilient open-source WebSockets server.", + logo: "soketi.png", + links: { + github: "https://github.com/soketi/soketi", + website: "https://soketi.app/", + docs: "https://docs.soketi.app/", + }, + tags: ["chat"], + load: () => import("./soketi/index").then((m) => m.generate), + }, + { + id: "aptabase", + name: "Aptabase", + version: "v1.0.0", + description: + "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "aptabase.svg", + links: { + github: "https://github.com/aptabase/aptabase", + website: "https://aptabase.com/", + docs: "https://github.com/aptabase/aptabase/blob/main/README.md", + }, + tags: ["analytics", "self-hosted"], + load: () => import("./aptabase/index").then((m) => m.generate), + }, + { + id: "typebot", + name: "Typebot", + version: "2.27.0", + description: "Typebot is an open-source chatbot builder platform.", + logo: "typebot.svg", + links: { + github: "https://github.com/baptisteArno/typebot.io", + website: "https://typebot.io/", + docs: "https://docs.typebot.io/get-started/introduction", + }, + tags: ["chatbot", "builder", "open-source"], + load: () => import("./typebot/index").then((m) => m.generate), + }, + { + id: "gitea", + name: "Gitea", + version: "1.22.3", + description: + "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", + logo: "gitea.png", + links: { + github: "https://github.com/go-gitea/gitea.git", + website: "https://gitea.com/", + docs: "https://docs.gitea.com/installation/install-with-docker", + }, + tags: ["self-hosted", "storage"], + load: () => import("./gitea/index").then((m) => m.generate), + }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", + }, + tags: ["self-hosted", "email", "webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + }, + { + id: "filebrowser", + name: "File Browser", + version: "2.31.2", + description: + "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", + logo: "filebrowser.svg", + links: { + github: "https://github.com/filebrowser/filebrowser", + website: "https://filebrowser.org/", + docs: "https://filebrowser.org/", + }, + tags: ["file-manager", "storage"], + load: () => import("./filebrowser/index").then((m) => m.generate), + }, + { + id: "tolgee", + name: "Tolgee", + version: "v3.80.4", + description: + "Developer & translator friendly web-based localization platform", + logo: "tolgee.svg", + links: { + github: "https://github.com/tolgee/tolgee-platform", + website: "https://tolgee.io", + docs: "https://tolgee.io/platform", + }, + tags: ["self-hosted", "i18n", "localization", "translations"], + load: () => import("./tolgee/index").then((m) => m.generate), + }, + { + id: "portainer", + name: "Portainer", + version: "2.21.4", + description: + "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", + logo: "portainer.svg", + links: { + github: "https://github.com/portainer/portainer", + website: "https://www.portainer.io/", + docs: "https://docs.portainer.io/", + }, + tags: ["cloud", "monitoring"], + load: () => import("./portainer/index").then((m) => m.generate), + }, + { + id: "influxdb", + name: "InfluxDB", + version: "2.7.10", + description: + "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", + logo: "influxdb.png", + links: { + github: "https://github.com/influxdata/influxdb", + website: "https://www.influxdata.com/", + docs: "https://docs.influxdata.com/influxdb/v2/", + }, + tags: ["self-hosted", "open-source", "storage", "database"], + load: () => import("./influxdb/index").then((m) => m.generate), + }, + { + id: "infisical", + name: "Infisical", + version: "0.90.1", + description: + "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", + logo: "infisical.jpg", + links: { + github: "https://github.com/Infisical/infisical", + website: "https://infisical.com/", + docs: "https://infisical.com/docs/documentation/getting-started/introduction", + }, + tags: ["self-hosted", "open-source"], + load: () => import("./infisical/index").then((m) => m.generate), + }, + { + id: "docmost", + name: "Docmost", + version: "0.4.1", + description: + "Docmost, is an open-source collaborative wiki and documentation software.", + logo: "docmost.png", + links: { + github: "https://github.com/docmost/docmost", + website: "https://docmost.com/", + docs: "https://docmost.com/docs/", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./docmost/index").then((m) => m.generate), + }, + { + id: "vaultwarden", + name: "Vaultwarden", + version: "1.32.7", + description: + "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", + logo: "vaultwarden.svg", + links: { + github: "https://github.com/dani-garcia/vaultwarden", + website: "", + docs: "https://github.com/dani-garcia/vaultwarden/wiki", + }, + tags: ["open-source"], + load: () => import("./vaultwarden/index").then((m) => m.generate), + }, + { + id: "hi-events", + name: "Hi.events", + version: "0.8.0-beta.1", + description: + "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", + logo: "hi-events.svg", + links: { + github: "https://github.com/HiEventsDev/hi.events", + website: "https://hi.events/", + docs: "https://hi.events/docs", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./hi-events/index").then((m) => m.generate), + }, + { + id: "windows", + name: "Windows (dockerized)", + version: "4.00", + description: "Windows inside a Docker container.", + logo: "windows.png", + links: { + github: "https://github.com/dockur/windows", + website: "", + docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./windows/index").then((m) => m.generate), + }, + { + id: "macos", + name: "MacOS (dockerized)", + version: "1.14", + description: "MacOS inside a Docker container.", + logo: "macos.png", + links: { + github: "https://github.com/dockur/macos", + website: "", + docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./macos/index").then((m) => m.generate), + }, + { + id: "coder", + name: "Coder", + version: "2.15.3", + description: + "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", + logo: "coder.svg", + links: { + github: "https://github.com/coder/coder", + website: "https://coder.com/", + docs: "https://coder.com/docs", + }, + tags: ["self-hosted", "open-source", "builder"], + load: () => import("./coder/index").then((m) => m.generate), + }, + { + id: "stirling", + name: "Stirling PDF", + version: "0.30.1", + description: "A locally hosted one-stop shop for all your PDF needs", + logo: "stirling.svg", + links: { + github: "https://github.com/Stirling-Tools/Stirling-PDF", + website: "https://www.stirlingpdf.com/", + docs: "https://docs.stirlingpdf.com/", + }, + tags: ["pdf", "tools"], + load: () => import("./stirling/index").then((m) => m.generate), + }, + { + id: "lobe-chat", + name: "Lobe Chat", + version: "v1.26.1", + description: "Lobe Chat - an open-source, modern-design AI chat framework.", + logo: "lobe-chat.png", + links: { + github: "https://github.com/lobehub/lobe-chat", + website: "https://chat-preview.lobehub.com/", + docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", + }, + tags: ["IA", "chat"], + load: () => import("./lobe-chat/index").then((m) => m.generate), + }, + { + id: "peppermint", + name: "Peppermint", + version: "latest", + description: + "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", + logo: "peppermint.svg", + links: { + github: "https://github.com/Peppermint-Lab/peppermint", + website: "https://peppermint.sh/", + docs: "https://docs.peppermint.sh/", + }, + tags: ["api", "development", "documentation"], + load: () => import("./peppermint/index").then((m) => m.generate), + }, + { + id: "windmill", + name: "Windmill", + version: "latest", + description: + "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", + logo: "windmill.svg", + links: { + github: "https://github.com/windmill-labs/windmill", + website: "https://www.windmill.dev/", + docs: "https://docs.windmill.dev/", + }, + tags: ["workflow", "automation", "development"], + load: () => import("./windmill/index").then((m) => m.generate), + }, + { + id: "activepieces", + name: "Activepieces", + version: "0.35.0", + description: + "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", + logo: "activepieces.svg", + links: { + github: "https://github.com/activepieces/activepieces", + website: "https://www.activepieces.com/", + docs: "https://www.activepieces.com/docs", + }, + tags: ["automation", "workflow", "no-code"], + load: () => import("./activepieces/index").then((m) => m.generate), + }, + { + id: "invoiceshelf", + name: "InvoiceShelf", + version: "latest", + description: + "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", + logo: "invoiceshelf.png", + links: { + github: "https://github.com/InvoiceShelf/invoiceshelf", + website: "https://invoiceshelf.com", + docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./invoiceshelf/index").then((m) => m.generate), + }, + { + id: "postiz", + name: "Postiz", + version: "latest", + description: + "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", + logo: "postiz.png", + links: { + github: "https://github.com/gitroomhq/postiz", + website: "https://postiz.com", + docs: "https://docs.postiz.com", + }, + tags: ["cms", "content-management", "publishing"], + load: () => import("./postiz/index").then((m) => m.generate), + }, + { + id: "slash", + name: "Slash", + version: "latest", + description: + "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", + logo: "slash.png", + links: { + github: "https://github.com/yourselfhosted/slash", + website: "https://github.com/yourselfhosted/slash#readme", + docs: "https://github.com/yourselfhosted/slash/wiki", + }, + tags: ["bookmarks", "link-shortener", "self-hosted"], + load: () => import("./slash/index").then((m) => m.generate), + }, + { + id: "discord-tickets", + name: "Discord Tickets", + version: "4.0.21", + description: + "An open-source Discord bot for creating and managing support ticket channels.", + logo: "discord-tickets.png", + links: { + github: "https://github.com/discord-tickets/bot", + website: "https://discordtickets.app", + docs: "https://discordtickets.app/self-hosting/installation/docker/", + }, + tags: ["discord", "tickets", "support"], + load: () => import("./discord-tickets/index").then((m) => m.generate), + }, + { + id: "nextcloud-aio", + name: "Nextcloud All in One", + version: "30.0.2", + description: + "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", + logo: "nextcloud-aio.svg", + links: { + github: "https://github.com/nextcloud/docker", + website: "https://nextcloud.com/", + docs: "https://docs.nextcloud.com/", + }, + tags: ["file-manager", "sync"], + load: () => import("./nextcloud-aio/index").then((m) => m.generate), + }, + { + id: "blender", + name: "Blender", + version: "latest", + description: + "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + logo: "blender.svg", + links: { + github: "https://github.com/linuxserver/docker-blender", + website: "https://www.blender.org/", + docs: "https://docs.blender.org/", + }, + tags: ["3d", "rendering", "animation"], + load: () => import("./blender/index").then((m) => m.generate), + }, + { + id: "heyform", + name: "HeyForm", + version: "latest", + description: + "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", + logo: "heyform.svg", + links: { + github: "https://github.com/heyform/heyform", + website: "https://heyform.net", + docs: "https://docs.heyform.net", + }, + tags: ["form", "builder", "questionnaire", "quiz", "survey"], + load: () => import("./heyform/index").then((m) => m.generate), + }, + { + id: "chatwoot", + name: "Chatwoot", + version: "v3.14.1", + description: + "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", + logo: "chatwoot.svg", + links: { + github: "https://github.com/chatwoot/chatwoot", + website: "https://www.chatwoot.com", + docs: "https://www.chatwoot.com/docs", + }, + tags: ["support", "chat", "customer-service"], + load: () => import("./chatwoot/index").then((m) => m.generate), + }, + { + id: "discourse", + name: "Discourse", + version: "3.3.2", + description: + "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", + logo: "discourse.svg", + links: { + github: "https://github.com/discourse/discourse", + website: "https://www.discourse.org/", + docs: "https://meta.discourse.org/", + }, + tags: ["forum", "community", "discussion"], + load: () => import("./discourse/index").then((m) => m.generate), + }, + { + id: "immich", + name: "Immich", + version: "v1.121.0", + description: + "High performance self-hosted photo and video backup solution directly from your mobile phone.", + logo: "immich.svg", + links: { + github: "https://github.com/immich-app/immich", + website: "https://immich.app/", + docs: "https://immich.app/docs/overview/introduction", + }, + tags: ["photos", "videos", "backup", "media"], + load: () => import("./immich/index").then((m) => m.generate), + }, + { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { + id: "yourls", + name: "YOURLS", + version: "1.9.2", + description: + "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", + logo: "yourls.svg", + links: { + github: "https://github.com/YOURLS/YOURLS", + website: "https://yourls.org/", + docs: "https://yourls.org/#documentation", + }, + tags: ["url-shortener", "php"], + load: () => import("./yourls/index").then((m) => m.generate), + }, + { + id: "ryot", + name: "Ryot", + version: "v7.10", + description: + "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", + logo: "ryot.png", + links: { + github: "https://github.com/IgnisDa/ryot", + website: "https://ryot.io/", + docs: "https://docs.ryot.io/", + }, + tags: ["media", "tracking", "self-hosted"], + load: () => import("./ryot/index").then((m) => m.generate), + }, + { + id: "photoprism", + name: "Photoprism", + version: "latest", + description: + "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + logo: "photoprism.svg", + links: { + github: "https://github.com/photoprism/photoprism", + website: "https://www.photoprism.app/", + docs: "https://docs.photoprism.app/", + }, + tags: ["media", "photos", "self-hosted"], + load: () => import("./photoprism/index").then((m) => m.generate), + }, + { + id: "ontime", + name: "Ontime", + version: "v3.8.0", + description: + "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", + logo: "ontime.png", + links: { + github: "https://github.com/cpvalente/ontime/", + website: "https://getontime.no", + docs: "https://docs.getontime.no", + }, + tags: ["event"], + load: () => import("./ontime/index").then((m) => m.generate), + }, + { + id: "triggerdotdev", + name: "Trigger.dev", + version: "v3", + description: + "Trigger is a platform for building event-driven applications.", + logo: "triggerdotdev.svg", + links: { + github: "https://github.com/triggerdotdev/trigger.dev", + website: "https://trigger.dev/", + docs: "https://trigger.dev/docs", + }, + tags: ["event-driven", "applications"], + load: () => import("./triggerdotdev/index").then((m) => m.generate), + }, + { + id: "browserless", + name: "Browserless", + version: "2.23.0", + description: + "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", + logo: "browserless.svg", + links: { + github: "https://github.com/browserless/browserless", + website: "https://www.browserless.io/", + docs: "https://docs.browserless.io/", + }, + tags: ["browser", "automation"], + load: () => import("./browserless/index").then((m) => m.generate), + }, + { + id: "drawio", + name: "draw.io", + version: "24.7.17", + description: + "draw.io is a configurable diagramming/whiteboarding visualization application.", + logo: "drawio.svg", + links: { + github: "https://github.com/jgraph/drawio", + website: "https://draw.io/", + docs: "https://www.drawio.com/doc/", + }, + tags: ["drawing", "diagrams"], + load: () => import("./drawio/index").then((m) => m.generate), + }, + { + id: "kimai", + name: "Kimai", + version: "2.26.0", + description: + "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", + logo: "kimai.svg", + links: { + github: "https://github.com/kimai/kimai", + website: "https://www.kimai.org", + docs: "https://www.kimai.org/documentation", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./kimai/index").then((m) => m.generate), + }, + { + id: "logto", + name: "Logto", + version: "1.22.0", + description: + "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", + logo: "logto.png", + links: { + github: "https://github.com/logto-io/logto", + website: "https://logto.io/", + docs: "https://docs.logto.io/introduction", + }, + tags: ["identity", "auth"], + load: () => import("./logto/index").then((m) => m.generate), + }, + { + id: "penpot", + name: "Penpot", + version: "2.3.2", + description: + "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", + logo: "penpot.svg", + links: { + github: "https://github.com/penpot/penpot", + website: "https://penpot.app/", + docs: "https://docs.penpot.app/", + }, + tags: ["design", "collaboration"], + load: () => import("./penpot/index").then((m) => m.generate), + }, + { + id: "huly", + name: "Huly", + version: "0.6.377", + description: + "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", + logo: "huly.svg", + links: { + github: "https://github.com/hcengineering/huly-selfhost", + website: "https://huly.io/", + docs: "https://docs.huly.io/", + }, + tags: ["project-management", "community", "discussion"], + load: () => import("./huly/index").then((m) => m.generate), + }, + { + id: "unsend", + name: "Unsend", + version: "v1.3.2", + description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", + logo: "unsend.png", + links: { + github: "https://github.com/unsend-dev/unsend", + website: "https://unsend.dev/", + docs: "https://docs.unsend.dev/get-started/", + }, + tags: ["e-mail", "marketing", "business"], + load: () => import("./unsend/index").then((m) => m.generate), + }, + { + id: "langflow", + name: "Langflow", + version: "1.1.1", + description: + "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", + logo: "langflow.svg", + links: { + github: "https://github.com/langflow-ai/langflow/tree/main", + website: "https://www.langflow.org/", + docs: "https://docs.langflow.org/", + }, + tags: ["ai"], + load: () => import("./langflow/index").then((m) => m.generate), + }, + { + id: "elastic-search", + name: "Elasticsearch", + version: "8.10.2", + description: + "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", + logo: "elasticsearch.svg", + links: { + github: "https://github.com/elastic/elasticsearch", + website: "https://www.elastic.co/elasticsearch/", + docs: "https://docs.elastic.co/elasticsearch/", + }, + tags: ["search", "analytics"], + load: () => import("./elastic-search/index").then((m) => m.generate), + }, + { + id: "onedev", + name: "OneDev", + version: "11.6.6", + description: + "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", + logo: "onedev.png", + links: { + github: "https://github.com/theonedev/onedev/", + website: "https://onedev.io/", + docs: "https://docs.onedev.io/", + }, + tags: ["self-hosted", "development"], + load: () => import("./onedev/index").then((m) => m.generate), + }, + { + id: "unifi", + name: "Unifi Network", + version: "11.6.6", + description: + "Unifi Network is an open-source enterprise network management platform for wireless networks.", + logo: "unifi.webp", + links: { + github: "https://github.com/ubiquiti", + website: "https://www.ui.com/", + docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", + }, + tags: ["self-hosted", "networking"], + load: () => import("./unifi/index").then((m) => m.generate), + }, + { + id: "glpi", + name: "GLPI Project", + version: "10.0.16", + description: "The most complete open source service management software", + logo: "glpi.webp", + links: { + github: "https://github.com/glpi-project/glpi", + website: "https://glpi-project.org/", + docs: "https://glpi-project.org/documentation/", + }, + tags: ["self-hosted", "project-management", "management"], + load: () => import("./glpi/index").then((m) => m.generate), + }, + { + id: "checkmate", + name: "Checkmate", + version: "2.0.1", + description: + "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", + logo: "checkmate.png", + links: { + github: "https://github.com/bluewave-labs/checkmate", + website: "https://bluewavelabs.ca", + docs: "https://bluewavelabs.gitbook.io/checkmate", + }, + tags: ["self-hosted", "monitoring", "uptime"], + load: () => import("./checkmate/index").then((m) => m.generate), + }, + { + id: "gotenberg", + name: "Gotenberg", + version: "latest", + description: "Gotenberg is a Docker-powered stateless API for PDF files.", + logo: "gotenberg.png", + links: { + github: "https://github.com/gotenberg/gotenberg", + website: "https://gotenberg.dev", + docs: "https://gotenberg.dev/docs/getting-started/introduction", + }, + tags: ["api", "backend", "pdf", "tools"], + load: () => import("./gotenberg/index").then((m) => m.generate), + }, + { + id: "actualbudget", + name: "Actual Budget", + version: "latest", + description: + "A super fast and privacy-focused app for managing your finances.", + logo: "actualbudget.png", + links: { + github: "https://github.com/actualbudget/actual", + website: "https://actualbudget.org", + docs: "https://actualbudget.org/docs", + }, + tags: ["budgeting", "finance", "money"], + load: () => import("./actualbudget/index").then((m) => m.generate), + }, + { + id: "conduit", + name: "Conduit", + version: "v0.9.0", + description: + "Conduit is a simple, fast and reliable chat server powered by Matrix", + logo: "conduit.svg", + links: { + github: "https://gitlab.com/famedly/conduit", + website: "https://conduit.rs/", + docs: "https://docs.conduit.rs/", + }, + tags: ["matrix", "communication"], + load: () => import("./conduit/index").then((m) => m.generate), + }, + { + id: "evolutionapi", + name: "Evolution API", + version: "v2.1.2", + description: + "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", + logo: "evolutionapi.png", + links: { + github: "https://github.com/EvolutionAPI/evolution-api", + docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", + website: "https://evolution-api.com/opensource-whatsapp-api/", + }, + tags: ["api", "whatsapp", "messaging"], + load: () => import("./evolutionapi/index").then((m) => m.generate), + }, + { + id: "conduwuit", + name: "Conduwuit", + version: "latest", + description: + "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", + logo: "conduwuit.svg", + links: { + github: "https://github.com/girlbossceo/conduwuit", + website: "https://conduwuit.puppyirl.gay", + docs: "https://conduwuit.puppyirl.gay/configuration.html", + }, + tags: ["backend", "chat", "communication", "matrix", "server"], + load: () => import("./conduwuit/index").then((m) => m.generate), + }, + { + id: "cloudflared", + name: "Cloudflared", + version: "latest", + description: + "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", + logo: "cloudflared.svg", + links: { + github: "https://github.com/cloudflare/cloudflared", + website: + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", + docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", + }, + tags: ["cloud", "networking", "security", "tunnel"], + load: () => import("./cloudflared/index").then((m) => m.generate), + }, + { + id: "couchdb", + name: "CouchDB", + version: "latest", + description: + "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", + logo: "couchdb.png", + links: { + github: "https://github.com/apache/couchdb", + website: "https://couchdb.apache.org/", + docs: "https://docs.couchdb.org/en/stable/", + }, + tags: ["database", "storage"], + load: () => import("./couchdb/index").then((m) => m.generate), + }, + { + id: "it-tools", + name: "IT Tools", + version: "latest", + description: "A collection of handy online it-tools for developers.", + logo: "it-tools.svg", + links: { + github: "https://github.com/CorentinTh/it-tools", + website: "https://it-tools.tech", + }, + tags: ["developer", "tools"], + load: () => import("./it-tools/index").then((m) => m.generate), + }, + { + id: "superset", + name: "Superset (Unofficial)", + version: "latest", + description: "Data visualization and data exploration platform.", + logo: "superset.svg", + links: { + github: "https://github.com/amancevice/docker-superset", + website: "https://superset.apache.org", + docs: "https://superset.apache.org/docs/intro", + }, + tags: ["analytics", "bi", "dashboard", "database", "sql"], + load: () => import("./superset/index").then((m) => m.generate), + }, + { + id: "glance", + name: "Glance", + version: "latest", + description: + "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", + logo: "glance.png", + links: { + github: "https://github.com/glanceapp/glance", + docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", + }, + tags: ["dashboard", "monitoring", "widgets", "rss"], + load: () => import("./glance/index").then((m) => m.generate), + }, + { + id: "homarr", + name: "Homarr", + version: "latest", + description: + "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", + logo: "homarr.png", + links: { + github: "https://github.com/homarr-labs/homarr", + docs: "https://homarr.dev/docs/getting-started/installation/docker", + website: "https://homarr.dev/", + }, + tags: ["dashboard", "monitoring"], + load: () => import("./homarr/index").then((m) => m.generate), + }, + { + id: "erpnext", + name: "ERPNext", + version: "version-15", + description: "100% Open Source and highly customizable ERP software.", + logo: "erpnext.svg", + links: { + github: "https://github.com/frappe/erpnext", + docs: "https://docs.frappe.io/erpnext", + website: "https://erpnext.com", + }, + tags: [ + "erp", + "accounts", + "manufacturing", + "retail", + "sales", + "pos", + "hrms", + ], + load: () => import("./erpnext/index").then((m) => m.generate), + }, + { + id: "maybe", + name: "Maybe", + version: "latest", + description: + "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", + logo: "maybe.svg", + links: { + github: "https://github.com/maybe-finance/maybe", + website: "https://maybe.finance/", + docs: "https://docs.maybe.finance/", + }, + tags: ["finance", "self-hosted"], + load: () => import("./maybe/index").then((m) => m.generate), + }, + { + id: "spacedrive", + name: "Spacedrive", + version: "latest", + description: + "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", + links: { + github: "https://github.com/spacedriveapp/spacedrive", + website: "https://spacedrive.com/", + docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", + }, + logo: "spacedrive.png", + tags: ["file-manager", "vdfs", "storage"], + load: () => import("./spacedrive/index").then((m) => m.generate), + }, + { + id: "registry", + name: "Docker Registry", + version: "2", + description: + "Distribution implementation for storing and distributing of Docker container images and artifacts.", + links: { + github: "https://github.com/distribution/distribution", + website: "https://hub.docker.com/_/registry", + docs: "https://distribution.github.io/distribution/", + }, + logo: "registry.png", + tags: ["registry", "docker", "self-hosted"], + load: () => import("./registry/index").then((m) => m.generate), + }, + { + id: "alist", + name: "AList", + version: "v3.41.0", + description: + "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", + logo: "alist.svg", + links: { + github: "https://github.com/AlistGo/alist", + website: "https://alist.nn.ci", + docs: "https://alist.nn.ci/guide/install/docker.html", + }, + tags: ["file", "webdav", "storage"], + load: () => import("./alist/index").then((m) => m.generate), + }, + { + id: "answer", + name: "Answer", + version: "v1.4.1", + description: + "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", + logo: "answer.png", + links: { + github: "https://github.com/apache/answer", + website: "https://answer.apache.org/", + docs: "https://answer.apache.org/docs", + }, + tags: ["q&a", "self-hosted"], + load: () => import("./answer/index").then((m) => m.generate), + }, + { + id: "shlink", + name: "Shlink", + version: "stable", + description: + "URL shortener that can be used to serve shortened URLs under your own domain.", + logo: "shlink.svg", + links: { + github: "https://github.com/shlinkio/shlink", + website: "https://shlink.io", + docs: "https://shlink.io/documentation", + }, + tags: ["sharing", "shortener", "url"], + load: () => import("./shlink/index").then((m) => m.generate), + }, + { + id: "frappe-hr", + name: "Frappe HR", + version: "version-15", + description: + "Feature rich HR & Payroll software. 100% FOSS and customizable.", + logo: "frappe-hr.svg", + links: { + github: "https://github.com/frappe/hrms", + docs: "https://docs.frappe.io/hr", + website: "https://frappe.io/hr", + }, + tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], + load: () => import("./frappe-hr/index").then((m) => m.generate), + }, + { + id: "formbricks", + name: "Formbricks", + version: "v3.1.3", + description: + "Formbricks is an open-source survey and form platform for collecting user data.", + logo: "formbricks.png", + links: { + github: "https://github.com/formbricks/formbricks", + website: "https://formbricks.com/", + docs: "https://formbricks.com/docs", + }, + tags: ["forms", "analytics"], + load: () => import("./formbricks/index").then((m) => m.generate), + }, + { + id: "trilium", + name: "Trilium", + description: + "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", + logo: "trilium.png", + version: "latest", + links: { + github: "https://github.com/zadam/trilium", + website: "https://github.com/zadam/trilium", + docs: "https://github.com/zadam/trilium/wiki/", + }, + tags: ["self-hosted", "productivity", "personal-use"], + load: () => import("./trilium/index").then((m) => m.generate), + }, + { + id: "convex", + name: "Convex", + version: "latest", + description: + "Convex is an open-source reactive database designed to make life easy for web app developers.", + logo: "convex.svg", + links: { + github: "https://github.com/get-convex/convex", + website: "https://www.convex.dev/", + docs: "https://www.convex.dev/docs", + }, + tags: ["backend", "database", "api"], + load: () => import("./convex/index").then((m) => m.generate), + }, ]; From 0b7996addec5dc7966ae042133487be8a44728e8 Mon Sep 17 00:00:00 2001 From: sondreal Date: Sun, 23 Feb 2025 19:13:42 +0100 Subject: [PATCH 103/126] removed my linting --- apps/dokploy/templates/templates.ts | 3114 +++++++++++++-------------- 1 file changed, 1557 insertions(+), 1557 deletions(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index 6d17532d5..b488cbdeb 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1,1562 +1,1562 @@ import type { TemplateData } from "./types/templates-data.type"; export const templates: TemplateData[] = [ - { - id: "appwrite", - name: "Appwrite", - version: "1.6.0", - description: - "Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.\n" + - "Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", - links: { - github: "https://github.com/appwrite/appwrite", - website: "https://appwrite.io/", - docs: "https://appwrite.io/docs", - }, - logo: "appwrite.svg", - tags: ["database", "firebase", "postgres"], - load: () => import("./appwrite/index").then((m) => m.generate), - }, - { - id: "outline", - name: "Outline", - version: "0.82.0", - description: - "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", - links: { - github: "https://github.com/outline/outline", - website: "https://getoutline.com/", - docs: "https://docs.getoutline.com/s/guide", - }, - logo: "outline.png", - load: () => import("./outline/index").then((m) => m.generate), - tags: ["documentation", "knowledge-base", "self-hosted"], - }, - { - id: "supabase", - name: "SupaBase", - version: "1.24.07", - description: - "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", - links: { - github: "https://github.com/supabase/supabase", - website: "https://supabase.com/", - docs: "https://supabase.com/docs/guides/self-hosting", - }, - logo: "supabase.svg", - load: () => import("./supabase/index").then((m) => m.generate), - tags: ["database", "firebase", "postgres"], - }, - { - id: "pocketbase", - name: "Pocketbase", - version: "v0.22.12", - description: - "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", - links: { - github: "https://github.com/pocketbase/pocketbase", - website: "https://pocketbase.io/", - docs: "https://pocketbase.io/docs/", - }, - logo: "pocketbase.svg", - load: () => import("./pocketbase/index").then((m) => m.generate), - tags: ["database", "cms", "headless"], - }, - { - id: "plausible", - name: "Plausible", - version: "v2.1.5", - description: - "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "plausible.svg", - links: { - github: "https://github.com/plausible/plausible", - website: "https://plausible.io/", - docs: "https://plausible.io/docs", - }, - tags: ["analytics"], - load: () => import("./plausible/index").then((m) => m.generate), - }, - { - id: "calcom", - name: "Calcom", - version: "v2.7.6", - description: - "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", + { + id: "appwrite", + name: "Appwrite", + version: "1.6.0", + description: + "Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.\n" + + "Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and more services.", + links: { + github: "https://github.com/appwrite/appwrite", + website: "https://appwrite.io/", + docs: "https://appwrite.io/docs", + }, + logo: "appwrite.svg", + tags: ["database", "firebase", "postgres"], + load: () => import("./appwrite/index").then((m) => m.generate), + }, + { + id: "outline", + name: "Outline", + version: "0.82.0", + description: + "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", + links: { + github: "https://github.com/outline/outline", + website: "https://outline.com/", + docs: "https://docs.outline.com/", + }, + logo: "outline.png", + load: () => import("./outline/index").then((m) => m.generate), + tags: ["documentation", "knowledge-base", "self-hosted"], + }, + { + id: "supabase", + name: "SupaBase", + version: "1.24.07", + description: + "The open source Firebase alternative. Supabase gives you a dedicated Postgres database to build your web, mobile, and AI applications. ", + links: { + github: "https://github.com/supabase/supabase", + website: "https://supabase.com/", + docs: "https://supabase.com/docs/guides/self-hosting", + }, + logo: "supabase.svg", + load: () => import("./supabase/index").then((m) => m.generate), + tags: ["database", "firebase", "postgres"], + }, + { + id: "pocketbase", + name: "Pocketbase", + version: "v0.22.12", + description: + "Pocketbase is a self-hosted alternative to Firebase that allows you to build and host your own backend services.", + links: { + github: "https://github.com/pocketbase/pocketbase", + website: "https://pocketbase.io/", + docs: "https://pocketbase.io/docs/", + }, + logo: "pocketbase.svg", + load: () => import("./pocketbase/index").then((m) => m.generate), + tags: ["database", "cms", "headless"], + }, + { + id: "plausible", + name: "Plausible", + version: "v2.1.5", + description: + "Plausible is a open source, self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "plausible.svg", + links: { + github: "https://github.com/plausible/plausible", + website: "https://plausible.io/", + docs: "https://plausible.io/docs", + }, + tags: ["analytics"], + load: () => import("./plausible/index").then((m) => m.generate), + }, + { + id: "calcom", + name: "Calcom", + version: "v2.7.6", + description: + "Calcom is a open source alternative to Calendly that allows to create scheduling and booking services.", - links: { - github: "https://github.com/calcom/cal.com", - website: "https://cal.com/", - docs: "https://cal.com/docs", - }, - logo: "calcom.jpg", - tags: ["scheduling", "booking"], - load: () => import("./calcom/index").then((m) => m.generate), - }, - { - id: "grafana", - name: "Grafana", - version: "9.5.20", - description: - "Grafana is an open source platform for data visualization and monitoring.", - logo: "grafana.svg", - links: { - github: "https://github.com/grafana/grafana", - website: "https://grafana.com/", - docs: "https://grafana.com/docs/", - }, - tags: ["monitoring"], - load: () => import("./grafana/index").then((m) => m.generate), - }, - { - id: "directus", - name: "Directus", - version: "11.0.2", - description: - "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", - logo: "directus.jpg", - links: { - github: "https://github.com/directus/directus", - website: "https://directus.io/", - docs: "https://docs.directus.io/", - }, - tags: ["cms"], - load: () => import("./directus/index").then((m) => m.generate), - }, - { - id: "baserow", - name: "Baserow", - version: "1.25.2", - description: - "Baserow is an open source database management tool that allows you to create and manage databases.", - logo: "baserow.webp", - links: { - github: "https://github.com/Baserow/baserow", - website: "https://baserow.io/", - docs: "https://baserow.io/docs/index", - }, - tags: ["database"], - load: () => import("./baserow/index").then((m) => m.generate), - }, - { - id: "budibase", - name: "Budibase", - version: "3.2.25", - description: - "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", - logo: "budibase.svg", - links: { - github: "https://github.com/Budibase/budibase", - website: "https://budibase.com/", - docs: "https://docs.budibase.com/docs/", - }, - tags: ["database", "low-code", "nocode", "applications"], - load: () => import("./budibase/index").then((m) => m.generate), - }, - { - id: "ghost", - name: "Ghost", - version: "5.0.0", - description: - "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", - logo: "ghost.jpeg", - links: { - github: "https://github.com/TryGhost/Ghost", - website: "https://ghost.org/", - docs: "https://ghost.org/docs/", - }, - tags: ["cms"], - load: () => import("./ghost/index").then((m) => m.generate), - }, - { - id: "uptime-kuma", - name: "Uptime Kuma", - version: "1.23.15", - description: - "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", - logo: "uptime-kuma.png", - links: { - github: "https://github.com/louislam/uptime-kuma", - website: "https://uptime.kuma.pet/", - docs: "https://github.com/louislam/uptime-kuma/wiki", - }, - tags: ["monitoring"], - load: () => import("./uptime-kuma/index").then((m) => m.generate), - }, - { - id: "n8n", - name: "n8n", - version: "1.70.3", - description: - "n8n is an open source low-code platform for automating workflows and integrations.", - logo: "n8n.png", - links: { - github: "https://github.com/n8n-io/n8n", - website: "https://n8n.io/", - docs: "https://docs.n8n.io/", - }, - tags: ["automation"], - load: () => import("./n8n/index").then((m) => m.generate), - }, - { - id: "wordpress", - name: "Wordpress", - version: "6.7.1", - description: - "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", - logo: "wordpress.png", - links: { - github: "https://github.com/WordPress/WordPress", - website: "https://wordpress.org/", - docs: "https://wordpress.org/documentation/", - }, - tags: ["cms"], - load: () => import("./wordpress/index").then((m) => m.generate), - }, - { - id: "odoo", - name: "Odoo", - version: "16.0", - description: - "Odoo is a free and open source business management software that helps you manage your company's operations.", - logo: "odoo.png", - links: { - github: "https://github.com/odoo/odoo", - website: "https://odoo.com/", - docs: "https://www.odoo.com/documentation/", - }, - tags: ["cms"], - load: () => import("./odoo/index").then((m) => m.generate), - }, - { - id: "appsmith", - name: "Appsmith", - version: "v1.29", - description: - "Appsmith is a free and open source platform for building internal tools and applications.", - logo: "appsmith.png", - links: { - github: "https://github.com/appsmithorg/appsmith", - website: "https://appsmith.com/", - docs: "https://docs.appsmith.com/", - }, - tags: ["cms"], - load: () => import("./appsmith/index").then((m) => m.generate), - }, - { - id: "excalidraw", - name: "Excalidraw", - version: "latest", - description: - "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", - logo: "excalidraw.jpg", - links: { - github: "https://github.com/excalidraw/excalidraw", - website: "https://excalidraw.com/", - docs: "https://docs.excalidraw.com/", - }, - tags: ["drawing"], - load: () => import("./excalidraw/index").then((m) => m.generate), - }, - { - id: "documenso", - name: "Documenso", - version: "v1.5.6", - description: - "Documenso is the open source alternative to DocuSign for signing documents digitally", - links: { - github: "https://github.com/documenso/documenso", - website: "https://documenso.com/", - docs: "https://documenso.com/docs", - }, - logo: "documenso.png", - tags: ["document-signing"], - load: () => import("./documenso/index").then((m) => m.generate), - }, - { - id: "nocodb", - name: "NocoDB", - version: "0.257.2", - description: - "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", + links: { + github: "https://github.com/calcom/cal.com", + website: "https://cal.com/", + docs: "https://cal.com/docs", + }, + logo: "calcom.jpg", + tags: ["scheduling", "booking"], + load: () => import("./calcom/index").then((m) => m.generate), + }, + { + id: "grafana", + name: "Grafana", + version: "9.5.20", + description: + "Grafana is an open source platform for data visualization and monitoring.", + logo: "grafana.svg", + links: { + github: "https://github.com/grafana/grafana", + website: "https://grafana.com/", + docs: "https://grafana.com/docs/", + }, + tags: ["monitoring"], + load: () => import("./grafana/index").then((m) => m.generate), + }, + { + id: "directus", + name: "Directus", + version: "11.0.2", + description: + "Directus is an open source headless CMS that provides an API-first solution for building custom backends.", + logo: "directus.jpg", + links: { + github: "https://github.com/directus/directus", + website: "https://directus.io/", + docs: "https://docs.directus.io/", + }, + tags: ["cms"], + load: () => import("./directus/index").then((m) => m.generate), + }, + { + id: "baserow", + name: "Baserow", + version: "1.25.2", + description: + "Baserow is an open source database management tool that allows you to create and manage databases.", + logo: "baserow.webp", + links: { + github: "https://github.com/Baserow/baserow", + website: "https://baserow.io/", + docs: "https://baserow.io/docs/index", + }, + tags: ["database"], + load: () => import("./baserow/index").then((m) => m.generate), + }, + { + id: "budibase", + name: "Budibase", + version: "3.2.25", + description: + "Budibase is an open-source low-code platform that saves engineers 100s of hours building forms, portals, and approval apps, securely.", + logo: "budibase.svg", + links: { + github: "https://github.com/Budibase/budibase", + website: "https://budibase.com/", + docs: "https://docs.budibase.com/docs/", + }, + tags: ["database", "low-code", "nocode", "applications"], + load: () => import("./budibase/index").then((m) => m.generate), + }, + { + id: "ghost", + name: "Ghost", + version: "5.0.0", + description: + "Ghost is a free and open source, professional publishing platform built on a modern Node.js technology stack.", + logo: "ghost.jpeg", + links: { + github: "https://github.com/TryGhost/Ghost", + website: "https://ghost.org/", + docs: "https://ghost.org/docs/", + }, + tags: ["cms"], + load: () => import("./ghost/index").then((m) => m.generate), + }, + { + id: "uptime-kuma", + name: "Uptime Kuma", + version: "1.23.15", + description: + "Uptime Kuma is a free and open source monitoring tool that allows you to monitor your websites and applications.", + logo: "uptime-kuma.png", + links: { + github: "https://github.com/louislam/uptime-kuma", + website: "https://uptime.kuma.pet/", + docs: "https://github.com/louislam/uptime-kuma/wiki", + }, + tags: ["monitoring"], + load: () => import("./uptime-kuma/index").then((m) => m.generate), + }, + { + id: "n8n", + name: "n8n", + version: "1.70.3", + description: + "n8n is an open source low-code platform for automating workflows and integrations.", + logo: "n8n.png", + links: { + github: "https://github.com/n8n-io/n8n", + website: "https://n8n.io/", + docs: "https://docs.n8n.io/", + }, + tags: ["automation"], + load: () => import("./n8n/index").then((m) => m.generate), + }, + { + id: "wordpress", + name: "Wordpress", + version: "6.7.1", + description: + "Wordpress is a free and open source content management system (CMS) for publishing and managing websites.", + logo: "wordpress.png", + links: { + github: "https://github.com/WordPress/WordPress", + website: "https://wordpress.org/", + docs: "https://wordpress.org/documentation/", + }, + tags: ["cms"], + load: () => import("./wordpress/index").then((m) => m.generate), + }, + { + id: "odoo", + name: "Odoo", + version: "16.0", + description: + "Odoo is a free and open source business management software that helps you manage your company's operations.", + logo: "odoo.png", + links: { + github: "https://github.com/odoo/odoo", + website: "https://odoo.com/", + docs: "https://www.odoo.com/documentation/", + }, + tags: ["cms"], + load: () => import("./odoo/index").then((m) => m.generate), + }, + { + id: "appsmith", + name: "Appsmith", + version: "v1.29", + description: + "Appsmith is a free and open source platform for building internal tools and applications.", + logo: "appsmith.png", + links: { + github: "https://github.com/appsmithorg/appsmith", + website: "https://appsmith.com/", + docs: "https://docs.appsmith.com/", + }, + tags: ["cms"], + load: () => import("./appsmith/index").then((m) => m.generate), + }, + { + id: "excalidraw", + name: "Excalidraw", + version: "latest", + description: + "Excalidraw is a free and open source online diagramming tool that lets you easily create and share beautiful diagrams.", + logo: "excalidraw.jpg", + links: { + github: "https://github.com/excalidraw/excalidraw", + website: "https://excalidraw.com/", + docs: "https://docs.excalidraw.com/", + }, + tags: ["drawing"], + load: () => import("./excalidraw/index").then((m) => m.generate), + }, + { + id: "documenso", + name: "Documenso", + version: "v1.5.6", + description: + "Documenso is the open source alternative to DocuSign for signing documents digitally", + links: { + github: "https://github.com/documenso/documenso", + website: "https://documenso.com/", + docs: "https://documenso.com/docs", + }, + logo: "documenso.png", + tags: ["document-signing"], + load: () => import("./documenso/index").then((m) => m.generate), + }, + { + id: "nocodb", + name: "NocoDB", + version: "0.257.2", + description: + "NocoDB is an opensource Airtable alternative that turns any MySQL, PostgreSQL, SQL Server, SQLite & MariaDB into a smart spreadsheet.", - links: { - github: "https://github.com/nocodb/nocodb", - website: "https://nocodb.com/", - docs: "https://docs.nocodb.com/", - }, - logo: "nocodb.png", - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./nocodb/index").then((m) => m.generate), - }, - { - id: "meilisearch", - name: "Meilisearch", - version: "v1.8.3", - description: - "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", - logo: "meilisearch.png", - links: { - github: "https://github.com/meilisearch/meilisearch", - website: "https://www.meilisearch.com/", - docs: "https://docs.meilisearch.com/", - }, - tags: ["search"], - load: () => import("./meilisearch/index").then((m) => m.generate), - }, - { - id: "phpmyadmin", - name: "Phpmyadmin", - version: "5.2.1", - description: - "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", - logo: "phpmyadmin.png", - links: { - github: "https://github.com/phpmyadmin/phpmyadmin", - website: "https://www.phpmyadmin.net/", - docs: "https://www.phpmyadmin.net/docs/", - }, - tags: ["database"], - load: () => import("./phpmyadmin/index").then((m) => m.generate), - }, - { - id: "rocketchat", - name: "Rocketchat", - version: "6.9.2", - description: - "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", - logo: "rocketchat.png", - links: { - github: "https://github.com/RocketChat/Rocket.Chat", - website: "https://rocket.chat/", - docs: "https://rocket.chat/docs/", - }, - tags: ["chat"], - load: () => import("./rocketchat/index").then((m) => m.generate), - }, - { - id: "minio", - name: "Minio", - description: - "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", - logo: "minio.png", - version: "latest", - links: { - github: "https://github.com/minio/minio", - website: "https://minio.io/", - docs: "https://docs.minio.io/", - }, - tags: ["storage"], - load: () => import("./minio/index").then((m) => m.generate), - }, - { - id: "metabase", - name: "Metabase", - version: "v0.50.8", - description: - "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", - logo: "metabase.png", - links: { - github: "https://github.com/metabase/metabase", - website: "https://www.metabase.com/", - docs: "https://www.metabase.com/docs/", - }, - tags: ["database", "dashboard"], - load: () => import("./metabase/index").then((m) => m.generate), - }, - { - id: "glitchtip", - name: "Glitchtip", - version: "v4.0", - description: "Glitchtip is simple, open source error tracking", - logo: "glitchtip.png", - links: { - github: "https://gitlab.com/glitchtip/", - website: "https://glitchtip.com/", - docs: "https://glitchtip.com/documentation", - }, - tags: ["hosting"], - load: () => import("./glitchtip/index").then((m) => m.generate), - }, - { - id: "open-webui", - name: "Open WebUI", - version: "v0.3.7", - description: - "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", - logo: "open-webui.png", - links: { - github: "https://github.com/open-webui/open-webui", - website: "https://openwebui.com/", - docs: "https://docs.openwebui.com/", - }, - tags: ["chat"], - load: () => import("./open-webui/index").then((m) => m.generate), - }, - { - id: "listmonk", - name: "Listmonk", - version: "v3.0.0", - description: - "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", - logo: "listmonk.png", - links: { - github: "https://github.com/knadh/listmonk", - website: "https://listmonk.app/", - docs: "https://listmonk.app/docs/", - }, - tags: ["email", "newsletter", "mailing-list"], - load: () => import("./listmonk/index").then((m) => m.generate), - }, - { - id: "doublezero", - name: "Double Zero", - version: "v0.2.1", - description: - "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", - logo: "doublezero.svg", - links: { - github: "https://github.com/technomancy-dev/00", - website: "https://www.double-zero.cloud/", - docs: "https://github.com/technomancy-dev/00", - }, - tags: ["email"], - load: () => import("./doublezero/index").then((m) => m.generate), - }, - { - id: "umami", - name: "Umami", - version: "v2.14.0", - description: - "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", - logo: "umami.png", - links: { - github: "https://github.com/umami-software/umami", - website: "https://umami.is", - docs: "https://umami.is/docs", - }, - tags: ["analytics"], - load: () => import("./umami/index").then((m) => m.generate), - }, - { - id: "jellyfin", - name: "jellyfin", - version: "v10.9.7", - description: - "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", - logo: "jellyfin.svg", - links: { - github: "https://github.com/jellyfin/jellyfin", - website: "https://jellyfin.org/", - docs: "https://jellyfin.org/docs/", - }, - tags: ["media system"], - load: () => import("./jellyfin/index").then((m) => m.generate), - }, - { - id: "teable", - name: "teable", - version: "v1.3.1-alpha-build.460", - description: - "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", - logo: "teable.png", - links: { - github: "https://github.com/teableio/teable", - website: "https://teable.io/", - docs: "https://help.teable.io/", - }, - tags: ["database", "spreadsheet", "low-code", "nocode"], - load: () => import("./teable/index").then((m) => m.generate), - }, - { - id: "zipline", - name: "Zipline", - version: "v3.7.9", - description: - "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", - logo: "zipline.png", - links: { - github: "https://github.com/diced/zipline", - website: "https://zipline.diced.sh/", - docs: "https://zipline.diced.sh/docs/", - }, - tags: ["media system", "storage"], - load: () => import("./zipline/index").then((m) => m.generate), - }, - { - id: "soketi", - name: "Soketi", - version: "v1.6.1-16", - description: - "Soketi is your simple, fast, and resilient open-source WebSockets server.", - logo: "soketi.png", - links: { - github: "https://github.com/soketi/soketi", - website: "https://soketi.app/", - docs: "https://docs.soketi.app/", - }, - tags: ["chat"], - load: () => import("./soketi/index").then((m) => m.generate), - }, - { - id: "aptabase", - name: "Aptabase", - version: "v1.0.0", - description: - "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", - logo: "aptabase.svg", - links: { - github: "https://github.com/aptabase/aptabase", - website: "https://aptabase.com/", - docs: "https://github.com/aptabase/aptabase/blob/main/README.md", - }, - tags: ["analytics", "self-hosted"], - load: () => import("./aptabase/index").then((m) => m.generate), - }, - { - id: "typebot", - name: "Typebot", - version: "2.27.0", - description: "Typebot is an open-source chatbot builder platform.", - logo: "typebot.svg", - links: { - github: "https://github.com/baptisteArno/typebot.io", - website: "https://typebot.io/", - docs: "https://docs.typebot.io/get-started/introduction", - }, - tags: ["chatbot", "builder", "open-source"], - load: () => import("./typebot/index").then((m) => m.generate), - }, - { - id: "gitea", - name: "Gitea", - version: "1.22.3", - description: - "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", - logo: "gitea.png", - links: { - github: "https://github.com/go-gitea/gitea.git", - website: "https://gitea.com/", - docs: "https://docs.gitea.com/installation/install-with-docker", - }, - tags: ["self-hosted", "storage"], - load: () => import("./gitea/index").then((m) => m.generate), - }, - { - id: "roundcube", - name: "Roundcube", - version: "1.6.9", - description: - "Free and open source webmail software for the masses, written in PHP.", - logo: "roundcube.svg", - links: { - github: "https://github.com/roundcube/roundcubemail", - website: "https://roundcube.net/", - docs: "https://roundcube.net/about/", - }, - tags: ["self-hosted", "email", "webmail"], - load: () => import("./roundcube/index").then((m) => m.generate), - }, - { - id: "filebrowser", - name: "File Browser", - version: "2.31.2", - description: - "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", - logo: "filebrowser.svg", - links: { - github: "https://github.com/filebrowser/filebrowser", - website: "https://filebrowser.org/", - docs: "https://filebrowser.org/", - }, - tags: ["file-manager", "storage"], - load: () => import("./filebrowser/index").then((m) => m.generate), - }, - { - id: "tolgee", - name: "Tolgee", - version: "v3.80.4", - description: - "Developer & translator friendly web-based localization platform", - logo: "tolgee.svg", - links: { - github: "https://github.com/tolgee/tolgee-platform", - website: "https://tolgee.io", - docs: "https://tolgee.io/platform", - }, - tags: ["self-hosted", "i18n", "localization", "translations"], - load: () => import("./tolgee/index").then((m) => m.generate), - }, - { - id: "portainer", - name: "Portainer", - version: "2.21.4", - description: - "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", - logo: "portainer.svg", - links: { - github: "https://github.com/portainer/portainer", - website: "https://www.portainer.io/", - docs: "https://docs.portainer.io/", - }, - tags: ["cloud", "monitoring"], - load: () => import("./portainer/index").then((m) => m.generate), - }, - { - id: "influxdb", - name: "InfluxDB", - version: "2.7.10", - description: - "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", - logo: "influxdb.png", - links: { - github: "https://github.com/influxdata/influxdb", - website: "https://www.influxdata.com/", - docs: "https://docs.influxdata.com/influxdb/v2/", - }, - tags: ["self-hosted", "open-source", "storage", "database"], - load: () => import("./influxdb/index").then((m) => m.generate), - }, - { - id: "infisical", - name: "Infisical", - version: "0.90.1", - description: - "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", - logo: "infisical.jpg", - links: { - github: "https://github.com/Infisical/infisical", - website: "https://infisical.com/", - docs: "https://infisical.com/docs/documentation/getting-started/introduction", - }, - tags: ["self-hosted", "open-source"], - load: () => import("./infisical/index").then((m) => m.generate), - }, - { - id: "docmost", - name: "Docmost", - version: "0.4.1", - description: - "Docmost, is an open-source collaborative wiki and documentation software.", - logo: "docmost.png", - links: { - github: "https://github.com/docmost/docmost", - website: "https://docmost.com/", - docs: "https://docmost.com/docs/", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./docmost/index").then((m) => m.generate), - }, - { - id: "vaultwarden", - name: "Vaultwarden", - version: "1.32.7", - description: - "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", - logo: "vaultwarden.svg", - links: { - github: "https://github.com/dani-garcia/vaultwarden", - website: "", - docs: "https://github.com/dani-garcia/vaultwarden/wiki", - }, - tags: ["open-source"], - load: () => import("./vaultwarden/index").then((m) => m.generate), - }, - { - id: "hi-events", - name: "Hi.events", - version: "0.8.0-beta.1", - description: - "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", - logo: "hi-events.svg", - links: { - github: "https://github.com/HiEventsDev/hi.events", - website: "https://hi.events/", - docs: "https://hi.events/docs", - }, - tags: ["self-hosted", "open-source", "manager"], - load: () => import("./hi-events/index").then((m) => m.generate), - }, - { - id: "windows", - name: "Windows (dockerized)", - version: "4.00", - description: "Windows inside a Docker container.", - logo: "windows.png", - links: { - github: "https://github.com/dockur/windows", - website: "", - docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./windows/index").then((m) => m.generate), - }, - { - id: "macos", - name: "MacOS (dockerized)", - version: "1.14", - description: "MacOS inside a Docker container.", - logo: "macos.png", - links: { - github: "https://github.com/dockur/macos", - website: "", - docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", - }, - tags: ["self-hosted", "open-source", "os"], - load: () => import("./macos/index").then((m) => m.generate), - }, - { - id: "coder", - name: "Coder", - version: "2.15.3", - description: - "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", - logo: "coder.svg", - links: { - github: "https://github.com/coder/coder", - website: "https://coder.com/", - docs: "https://coder.com/docs", - }, - tags: ["self-hosted", "open-source", "builder"], - load: () => import("./coder/index").then((m) => m.generate), - }, - { - id: "stirling", - name: "Stirling PDF", - version: "0.30.1", - description: "A locally hosted one-stop shop for all your PDF needs", - logo: "stirling.svg", - links: { - github: "https://github.com/Stirling-Tools/Stirling-PDF", - website: "https://www.stirlingpdf.com/", - docs: "https://docs.stirlingpdf.com/", - }, - tags: ["pdf", "tools"], - load: () => import("./stirling/index").then((m) => m.generate), - }, - { - id: "lobe-chat", - name: "Lobe Chat", - version: "v1.26.1", - description: "Lobe Chat - an open-source, modern-design AI chat framework.", - logo: "lobe-chat.png", - links: { - github: "https://github.com/lobehub/lobe-chat", - website: "https://chat-preview.lobehub.com/", - docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", - }, - tags: ["IA", "chat"], - load: () => import("./lobe-chat/index").then((m) => m.generate), - }, - { - id: "peppermint", - name: "Peppermint", - version: "latest", - description: - "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", - logo: "peppermint.svg", - links: { - github: "https://github.com/Peppermint-Lab/peppermint", - website: "https://peppermint.sh/", - docs: "https://docs.peppermint.sh/", - }, - tags: ["api", "development", "documentation"], - load: () => import("./peppermint/index").then((m) => m.generate), - }, - { - id: "windmill", - name: "Windmill", - version: "latest", - description: - "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", - logo: "windmill.svg", - links: { - github: "https://github.com/windmill-labs/windmill", - website: "https://www.windmill.dev/", - docs: "https://docs.windmill.dev/", - }, - tags: ["workflow", "automation", "development"], - load: () => import("./windmill/index").then((m) => m.generate), - }, - { - id: "activepieces", - name: "Activepieces", - version: "0.35.0", - description: - "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", - logo: "activepieces.svg", - links: { - github: "https://github.com/activepieces/activepieces", - website: "https://www.activepieces.com/", - docs: "https://www.activepieces.com/docs", - }, - tags: ["automation", "workflow", "no-code"], - load: () => import("./activepieces/index").then((m) => m.generate), - }, - { - id: "invoiceshelf", - name: "InvoiceShelf", - version: "latest", - description: - "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", - logo: "invoiceshelf.png", - links: { - github: "https://github.com/InvoiceShelf/invoiceshelf", - website: "https://invoiceshelf.com", - docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./invoiceshelf/index").then((m) => m.generate), - }, - { - id: "postiz", - name: "Postiz", - version: "latest", - description: - "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", - logo: "postiz.png", - links: { - github: "https://github.com/gitroomhq/postiz", - website: "https://postiz.com", - docs: "https://docs.postiz.com", - }, - tags: ["cms", "content-management", "publishing"], - load: () => import("./postiz/index").then((m) => m.generate), - }, - { - id: "slash", - name: "Slash", - version: "latest", - description: - "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", - logo: "slash.png", - links: { - github: "https://github.com/yourselfhosted/slash", - website: "https://github.com/yourselfhosted/slash#readme", - docs: "https://github.com/yourselfhosted/slash/wiki", - }, - tags: ["bookmarks", "link-shortener", "self-hosted"], - load: () => import("./slash/index").then((m) => m.generate), - }, - { - id: "discord-tickets", - name: "Discord Tickets", - version: "4.0.21", - description: - "An open-source Discord bot for creating and managing support ticket channels.", - logo: "discord-tickets.png", - links: { - github: "https://github.com/discord-tickets/bot", - website: "https://discordtickets.app", - docs: "https://discordtickets.app/self-hosting/installation/docker/", - }, - tags: ["discord", "tickets", "support"], - load: () => import("./discord-tickets/index").then((m) => m.generate), - }, - { - id: "nextcloud-aio", - name: "Nextcloud All in One", - version: "30.0.2", - description: - "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", - logo: "nextcloud-aio.svg", - links: { - github: "https://github.com/nextcloud/docker", - website: "https://nextcloud.com/", - docs: "https://docs.nextcloud.com/", - }, - tags: ["file-manager", "sync"], - load: () => import("./nextcloud-aio/index").then((m) => m.generate), - }, - { - id: "blender", - name: "Blender", - version: "latest", - description: - "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", - logo: "blender.svg", - links: { - github: "https://github.com/linuxserver/docker-blender", - website: "https://www.blender.org/", - docs: "https://docs.blender.org/", - }, - tags: ["3d", "rendering", "animation"], - load: () => import("./blender/index").then((m) => m.generate), - }, - { - id: "heyform", - name: "HeyForm", - version: "latest", - description: - "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", - logo: "heyform.svg", - links: { - github: "https://github.com/heyform/heyform", - website: "https://heyform.net", - docs: "https://docs.heyform.net", - }, - tags: ["form", "builder", "questionnaire", "quiz", "survey"], - load: () => import("./heyform/index").then((m) => m.generate), - }, - { - id: "chatwoot", - name: "Chatwoot", - version: "v3.14.1", - description: - "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", - logo: "chatwoot.svg", - links: { - github: "https://github.com/chatwoot/chatwoot", - website: "https://www.chatwoot.com", - docs: "https://www.chatwoot.com/docs", - }, - tags: ["support", "chat", "customer-service"], - load: () => import("./chatwoot/index").then((m) => m.generate), - }, - { - id: "discourse", - name: "Discourse", - version: "3.3.2", - description: - "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", - logo: "discourse.svg", - links: { - github: "https://github.com/discourse/discourse", - website: "https://www.discourse.org/", - docs: "https://meta.discourse.org/", - }, - tags: ["forum", "community", "discussion"], - load: () => import("./discourse/index").then((m) => m.generate), - }, - { - id: "immich", - name: "Immich", - version: "v1.121.0", - description: - "High performance self-hosted photo and video backup solution directly from your mobile phone.", - logo: "immich.svg", - links: { - github: "https://github.com/immich-app/immich", - website: "https://immich.app/", - docs: "https://immich.app/docs/overview/introduction", - }, - tags: ["photos", "videos", "backup", "media"], - load: () => import("./immich/index").then((m) => m.generate), - }, - { - id: "twenty", - name: "Twenty CRM", - version: "latest", - description: - "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", - logo: "twenty.svg", - links: { - github: "https://github.com/twentyhq/twenty", - website: "https://twenty.com", - docs: "https://docs.twenty.com", - }, - tags: ["crm", "sales", "business"], - load: () => import("./twenty/index").then((m) => m.generate), - }, - { - id: "yourls", - name: "YOURLS", - version: "1.9.2", - description: - "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", - logo: "yourls.svg", - links: { - github: "https://github.com/YOURLS/YOURLS", - website: "https://yourls.org/", - docs: "https://yourls.org/#documentation", - }, - tags: ["url-shortener", "php"], - load: () => import("./yourls/index").then((m) => m.generate), - }, - { - id: "ryot", - name: "Ryot", - version: "v7.10", - description: - "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", - logo: "ryot.png", - links: { - github: "https://github.com/IgnisDa/ryot", - website: "https://ryot.io/", - docs: "https://docs.ryot.io/", - }, - tags: ["media", "tracking", "self-hosted"], - load: () => import("./ryot/index").then((m) => m.generate), - }, - { - id: "photoprism", - name: "Photoprism", - version: "latest", - description: - "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", - logo: "photoprism.svg", - links: { - github: "https://github.com/photoprism/photoprism", - website: "https://www.photoprism.app/", - docs: "https://docs.photoprism.app/", - }, - tags: ["media", "photos", "self-hosted"], - load: () => import("./photoprism/index").then((m) => m.generate), - }, - { - id: "ontime", - name: "Ontime", - version: "v3.8.0", - description: - "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", - logo: "ontime.png", - links: { - github: "https://github.com/cpvalente/ontime/", - website: "https://getontime.no", - docs: "https://docs.getontime.no", - }, - tags: ["event"], - load: () => import("./ontime/index").then((m) => m.generate), - }, - { - id: "triggerdotdev", - name: "Trigger.dev", - version: "v3", - description: - "Trigger is a platform for building event-driven applications.", - logo: "triggerdotdev.svg", - links: { - github: "https://github.com/triggerdotdev/trigger.dev", - website: "https://trigger.dev/", - docs: "https://trigger.dev/docs", - }, - tags: ["event-driven", "applications"], - load: () => import("./triggerdotdev/index").then((m) => m.generate), - }, - { - id: "browserless", - name: "Browserless", - version: "2.23.0", - description: - "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", - logo: "browserless.svg", - links: { - github: "https://github.com/browserless/browserless", - website: "https://www.browserless.io/", - docs: "https://docs.browserless.io/", - }, - tags: ["browser", "automation"], - load: () => import("./browserless/index").then((m) => m.generate), - }, - { - id: "drawio", - name: "draw.io", - version: "24.7.17", - description: - "draw.io is a configurable diagramming/whiteboarding visualization application.", - logo: "drawio.svg", - links: { - github: "https://github.com/jgraph/drawio", - website: "https://draw.io/", - docs: "https://www.drawio.com/doc/", - }, - tags: ["drawing", "diagrams"], - load: () => import("./drawio/index").then((m) => m.generate), - }, - { - id: "kimai", - name: "Kimai", - version: "2.26.0", - description: - "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", - logo: "kimai.svg", - links: { - github: "https://github.com/kimai/kimai", - website: "https://www.kimai.org", - docs: "https://www.kimai.org/documentation", - }, - tags: ["invoice", "business", "finance"], - load: () => import("./kimai/index").then((m) => m.generate), - }, - { - id: "logto", - name: "Logto", - version: "1.22.0", - description: - "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", - logo: "logto.png", - links: { - github: "https://github.com/logto-io/logto", - website: "https://logto.io/", - docs: "https://docs.logto.io/introduction", - }, - tags: ["identity", "auth"], - load: () => import("./logto/index").then((m) => m.generate), - }, - { - id: "penpot", - name: "Penpot", - version: "2.3.2", - description: - "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", - logo: "penpot.svg", - links: { - github: "https://github.com/penpot/penpot", - website: "https://penpot.app/", - docs: "https://docs.penpot.app/", - }, - tags: ["design", "collaboration"], - load: () => import("./penpot/index").then((m) => m.generate), - }, - { - id: "huly", - name: "Huly", - version: "0.6.377", - description: - "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", - logo: "huly.svg", - links: { - github: "https://github.com/hcengineering/huly-selfhost", - website: "https://huly.io/", - docs: "https://docs.huly.io/", - }, - tags: ["project-management", "community", "discussion"], - load: () => import("./huly/index").then((m) => m.generate), - }, - { - id: "unsend", - name: "Unsend", - version: "v1.3.2", - description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", - logo: "unsend.png", - links: { - github: "https://github.com/unsend-dev/unsend", - website: "https://unsend.dev/", - docs: "https://docs.unsend.dev/get-started/", - }, - tags: ["e-mail", "marketing", "business"], - load: () => import("./unsend/index").then((m) => m.generate), - }, - { - id: "langflow", - name: "Langflow", - version: "1.1.1", - description: - "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", - logo: "langflow.svg", - links: { - github: "https://github.com/langflow-ai/langflow/tree/main", - website: "https://www.langflow.org/", - docs: "https://docs.langflow.org/", - }, - tags: ["ai"], - load: () => import("./langflow/index").then((m) => m.generate), - }, - { - id: "elastic-search", - name: "Elasticsearch", - version: "8.10.2", - description: - "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", - logo: "elasticsearch.svg", - links: { - github: "https://github.com/elastic/elasticsearch", - website: "https://www.elastic.co/elasticsearch/", - docs: "https://docs.elastic.co/elasticsearch/", - }, - tags: ["search", "analytics"], - load: () => import("./elastic-search/index").then((m) => m.generate), - }, - { - id: "onedev", - name: "OneDev", - version: "11.6.6", - description: - "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", - logo: "onedev.png", - links: { - github: "https://github.com/theonedev/onedev/", - website: "https://onedev.io/", - docs: "https://docs.onedev.io/", - }, - tags: ["self-hosted", "development"], - load: () => import("./onedev/index").then((m) => m.generate), - }, - { - id: "unifi", - name: "Unifi Network", - version: "11.6.6", - description: - "Unifi Network is an open-source enterprise network management platform for wireless networks.", - logo: "unifi.webp", - links: { - github: "https://github.com/ubiquiti", - website: "https://www.ui.com/", - docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", - }, - tags: ["self-hosted", "networking"], - load: () => import("./unifi/index").then((m) => m.generate), - }, - { - id: "glpi", - name: "GLPI Project", - version: "10.0.16", - description: "The most complete open source service management software", - logo: "glpi.webp", - links: { - github: "https://github.com/glpi-project/glpi", - website: "https://glpi-project.org/", - docs: "https://glpi-project.org/documentation/", - }, - tags: ["self-hosted", "project-management", "management"], - load: () => import("./glpi/index").then((m) => m.generate), - }, - { - id: "checkmate", - name: "Checkmate", - version: "2.0.1", - description: - "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", - logo: "checkmate.png", - links: { - github: "https://github.com/bluewave-labs/checkmate", - website: "https://bluewavelabs.ca", - docs: "https://bluewavelabs.gitbook.io/checkmate", - }, - tags: ["self-hosted", "monitoring", "uptime"], - load: () => import("./checkmate/index").then((m) => m.generate), - }, - { - id: "gotenberg", - name: "Gotenberg", - version: "latest", - description: "Gotenberg is a Docker-powered stateless API for PDF files.", - logo: "gotenberg.png", - links: { - github: "https://github.com/gotenberg/gotenberg", - website: "https://gotenberg.dev", - docs: "https://gotenberg.dev/docs/getting-started/introduction", - }, - tags: ["api", "backend", "pdf", "tools"], - load: () => import("./gotenberg/index").then((m) => m.generate), - }, - { - id: "actualbudget", - name: "Actual Budget", - version: "latest", - description: - "A super fast and privacy-focused app for managing your finances.", - logo: "actualbudget.png", - links: { - github: "https://github.com/actualbudget/actual", - website: "https://actualbudget.org", - docs: "https://actualbudget.org/docs", - }, - tags: ["budgeting", "finance", "money"], - load: () => import("./actualbudget/index").then((m) => m.generate), - }, - { - id: "conduit", - name: "Conduit", - version: "v0.9.0", - description: - "Conduit is a simple, fast and reliable chat server powered by Matrix", - logo: "conduit.svg", - links: { - github: "https://gitlab.com/famedly/conduit", - website: "https://conduit.rs/", - docs: "https://docs.conduit.rs/", - }, - tags: ["matrix", "communication"], - load: () => import("./conduit/index").then((m) => m.generate), - }, - { - id: "evolutionapi", - name: "Evolution API", - version: "v2.1.2", - description: - "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", - logo: "evolutionapi.png", - links: { - github: "https://github.com/EvolutionAPI/evolution-api", - docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", - website: "https://evolution-api.com/opensource-whatsapp-api/", - }, - tags: ["api", "whatsapp", "messaging"], - load: () => import("./evolutionapi/index").then((m) => m.generate), - }, - { - id: "conduwuit", - name: "Conduwuit", - version: "latest", - description: - "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", - logo: "conduwuit.svg", - links: { - github: "https://github.com/girlbossceo/conduwuit", - website: "https://conduwuit.puppyirl.gay", - docs: "https://conduwuit.puppyirl.gay/configuration.html", - }, - tags: ["backend", "chat", "communication", "matrix", "server"], - load: () => import("./conduwuit/index").then((m) => m.generate), - }, - { - id: "cloudflared", - name: "Cloudflared", - version: "latest", - description: - "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", - logo: "cloudflared.svg", - links: { - github: "https://github.com/cloudflare/cloudflared", - website: - "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", - docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", - }, - tags: ["cloud", "networking", "security", "tunnel"], - load: () => import("./cloudflared/index").then((m) => m.generate), - }, - { - id: "couchdb", - name: "CouchDB", - version: "latest", - description: - "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", - logo: "couchdb.png", - links: { - github: "https://github.com/apache/couchdb", - website: "https://couchdb.apache.org/", - docs: "https://docs.couchdb.org/en/stable/", - }, - tags: ["database", "storage"], - load: () => import("./couchdb/index").then((m) => m.generate), - }, - { - id: "it-tools", - name: "IT Tools", - version: "latest", - description: "A collection of handy online it-tools for developers.", - logo: "it-tools.svg", - links: { - github: "https://github.com/CorentinTh/it-tools", - website: "https://it-tools.tech", - }, - tags: ["developer", "tools"], - load: () => import("./it-tools/index").then((m) => m.generate), - }, - { - id: "superset", - name: "Superset (Unofficial)", - version: "latest", - description: "Data visualization and data exploration platform.", - logo: "superset.svg", - links: { - github: "https://github.com/amancevice/docker-superset", - website: "https://superset.apache.org", - docs: "https://superset.apache.org/docs/intro", - }, - tags: ["analytics", "bi", "dashboard", "database", "sql"], - load: () => import("./superset/index").then((m) => m.generate), - }, - { - id: "glance", - name: "Glance", - version: "latest", - description: - "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", - logo: "glance.png", - links: { - github: "https://github.com/glanceapp/glance", - docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", - }, - tags: ["dashboard", "monitoring", "widgets", "rss"], - load: () => import("./glance/index").then((m) => m.generate), - }, - { - id: "homarr", - name: "Homarr", - version: "latest", - description: - "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", - logo: "homarr.png", - links: { - github: "https://github.com/homarr-labs/homarr", - docs: "https://homarr.dev/docs/getting-started/installation/docker", - website: "https://homarr.dev/", - }, - tags: ["dashboard", "monitoring"], - load: () => import("./homarr/index").then((m) => m.generate), - }, - { - id: "erpnext", - name: "ERPNext", - version: "version-15", - description: "100% Open Source and highly customizable ERP software.", - logo: "erpnext.svg", - links: { - github: "https://github.com/frappe/erpnext", - docs: "https://docs.frappe.io/erpnext", - website: "https://erpnext.com", - }, - tags: [ - "erp", - "accounts", - "manufacturing", - "retail", - "sales", - "pos", - "hrms", - ], - load: () => import("./erpnext/index").then((m) => m.generate), - }, - { - id: "maybe", - name: "Maybe", - version: "latest", - description: - "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", - logo: "maybe.svg", - links: { - github: "https://github.com/maybe-finance/maybe", - website: "https://maybe.finance/", - docs: "https://docs.maybe.finance/", - }, - tags: ["finance", "self-hosted"], - load: () => import("./maybe/index").then((m) => m.generate), - }, - { - id: "spacedrive", - name: "Spacedrive", - version: "latest", - description: - "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", - links: { - github: "https://github.com/spacedriveapp/spacedrive", - website: "https://spacedrive.com/", - docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", - }, - logo: "spacedrive.png", - tags: ["file-manager", "vdfs", "storage"], - load: () => import("./spacedrive/index").then((m) => m.generate), - }, - { - id: "registry", - name: "Docker Registry", - version: "2", - description: - "Distribution implementation for storing and distributing of Docker container images and artifacts.", - links: { - github: "https://github.com/distribution/distribution", - website: "https://hub.docker.com/_/registry", - docs: "https://distribution.github.io/distribution/", - }, - logo: "registry.png", - tags: ["registry", "docker", "self-hosted"], - load: () => import("./registry/index").then((m) => m.generate), - }, - { - id: "alist", - name: "AList", - version: "v3.41.0", - description: - "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", - logo: "alist.svg", - links: { - github: "https://github.com/AlistGo/alist", - website: "https://alist.nn.ci", - docs: "https://alist.nn.ci/guide/install/docker.html", - }, - tags: ["file", "webdav", "storage"], - load: () => import("./alist/index").then((m) => m.generate), - }, - { - id: "answer", - name: "Answer", - version: "v1.4.1", - description: - "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", - logo: "answer.png", - links: { - github: "https://github.com/apache/answer", - website: "https://answer.apache.org/", - docs: "https://answer.apache.org/docs", - }, - tags: ["q&a", "self-hosted"], - load: () => import("./answer/index").then((m) => m.generate), - }, - { - id: "shlink", - name: "Shlink", - version: "stable", - description: - "URL shortener that can be used to serve shortened URLs under your own domain.", - logo: "shlink.svg", - links: { - github: "https://github.com/shlinkio/shlink", - website: "https://shlink.io", - docs: "https://shlink.io/documentation", - }, - tags: ["sharing", "shortener", "url"], - load: () => import("./shlink/index").then((m) => m.generate), - }, - { - id: "frappe-hr", - name: "Frappe HR", - version: "version-15", - description: - "Feature rich HR & Payroll software. 100% FOSS and customizable.", - logo: "frappe-hr.svg", - links: { - github: "https://github.com/frappe/hrms", - docs: "https://docs.frappe.io/hr", - website: "https://frappe.io/hr", - }, - tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], - load: () => import("./frappe-hr/index").then((m) => m.generate), - }, - { - id: "formbricks", - name: "Formbricks", - version: "v3.1.3", - description: - "Formbricks is an open-source survey and form platform for collecting user data.", - logo: "formbricks.png", - links: { - github: "https://github.com/formbricks/formbricks", - website: "https://formbricks.com/", - docs: "https://formbricks.com/docs", - }, - tags: ["forms", "analytics"], - load: () => import("./formbricks/index").then((m) => m.generate), - }, - { - id: "trilium", - name: "Trilium", - description: - "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", - logo: "trilium.png", - version: "latest", - links: { - github: "https://github.com/zadam/trilium", - website: "https://github.com/zadam/trilium", - docs: "https://github.com/zadam/trilium/wiki/", - }, - tags: ["self-hosted", "productivity", "personal-use"], - load: () => import("./trilium/index").then((m) => m.generate), - }, - { - id: "convex", - name: "Convex", - version: "latest", - description: - "Convex is an open-source reactive database designed to make life easy for web app developers.", - logo: "convex.svg", - links: { - github: "https://github.com/get-convex/convex", - website: "https://www.convex.dev/", - docs: "https://www.convex.dev/docs", - }, - tags: ["backend", "database", "api"], - load: () => import("./convex/index").then((m) => m.generate), - }, -]; + links: { + github: "https://github.com/nocodb/nocodb", + website: "https://nocodb.com/", + docs: "https://docs.nocodb.com/", + }, + logo: "nocodb.png", + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./nocodb/index").then((m) => m.generate), + }, + { + id: "meilisearch", + name: "Meilisearch", + version: "v1.8.3", + description: + "Meilisearch is a free and open-source search engine that allows you to easily add search functionality to your web applications.", + logo: "meilisearch.png", + links: { + github: "https://github.com/meilisearch/meilisearch", + website: "https://www.meilisearch.com/", + docs: "https://docs.meilisearch.com/", + }, + tags: ["search"], + load: () => import("./meilisearch/index").then((m) => m.generate), + }, + { + id: "phpmyadmin", + name: "Phpmyadmin", + version: "5.2.1", + description: + "Phpmyadmin is a free and open-source web interface for MySQL and MariaDB that allows you to manage your databases.", + logo: "phpmyadmin.png", + links: { + github: "https://github.com/phpmyadmin/phpmyadmin", + website: "https://www.phpmyadmin.net/", + docs: "https://www.phpmyadmin.net/docs/", + }, + tags: ["database"], + load: () => import("./phpmyadmin/index").then((m) => m.generate), + }, + { + id: "rocketchat", + name: "Rocketchat", + version: "6.9.2", + description: + "Rocket.Chat is a free and open-source web chat platform that allows you to build and manage your own chat applications.", + logo: "rocketchat.png", + links: { + github: "https://github.com/RocketChat/Rocket.Chat", + website: "https://rocket.chat/", + docs: "https://rocket.chat/docs/", + }, + tags: ["chat"], + load: () => import("./rocketchat/index").then((m) => m.generate), + }, + { + id: "minio", + name: "Minio", + description: + "Minio is an open source object storage server compatible with Amazon S3 cloud storage service.", + logo: "minio.png", + version: "latest", + links: { + github: "https://github.com/minio/minio", + website: "https://minio.io/", + docs: "https://docs.minio.io/", + }, + tags: ["storage"], + load: () => import("./minio/index").then((m) => m.generate), + }, + { + id: "metabase", + name: "Metabase", + version: "v0.50.8", + description: + "Metabase is an open source business intelligence tool that allows you to ask questions and visualize data.", + logo: "metabase.png", + links: { + github: "https://github.com/metabase/metabase", + website: "https://www.metabase.com/", + docs: "https://www.metabase.com/docs/", + }, + tags: ["database", "dashboard"], + load: () => import("./metabase/index").then((m) => m.generate), + }, + { + id: "glitchtip", + name: "Glitchtip", + version: "v4.0", + description: "Glitchtip is simple, open source error tracking", + logo: "glitchtip.png", + links: { + github: "https://gitlab.com/glitchtip/", + website: "https://glitchtip.com/", + docs: "https://glitchtip.com/documentation", + }, + tags: ["hosting"], + load: () => import("./glitchtip/index").then((m) => m.generate), + }, + { + id: "open-webui", + name: "Open WebUI", + version: "v0.3.7", + description: + "Open WebUI is a free and open source chatgpt alternative. Open WebUI is an extensible, feature-rich, and user-friendly self-hosted WebUI designed to operate entirely offline. It supports various LLM runners, including Ollama and OpenAI-compatible APIs. The template include ollama and webui services.", + logo: "open-webui.png", + links: { + github: "https://github.com/open-webui/open-webui", + website: "https://openwebui.com/", + docs: "https://docs.openwebui.com/", + }, + tags: ["chat"], + load: () => import("./open-webui/index").then((m) => m.generate), + }, + { + id: "listmonk", + name: "Listmonk", + version: "v3.0.0", + description: + "High performance, self-hosted, newsletter and mailing list manager with a modern dashboard.", + logo: "listmonk.png", + links: { + github: "https://github.com/knadh/listmonk", + website: "https://listmonk.app/", + docs: "https://listmonk.app/docs/", + }, + tags: ["email", "newsletter", "mailing-list"], + load: () => import("./listmonk/index").then((m) => m.generate), + }, + { + id: "doublezero", + name: "Double Zero", + version: "v0.2.1", + description: + "00 is a self hostable SES dashboard for sending and monitoring emails with AWS", + logo: "doublezero.svg", + links: { + github: "https://github.com/technomancy-dev/00", + website: "https://www.double-zero.cloud/", + docs: "https://github.com/technomancy-dev/00", + }, + tags: ["email"], + load: () => import("./doublezero/index").then((m) => m.generate), + }, + { + id: "umami", + name: "Umami", + version: "v2.14.0", + description: + "Umami is a simple, fast, privacy-focused alternative to Google Analytics.", + logo: "umami.png", + links: { + github: "https://github.com/umami-software/umami", + website: "https://umami.is", + docs: "https://umami.is/docs", + }, + tags: ["analytics"], + load: () => import("./umami/index").then((m) => m.generate), + }, + { + id: "jellyfin", + name: "jellyfin", + version: "v10.9.7", + description: + "Jellyfin is a Free Software Media System that puts you in control of managing and streaming your media. ", + logo: "jellyfin.svg", + links: { + github: "https://github.com/jellyfin/jellyfin", + website: "https://jellyfin.org/", + docs: "https://jellyfin.org/docs/", + }, + tags: ["media system"], + load: () => import("./jellyfin/index").then((m) => m.generate), + }, + { + id: "teable", + name: "teable", + version: "v1.3.1-alpha-build.460", + description: + "Teable is a Super fast, Real-time, Professional, Developer friendly, No-code database built on Postgres. It uses a simple, spreadsheet-like interface to create complex enterprise-level database applications. Unlock efficient app development with no-code, free from the hurdles of data security and scalability.", + logo: "teable.png", + links: { + github: "https://github.com/teableio/teable", + website: "https://teable.io/", + docs: "https://help.teable.io/", + }, + tags: ["database", "spreadsheet", "low-code", "nocode"], + load: () => import("./teable/index").then((m) => m.generate), + }, + { + id: "zipline", + name: "Zipline", + version: "v3.7.9", + description: + "A ShareX/file upload server that is easy to use, packed with features, and with an easy setup!", + logo: "zipline.png", + links: { + github: "https://github.com/diced/zipline", + website: "https://zipline.diced.sh/", + docs: "https://zipline.diced.sh/docs/", + }, + tags: ["media system", "storage"], + load: () => import("./zipline/index").then((m) => m.generate), + }, + { + id: "soketi", + name: "Soketi", + version: "v1.6.1-16", + description: + "Soketi is your simple, fast, and resilient open-source WebSockets server.", + logo: "soketi.png", + links: { + github: "https://github.com/soketi/soketi", + website: "https://soketi.app/", + docs: "https://docs.soketi.app/", + }, + tags: ["chat"], + load: () => import("./soketi/index").then((m) => m.generate), + }, + { + id: "aptabase", + name: "Aptabase", + version: "v1.0.0", + description: + "Aptabase is a self-hosted web analytics platform that lets you track website traffic and user behavior.", + logo: "aptabase.svg", + links: { + github: "https://github.com/aptabase/aptabase", + website: "https://aptabase.com/", + docs: "https://github.com/aptabase/aptabase/blob/main/README.md", + }, + tags: ["analytics", "self-hosted"], + load: () => import("./aptabase/index").then((m) => m.generate), + }, + { + id: "typebot", + name: "Typebot", + version: "2.27.0", + description: "Typebot is an open-source chatbot builder platform.", + logo: "typebot.svg", + links: { + github: "https://github.com/baptisteArno/typebot.io", + website: "https://typebot.io/", + docs: "https://docs.typebot.io/get-started/introduction", + }, + tags: ["chatbot", "builder", "open-source"], + load: () => import("./typebot/index").then((m) => m.generate), + }, + { + id: "gitea", + name: "Gitea", + version: "1.22.3", + description: + "Git with a cup of tea! Painless self-hosted all-in-one software development service, including Git hosting, code review, team collaboration, package registry and CI/CD.", + logo: "gitea.png", + links: { + github: "https://github.com/go-gitea/gitea.git", + website: "https://gitea.com/", + docs: "https://docs.gitea.com/installation/install-with-docker", + }, + tags: ["self-hosted", "storage"], + load: () => import("./gitea/index").then((m) => m.generate), + }, + { + id: "roundcube", + name: "Roundcube", + version: "1.6.9", + description: + "Free and open source webmail software for the masses, written in PHP.", + logo: "roundcube.svg", + links: { + github: "https://github.com/roundcube/roundcubemail", + website: "https://roundcube.net/", + docs: "https://roundcube.net/about/", + }, + tags: ["self-hosted", "email", "webmail"], + load: () => import("./roundcube/index").then((m) => m.generate), + }, + { + id: "filebrowser", + name: "File Browser", + version: "2.31.2", + description: + "Filebrowser is a standalone file manager for uploading, deleting, previewing, renaming, and editing files, with support for multiple users, each with their own directory.", + logo: "filebrowser.svg", + links: { + github: "https://github.com/filebrowser/filebrowser", + website: "https://filebrowser.org/", + docs: "https://filebrowser.org/", + }, + tags: ["file-manager", "storage"], + load: () => import("./filebrowser/index").then((m) => m.generate), + }, + { + id: "tolgee", + name: "Tolgee", + version: "v3.80.4", + description: + "Developer & translator friendly web-based localization platform", + logo: "tolgee.svg", + links: { + github: "https://github.com/tolgee/tolgee-platform", + website: "https://tolgee.io", + docs: "https://tolgee.io/platform", + }, + tags: ["self-hosted", "i18n", "localization", "translations"], + load: () => import("./tolgee/index").then((m) => m.generate), + }, + { + id: "portainer", + name: "Portainer", + version: "2.21.4", + description: + "Portainer is a container management tool for deploying, troubleshooting, and securing applications across cloud, data centers, and IoT.", + logo: "portainer.svg", + links: { + github: "https://github.com/portainer/portainer", + website: "https://www.portainer.io/", + docs: "https://docs.portainer.io/", + }, + tags: ["cloud", "monitoring"], + load: () => import("./portainer/index").then((m) => m.generate), + }, + { + id: "influxdb", + name: "InfluxDB", + version: "2.7.10", + description: + "InfluxDB 2.7 is the platform purpose-built to collect, store, process and visualize time series data.", + logo: "influxdb.png", + links: { + github: "https://github.com/influxdata/influxdb", + website: "https://www.influxdata.com/", + docs: "https://docs.influxdata.com/influxdb/v2/", + }, + tags: ["self-hosted", "open-source", "storage", "database"], + load: () => import("./influxdb/index").then((m) => m.generate), + }, + { + id: "infisical", + name: "Infisical", + version: "0.90.1", + description: + "All-in-one platform to securely manage application configuration and secrets across your team and infrastructure.", + logo: "infisical.jpg", + links: { + github: "https://github.com/Infisical/infisical", + website: "https://infisical.com/", + docs: "https://infisical.com/docs/documentation/getting-started/introduction", + }, + tags: ["self-hosted", "open-source"], + load: () => import("./infisical/index").then((m) => m.generate), + }, + { + id: "docmost", + name: "Docmost", + version: "0.4.1", + description: + "Docmost, is an open-source collaborative wiki and documentation software.", + logo: "docmost.png", + links: { + github: "https://github.com/docmost/docmost", + website: "https://docmost.com/", + docs: "https://docmost.com/docs/", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./docmost/index").then((m) => m.generate), + }, + { + id: "vaultwarden", + name: "Vaultwarden", + version: "1.32.7", + description: + "Unofficial Bitwarden compatible server written in Rust, formerly known as bitwarden_rs", + logo: "vaultwarden.svg", + links: { + github: "https://github.com/dani-garcia/vaultwarden", + website: "", + docs: "https://github.com/dani-garcia/vaultwarden/wiki", + }, + tags: ["open-source"], + load: () => import("./vaultwarden/index").then((m) => m.generate), + }, + { + id: "hi-events", + name: "Hi.events", + version: "0.8.0-beta.1", + description: + "Hi.Events is a self-hosted event management and ticket selling platform that allows you to create, manage and promote events easily.", + logo: "hi-events.svg", + links: { + github: "https://github.com/HiEventsDev/hi.events", + website: "https://hi.events/", + docs: "https://hi.events/docs", + }, + tags: ["self-hosted", "open-source", "manager"], + load: () => import("./hi-events/index").then((m) => m.generate), + }, + { + id: "windows", + name: "Windows (dockerized)", + version: "4.00", + description: "Windows inside a Docker container.", + logo: "windows.png", + links: { + github: "https://github.com/dockur/windows", + website: "", + docs: "https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./windows/index").then((m) => m.generate), + }, + { + id: "macos", + name: "MacOS (dockerized)", + version: "1.14", + description: "MacOS inside a Docker container.", + logo: "macos.png", + links: { + github: "https://github.com/dockur/macos", + website: "", + docs: "https://github.com/dockur/macos?tab=readme-ov-file#how-do-i-use-it", + }, + tags: ["self-hosted", "open-source", "os"], + load: () => import("./macos/index").then((m) => m.generate), + }, + { + id: "coder", + name: "Coder", + version: "2.15.3", + description: + "Coder is an open-source cloud development environment (CDE) that you host in your cloud or on-premises.", + logo: "coder.svg", + links: { + github: "https://github.com/coder/coder", + website: "https://coder.com/", + docs: "https://coder.com/docs", + }, + tags: ["self-hosted", "open-source", "builder"], + load: () => import("./coder/index").then((m) => m.generate), + }, + { + id: "stirling", + name: "Stirling PDF", + version: "0.30.1", + description: "A locally hosted one-stop shop for all your PDF needs", + logo: "stirling.svg", + links: { + github: "https://github.com/Stirling-Tools/Stirling-PDF", + website: "https://www.stirlingpdf.com/", + docs: "https://docs.stirlingpdf.com/", + }, + tags: ["pdf", "tools"], + load: () => import("./stirling/index").then((m) => m.generate), + }, + { + id: "lobe-chat", + name: "Lobe Chat", + version: "v1.26.1", + description: "Lobe Chat - an open-source, modern-design AI chat framework.", + logo: "lobe-chat.png", + links: { + github: "https://github.com/lobehub/lobe-chat", + website: "https://chat-preview.lobehub.com/", + docs: "https://lobehub.com/docs/self-hosting/platform/docker-compose", + }, + tags: ["IA", "chat"], + load: () => import("./lobe-chat/index").then((m) => m.generate), + }, + { + id: "peppermint", + name: "Peppermint", + version: "latest", + description: + "Peppermint is a modern, open-source API development platform that helps you build, test and document your APIs.", + logo: "peppermint.svg", + links: { + github: "https://github.com/Peppermint-Lab/peppermint", + website: "https://peppermint.sh/", + docs: "https://docs.peppermint.sh/", + }, + tags: ["api", "development", "documentation"], + load: () => import("./peppermint/index").then((m) => m.generate), + }, + { + id: "windmill", + name: "Windmill", + version: "latest", + description: + "A developer platform to build production-grade workflows and internal apps. Open-source alternative to Airplane, Retool, and GitHub Actions.", + logo: "windmill.svg", + links: { + github: "https://github.com/windmill-labs/windmill", + website: "https://www.windmill.dev/", + docs: "https://docs.windmill.dev/", + }, + tags: ["workflow", "automation", "development"], + load: () => import("./windmill/index").then((m) => m.generate), + }, + { + id: "activepieces", + name: "Activepieces", + version: "0.35.0", + description: + "Open-source no-code business automation tool. An alternative to Zapier, Make.com, and Tray.", + logo: "activepieces.svg", + links: { + github: "https://github.com/activepieces/activepieces", + website: "https://www.activepieces.com/", + docs: "https://www.activepieces.com/docs", + }, + tags: ["automation", "workflow", "no-code"], + load: () => import("./activepieces/index").then((m) => m.generate), + }, + { + id: "invoiceshelf", + name: "InvoiceShelf", + version: "latest", + description: + "InvoiceShelf is a self-hosted open source invoicing system for freelancers and small businesses.", + logo: "invoiceshelf.png", + links: { + github: "https://github.com/InvoiceShelf/invoiceshelf", + website: "https://invoiceshelf.com", + docs: "https://github.com/InvoiceShelf/invoiceshelf#readme", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./invoiceshelf/index").then((m) => m.generate), + }, + { + id: "postiz", + name: "Postiz", + version: "latest", + description: + "Postiz is a modern, open-source platform for managing and publishing content across multiple channels.", + logo: "postiz.png", + links: { + github: "https://github.com/gitroomhq/postiz", + website: "https://postiz.com", + docs: "https://docs.postiz.com", + }, + tags: ["cms", "content-management", "publishing"], + load: () => import("./postiz/index").then((m) => m.generate), + }, + { + id: "slash", + name: "Slash", + version: "latest", + description: + "Slash is a modern, self-hosted bookmarking service and link shortener that helps you organize and share your favorite links.", + logo: "slash.png", + links: { + github: "https://github.com/yourselfhosted/slash", + website: "https://github.com/yourselfhosted/slash#readme", + docs: "https://github.com/yourselfhosted/slash/wiki", + }, + tags: ["bookmarks", "link-shortener", "self-hosted"], + load: () => import("./slash/index").then((m) => m.generate), + }, + { + id: "discord-tickets", + name: "Discord Tickets", + version: "4.0.21", + description: + "An open-source Discord bot for creating and managing support ticket channels.", + logo: "discord-tickets.png", + links: { + github: "https://github.com/discord-tickets/bot", + website: "https://discordtickets.app", + docs: "https://discordtickets.app/self-hosting/installation/docker/", + }, + tags: ["discord", "tickets", "support"], + load: () => import("./discord-tickets/index").then((m) => m.generate), + }, + { + id: "nextcloud-aio", + name: "Nextcloud All in One", + version: "30.0.2", + description: + "Nextcloud (AIO) is a self-hosted file storage and sync platform with powerful collaboration capabilities. It integrates Files, Talk, Groupware, Office, Assistant and more into a single platform for remote work and data protection.", + logo: "nextcloud-aio.svg", + links: { + github: "https://github.com/nextcloud/docker", + website: "https://nextcloud.com/", + docs: "https://docs.nextcloud.com/", + }, + tags: ["file-manager", "sync"], + load: () => import("./nextcloud-aio/index").then((m) => m.generate), + }, + { + id: "blender", + name: "Blender", + version: "latest", + description: + "Blender is a free and open-source 3D creation suite. It supports the entire 3D pipeline—modeling, rigging, animation, simulation, rendering, compositing and motion tracking, video editing and 2D animation pipeline.", + logo: "blender.svg", + links: { + github: "https://github.com/linuxserver/docker-blender", + website: "https://www.blender.org/", + docs: "https://docs.blender.org/", + }, + tags: ["3d", "rendering", "animation"], + load: () => import("./blender/index").then((m) => m.generate), + }, + { + id: "heyform", + name: "HeyForm", + version: "latest", + description: + "Allows anyone to create engaging conversational forms for surveys, questionnaires, quizzes, and polls. No coding skills required.", + logo: "heyform.svg", + links: { + github: "https://github.com/heyform/heyform", + website: "https://heyform.net", + docs: "https://docs.heyform.net", + }, + tags: ["form", "builder", "questionnaire", "quiz", "survey"], + load: () => import("./heyform/index").then((m) => m.generate), + }, + { + id: "chatwoot", + name: "Chatwoot", + version: "v3.14.1", + description: + "Open-source customer engagement platform that provides a shared inbox for teams, live chat, and omnichannel support.", + logo: "chatwoot.svg", + links: { + github: "https://github.com/chatwoot/chatwoot", + website: "https://www.chatwoot.com", + docs: "https://www.chatwoot.com/docs", + }, + tags: ["support", "chat", "customer-service"], + load: () => import("./chatwoot/index").then((m) => m.generate), + }, + { + id: "discourse", + name: "Discourse", + version: "3.3.2", + description: + "Discourse is a modern forum software for your community. Use it as a mailing list, discussion forum, or long-form chat room.", + logo: "discourse.svg", + links: { + github: "https://github.com/discourse/discourse", + website: "https://www.discourse.org/", + docs: "https://meta.discourse.org/", + }, + tags: ["forum", "community", "discussion"], + load: () => import("./discourse/index").then((m) => m.generate), + }, + { + id: "immich", + name: "Immich", + version: "v1.121.0", + description: + "High performance self-hosted photo and video backup solution directly from your mobile phone.", + logo: "immich.svg", + links: { + github: "https://github.com/immich-app/immich", + website: "https://immich.app/", + docs: "https://immich.app/docs/overview/introduction", + }, + tags: ["photos", "videos", "backup", "media"], + load: () => import("./immich/index").then((m) => m.generate), + }, + { + id: "twenty", + name: "Twenty CRM", + version: "latest", + description: + "Twenty is a modern CRM offering a powerful spreadsheet interface and open-source alternative to Salesforce.", + logo: "twenty.svg", + links: { + github: "https://github.com/twentyhq/twenty", + website: "https://twenty.com", + docs: "https://docs.twenty.com", + }, + tags: ["crm", "sales", "business"], + load: () => import("./twenty/index").then((m) => m.generate), + }, + { + id: "yourls", + name: "YOURLS", + version: "1.9.2", + description: + "YOURLS (Your Own URL Shortener) is a set of PHP scripts that will allow you to run your own URL shortening service (a la TinyURL or Bitly).", + logo: "yourls.svg", + links: { + github: "https://github.com/YOURLS/YOURLS", + website: "https://yourls.org/", + docs: "https://yourls.org/#documentation", + }, + tags: ["url-shortener", "php"], + load: () => import("./yourls/index").then((m) => m.generate), + }, + { + id: "ryot", + name: "Ryot", + version: "v7.10", + description: + "A self-hosted platform for tracking various media types including movies, TV shows, video games, books, audiobooks, and more.", + logo: "ryot.png", + links: { + github: "https://github.com/IgnisDa/ryot", + website: "https://ryot.io/", + docs: "https://docs.ryot.io/", + }, + tags: ["media", "tracking", "self-hosted"], + load: () => import("./ryot/index").then((m) => m.generate), + }, + { + id: "photoprism", + name: "Photoprism", + version: "latest", + description: + "PhotoPrism® is an AI-Powered Photos App for the Decentralized Web. It makes use of the latest technologies to tag and find pictures automatically without getting in your way.", + logo: "photoprism.svg", + links: { + github: "https://github.com/photoprism/photoprism", + website: "https://www.photoprism.app/", + docs: "https://docs.photoprism.app/", + }, + tags: ["media", "photos", "self-hosted"], + load: () => import("./photoprism/index").then((m) => m.generate), + }, + { + id: "ontime", + name: "Ontime", + version: "v3.8.0", + description: + "Ontime is browser-based application that manages event rundowns, scheduliing and cuing", + logo: "ontime.png", + links: { + github: "https://github.com/cpvalente/ontime/", + website: "https://getontime.no", + docs: "https://docs.getontime.no", + }, + tags: ["event"], + load: () => import("./ontime/index").then((m) => m.generate), + }, + { + id: "triggerdotdev", + name: "Trigger.dev", + version: "v3", + description: + "Trigger is a platform for building event-driven applications.", + logo: "triggerdotdev.svg", + links: { + github: "https://github.com/triggerdotdev/trigger.dev", + website: "https://trigger.dev/", + docs: "https://trigger.dev/docs", + }, + tags: ["event-driven", "applications"], + load: () => import("./triggerdotdev/index").then((m) => m.generate), + }, + { + id: "browserless", + name: "Browserless", + version: "2.23.0", + description: + "Browserless allows remote clients to connect and execute headless work, all inside of docker. It supports the standard, unforked Puppeteer and Playwright libraries, as well offering REST-based APIs for common actions like data collection, PDF generation and more.", + logo: "browserless.svg", + links: { + github: "https://github.com/browserless/browserless", + website: "https://www.browserless.io/", + docs: "https://docs.browserless.io/", + }, + tags: ["browser", "automation"], + load: () => import("./browserless/index").then((m) => m.generate), + }, + { + id: "drawio", + name: "draw.io", + version: "24.7.17", + description: + "draw.io is a configurable diagramming/whiteboarding visualization application.", + logo: "drawio.svg", + links: { + github: "https://github.com/jgraph/drawio", + website: "https://draw.io/", + docs: "https://www.drawio.com/doc/", + }, + tags: ["drawing", "diagrams"], + load: () => import("./drawio/index").then((m) => m.generate), + }, + { + id: "kimai", + name: "Kimai", + version: "2.26.0", + description: + "Kimai is a web-based multi-user time-tracking application. Works great for everyone: freelancers, companies, organizations - everyone can track their times, generate reports, create invoices and do so much more.", + logo: "kimai.svg", + links: { + github: "https://github.com/kimai/kimai", + website: "https://www.kimai.org", + docs: "https://www.kimai.org/documentation", + }, + tags: ["invoice", "business", "finance"], + load: () => import("./kimai/index").then((m) => m.generate), + }, + { + id: "logto", + name: "Logto", + version: "1.22.0", + description: + "Logto is an open-source Identity and Access Management (IAM) platform designed to streamline Customer Identity and Access Management (CIAM) and Workforce Identity Management.", + logo: "logto.png", + links: { + github: "https://github.com/logto-io/logto", + website: "https://logto.io/", + docs: "https://docs.logto.io/introduction", + }, + tags: ["identity", "auth"], + load: () => import("./logto/index").then((m) => m.generate), + }, + { + id: "penpot", + name: "Penpot", + version: "2.3.2", + description: + "Penpot is the web-based open-source design tool that bridges the gap between designers and developers.", + logo: "penpot.svg", + links: { + github: "https://github.com/penpot/penpot", + website: "https://penpot.app/", + docs: "https://docs.penpot.app/", + }, + tags: ["design", "collaboration"], + load: () => import("./penpot/index").then((m) => m.generate), + }, + { + id: "huly", + name: "Huly", + version: "0.6.377", + description: + "Huly — All-in-One Project Management Platform (alternative to Linear, Jira, Slack, Notion, Motion)", + logo: "huly.svg", + links: { + github: "https://github.com/hcengineering/huly-selfhost", + website: "https://huly.io/", + docs: "https://docs.huly.io/", + }, + tags: ["project-management", "community", "discussion"], + load: () => import("./huly/index").then((m) => m.generate), + }, + { + id: "unsend", + name: "Unsend", + version: "v1.3.2", + description: "Open source alternative to Resend,Sendgrid, Postmark etc. ", + logo: "unsend.png", + links: { + github: "https://github.com/unsend-dev/unsend", + website: "https://unsend.dev/", + docs: "https://docs.unsend.dev/get-started/", + }, + tags: ["e-mail", "marketing", "business"], + load: () => import("./unsend/index").then((m) => m.generate), + }, + { + id: "langflow", + name: "Langflow", + version: "1.1.1", + description: + "Langflow is a low-code app builder for RAG and multi-agent AI applications. It’s Python-based and agnostic to any model, API, or database. ", + logo: "langflow.svg", + links: { + github: "https://github.com/langflow-ai/langflow/tree/main", + website: "https://www.langflow.org/", + docs: "https://docs.langflow.org/", + }, + tags: ["ai"], + load: () => import("./langflow/index").then((m) => m.generate), + }, + { + id: "elastic-search", + name: "Elasticsearch", + version: "8.10.2", + description: + "Elasticsearch is an open-source search and analytics engine, used for full-text search and analytics on structured data such as text, web pages, images, and videos.", + logo: "elasticsearch.svg", + links: { + github: "https://github.com/elastic/elasticsearch", + website: "https://www.elastic.co/elasticsearch/", + docs: "https://docs.elastic.co/elasticsearch/", + }, + tags: ["search", "analytics"], + load: () => import("./elastic-search/index").then((m) => m.generate), + }, + { + id: "onedev", + name: "OneDev", + version: "11.6.6", + description: + "Git server with CI/CD, kanban, and packages. Seamless integration. Unparalleled experience.", + logo: "onedev.png", + links: { + github: "https://github.com/theonedev/onedev/", + website: "https://onedev.io/", + docs: "https://docs.onedev.io/", + }, + tags: ["self-hosted", "development"], + load: () => import("./onedev/index").then((m) => m.generate), + }, + { + id: "unifi", + name: "Unifi Network", + version: "11.6.6", + description: + "Unifi Network is an open-source enterprise network management platform for wireless networks.", + logo: "unifi.webp", + links: { + github: "https://github.com/ubiquiti", + website: "https://www.ui.com/", + docs: "https://help.ui.com/hc/en-us/articles/360012282453-Self-Hosting-a-UniFi-Network-Server", + }, + tags: ["self-hosted", "networking"], + load: () => import("./unifi/index").then((m) => m.generate), + }, + { + id: "glpi", + name: "GLPI Project", + version: "10.0.16", + description: "The most complete open source service management software", + logo: "glpi.webp", + links: { + github: "https://github.com/glpi-project/glpi", + website: "https://glpi-project.org/", + docs: "https://glpi-project.org/documentation/", + }, + tags: ["self-hosted", "project-management", "management"], + load: () => import("./glpi/index").then((m) => m.generate), + }, + { + id: "checkmate", + name: "Checkmate", + version: "2.0.1", + description: + "Checkmate is an open-source, self-hosted tool designed to track and monitor server hardware, uptime, response times, and incidents in real-time with beautiful visualizations.", + logo: "checkmate.png", + links: { + github: "https://github.com/bluewave-labs/checkmate", + website: "https://bluewavelabs.ca", + docs: "https://bluewavelabs.gitbook.io/checkmate", + }, + tags: ["self-hosted", "monitoring", "uptime"], + load: () => import("./checkmate/index").then((m) => m.generate), + }, + { + id: "gotenberg", + name: "Gotenberg", + version: "latest", + description: "Gotenberg is a Docker-powered stateless API for PDF files.", + logo: "gotenberg.png", + links: { + github: "https://github.com/gotenberg/gotenberg", + website: "https://gotenberg.dev", + docs: "https://gotenberg.dev/docs/getting-started/introduction", + }, + tags: ["api", "backend", "pdf", "tools"], + load: () => import("./gotenberg/index").then((m) => m.generate), + }, + { + id: "actualbudget", + name: "Actual Budget", + version: "latest", + description: + "A super fast and privacy-focused app for managing your finances.", + logo: "actualbudget.png", + links: { + github: "https://github.com/actualbudget/actual", + website: "https://actualbudget.org", + docs: "https://actualbudget.org/docs", + }, + tags: ["budgeting", "finance", "money"], + load: () => import("./actualbudget/index").then((m) => m.generate), + }, + { + id: "conduit", + name: "Conduit", + version: "v0.9.0", + description: + "Conduit is a simple, fast and reliable chat server powered by Matrix", + logo: "conduit.svg", + links: { + github: "https://gitlab.com/famedly/conduit", + website: "https://conduit.rs/", + docs: "https://docs.conduit.rs/", + }, + tags: ["matrix", "communication"], + load: () => import("./conduit/index").then((m) => m.generate), + }, + { + id: "evolutionapi", + name: "Evolution API", + version: "v2.1.2", + description: + "Evolution API is a robust platform dedicated to empowering small businesses with limited resources, going beyond a simple messaging solution via WhatsApp.", + logo: "evolutionapi.png", + links: { + github: "https://github.com/EvolutionAPI/evolution-api", + docs: "https://doc.evolution-api.com/v2/en/get-started/introduction", + website: "https://evolution-api.com/opensource-whatsapp-api/", + }, + tags: ["api", "whatsapp", "messaging"], + load: () => import("./evolutionapi/index").then((m) => m.generate), + }, + { + id: "conduwuit", + name: "Conduwuit", + version: "latest", + description: + "Well-maintained, featureful Matrix chat homeserver (fork of Conduit)", + logo: "conduwuit.svg", + links: { + github: "https://github.com/girlbossceo/conduwuit", + website: "https://conduwuit.puppyirl.gay", + docs: "https://conduwuit.puppyirl.gay/configuration.html", + }, + tags: ["backend", "chat", "communication", "matrix", "server"], + load: () => import("./conduwuit/index").then((m) => m.generate), + }, + { + id: "cloudflared", + name: "Cloudflared", + version: "latest", + description: + "A lightweight daemon that securely connects local services to the internet through Cloudflare Tunnel.", + logo: "cloudflared.svg", + links: { + github: "https://github.com/cloudflare/cloudflared", + website: + "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/", + docs: "https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/", + }, + tags: ["cloud", "networking", "security", "tunnel"], + load: () => import("./cloudflared/index").then((m) => m.generate), + }, + { + id: "couchdb", + name: "CouchDB", + version: "latest", + description: + "CouchDB is a document-oriented NoSQL database that excels at replication and horizontal scaling.", + logo: "couchdb.png", + links: { + github: "https://github.com/apache/couchdb", + website: "https://couchdb.apache.org/", + docs: "https://docs.couchdb.org/en/stable/", + }, + tags: ["database", "storage"], + load: () => import("./couchdb/index").then((m) => m.generate), + }, + { + id: "it-tools", + name: "IT Tools", + version: "latest", + description: "A collection of handy online it-tools for developers.", + logo: "it-tools.svg", + links: { + github: "https://github.com/CorentinTh/it-tools", + website: "https://it-tools.tech", + }, + tags: ["developer", "tools"], + load: () => import("./it-tools/index").then((m) => m.generate), + }, + { + id: "superset", + name: "Superset (Unofficial)", + version: "latest", + description: "Data visualization and data exploration platform.", + logo: "superset.svg", + links: { + github: "https://github.com/amancevice/docker-superset", + website: "https://superset.apache.org", + docs: "https://superset.apache.org/docs/intro", + }, + tags: ["analytics", "bi", "dashboard", "database", "sql"], + load: () => import("./superset/index").then((m) => m.generate), + }, + { + id: "glance", + name: "Glance", + version: "latest", + description: + "A self-hosted dashboard that puts all your feeds in one place. Features RSS feeds, weather, bookmarks, site monitoring, and more in a minimal, fast interface.", + logo: "glance.png", + links: { + github: "https://github.com/glanceapp/glance", + docs: "https://github.com/glanceapp/glance/blob/main/docs/configuration.md", + }, + tags: ["dashboard", "monitoring", "widgets", "rss"], + load: () => import("./glance/index").then((m) => m.generate), + }, + { + id: "homarr", + name: "Homarr", + version: "latest", + description: + "A sleek, modern dashboard that puts all your apps and services in one place with Docker integration.", + logo: "homarr.png", + links: { + github: "https://github.com/homarr-labs/homarr", + docs: "https://homarr.dev/docs/getting-started/installation/docker", + website: "https://homarr.dev/", + }, + tags: ["dashboard", "monitoring"], + load: () => import("./homarr/index").then((m) => m.generate), + }, + { + id: "erpnext", + name: "ERPNext", + version: "version-15", + description: "100% Open Source and highly customizable ERP software.", + logo: "erpnext.svg", + links: { + github: "https://github.com/frappe/erpnext", + docs: "https://docs.frappe.io/erpnext", + website: "https://erpnext.com", + }, + tags: [ + "erp", + "accounts", + "manufacturing", + "retail", + "sales", + "pos", + "hrms", + ], + load: () => import("./erpnext/index").then((m) => m.generate), + }, + { + id: "maybe", + name: "Maybe", + version: "latest", + description: + "Maybe is a self-hosted finance tracking application designed to simplify budgeting and expenses.", + logo: "maybe.svg", + links: { + github: "https://github.com/maybe-finance/maybe", + website: "https://maybe.finance/", + docs: "https://docs.maybe.finance/", + }, + tags: ["finance", "self-hosted"], + load: () => import("./maybe/index").then((m) => m.generate), + }, + { + id: "spacedrive", + name: "Spacedrive", + version: "latest", + description: + "Spacedrive is a cross-platform file manager. It connects your devices together to help you organize files from anywhere. powered by a virtual distributed filesystem (VDFS) written in Rust. Organize files across many devices in one place.", + links: { + github: "https://github.com/spacedriveapp/spacedrive", + website: "https://spacedrive.com/", + docs: "https://www.spacedrive.com/docs/product/getting-started/introduction", + }, + logo: "spacedrive.png", + tags: ["file-manager", "vdfs", "storage"], + load: () => import("./spacedrive/index").then((m) => m.generate), + }, + { + id: "registry", + name: "Docker Registry", + version: "2", + description: + "Distribution implementation for storing and distributing of Docker container images and artifacts.", + links: { + github: "https://github.com/distribution/distribution", + website: "https://hub.docker.com/_/registry", + docs: "https://distribution.github.io/distribution/", + }, + logo: "registry.png", + tags: ["registry", "docker", "self-hosted"], + load: () => import("./registry/index").then((m) => m.generate), + }, + { + id: "alist", + name: "AList", + version: "v3.41.0", + description: + "🗂️A file list/WebDAV program that supports multiple storages, powered by Gin and Solidjs.", + logo: "alist.svg", + links: { + github: "https://github.com/AlistGo/alist", + website: "https://alist.nn.ci", + docs: "https://alist.nn.ci/guide/install/docker.html", + }, + tags: ["file", "webdav", "storage"], + load: () => import("./alist/index").then((m) => m.generate), + }, + { + id: "answer", + name: "Answer", + version: "v1.4.1", + description: + "Answer is an open-source Q&A platform for building a self-hosted question-and-answer service.", + logo: "answer.png", + links: { + github: "https://github.com/apache/answer", + website: "https://answer.apache.org/", + docs: "https://answer.apache.org/docs", + }, + tags: ["q&a", "self-hosted"], + load: () => import("./answer/index").then((m) => m.generate), + }, + { + id: "shlink", + name: "Shlink", + version: "stable", + description: + "URL shortener that can be used to serve shortened URLs under your own domain.", + logo: "shlink.svg", + links: { + github: "https://github.com/shlinkio/shlink", + website: "https://shlink.io", + docs: "https://shlink.io/documentation", + }, + tags: ["sharing", "shortener", "url"], + load: () => import("./shlink/index").then((m) => m.generate), + }, + { + id: "frappe-hr", + name: "Frappe HR", + version: "version-15", + description: + "Feature rich HR & Payroll software. 100% FOSS and customizable.", + logo: "frappe-hr.svg", + links: { + github: "https://github.com/frappe/hrms", + docs: "https://docs.frappe.io/hr", + website: "https://frappe.io/hr", + }, + tags: ["hrms", "payroll", "leaves", "expenses", "attendance", "performace"], + load: () => import("./frappe-hr/index").then((m) => m.generate), + }, + { + id: "formbricks", + name: "Formbricks", + version: "v3.1.3", + description: + "Formbricks is an open-source survey and form platform for collecting user data.", + logo: "formbricks.png", + links: { + github: "https://github.com/formbricks/formbricks", + website: "https://formbricks.com/", + docs: "https://formbricks.com/docs", + }, + tags: ["forms", "analytics"], + load: () => import("./formbricks/index").then((m) => m.generate), + }, + { + id: "trilium", + name: "Trilium", + description: + "Trilium Notes is a hierarchical note taking application with focus on building large personal knowledge bases.", + logo: "trilium.png", + version: "latest", + links: { + github: "https://github.com/zadam/trilium", + website: "https://github.com/zadam/trilium", + docs: "https://github.com/zadam/trilium/wiki/", + }, + tags: ["self-hosted", "productivity", "personal-use"], + load: () => import("./trilium/index").then((m) => m.generate), + }, + { + id: "convex", + name: "Convex", + version: "latest", + description: + "Convex is an open-source reactive database designed to make life easy for web app developers.", + logo: "convex.svg", + links: { + github: "https://github.com/get-convex/convex", + website: "https://www.convex.dev/", + docs: "https://www.convex.dev/docs", + }, + tags: ["backend", "database", "api"], + load: () => import("./convex/index").then((m) => m.generate), + }, +]; \ No newline at end of file From 51310dae1dfd3f6fda1b8b5a3fcc822a19154757 Mon Sep 17 00:00:00 2001 From: sondreal Date: Sun, 23 Feb 2025 19:15:17 +0100 Subject: [PATCH 104/126] fixed the links --- apps/dokploy/templates/templates.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index b488cbdeb..4fafecb58 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -25,8 +25,8 @@ export const templates: TemplateData[] = [ "Outline is a self-hosted knowledge base and documentation platform that allows you to build and manage your own knowledge base applications.", links: { github: "https://github.com/outline/outline", - website: "https://outline.com/", - docs: "https://docs.outline.com/", + website: "https://getoutline.com/", + docs: "https://docs.getoutline.com/s/guide", }, logo: "outline.png", load: () => import("./outline/index").then((m) => m.generate), From f4d13c30302a342dd5fe4b99f25894aa699ebe97 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 14:35:28 -0600 Subject: [PATCH 105/126] Update apps/dokploy/templates/wikijs/docker-compose.yml --- apps/dokploy/templates/wikijs/docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/apps/dokploy/templates/wikijs/docker-compose.yml b/apps/dokploy/templates/wikijs/docker-compose.yml index 132774198..137aca0ce 100644 --- a/apps/dokploy/templates/wikijs/docker-compose.yml +++ b/apps/dokploy/templates/wikijs/docker-compose.yml @@ -3,9 +3,6 @@ services: wiki: image: ghcr.io/requarks/wiki:2.5 restart: unless-stopped - ports: - # Change 5000 to the desired port - - "5000:3000" environment: - DB_TYPE - DB_HOST From 5871a91da57a783f8b115cb6cdf6621638e71571 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 14:35:33 -0600 Subject: [PATCH 106/126] Update apps/dokploy/templates/wikijs/docker-compose.yml --- apps/dokploy/templates/wikijs/docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/dokploy/templates/wikijs/docker-compose.yml b/apps/dokploy/templates/wikijs/docker-compose.yml index 137aca0ce..c624f2fee 100644 --- a/apps/dokploy/templates/wikijs/docker-compose.yml +++ b/apps/dokploy/templates/wikijs/docker-compose.yml @@ -12,8 +12,6 @@ services: - DB_NAME depends_on: - db - networks: - - dokploy-network labels: - traefik.enable=true - traefik.constraint-label-stack=wikijs From c0b64c6e559ed74ca0ced596aa9747671a87ec3e Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 14:35:39 -0600 Subject: [PATCH 107/126] Update apps/dokploy/templates/wikijs/docker-compose.yml --- apps/dokploy/templates/wikijs/docker-compose.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/dokploy/templates/wikijs/docker-compose.yml b/apps/dokploy/templates/wikijs/docker-compose.yml index c624f2fee..6b21423d1 100644 --- a/apps/dokploy/templates/wikijs/docker-compose.yml +++ b/apps/dokploy/templates/wikijs/docker-compose.yml @@ -24,8 +24,6 @@ services: - POSTGRES_DB volumes: - wiki-db-data:/var/lib/postgresql/data - networks: - - dokploy-network networks: dokploy-network: external: true From ac3378ccb83a3a6bd53d110768363fb46031f7f5 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 15:38:25 -0600 Subject: [PATCH 108/126] feat(sponsors): add Synexa to sponsors section --- .github/sponsors/synexa.png | Bin 0 -> 42254 bytes README.md | 4 ++++ 2 files changed, 4 insertions(+) create mode 100644 .github/sponsors/synexa.png diff --git a/.github/sponsors/synexa.png b/.github/sponsors/synexa.png new file mode 100644 index 0000000000000000000000000000000000000000..737ccd57629252cf86562ba3b5cee74128a889be GIT binary patch literal 42254 zcmeEug;SPaw6%1Hgn+c5ba%IODV>6Zba#g!AYIZS`O@7WAlJ`7cXEg{{2IS1^**iE3^FKh5rj# zNij9|^usn3Z#>h?XG3Lmx1Dy}I8j)XbyR78ikFfwVhAU2&&>P0%^U*fP={8RozvHL zZqwSPn$o0w`Yt4WpL>ZBV46+@#ARSn{tC2ffO=$fg4faqp$z>(f`Wbhzc-x5qNiwZ38=H|D*=HP{)NSs=4RLc{{oBb?e(k> zGhs(d2b>`UF(nlDOq$@`W?K;nSK19$Ja=^F3~<;^X5ssolpMg;NjV8?qaGoU0bw=tA)9!j{6E9HZrhI+sfq#Yja`Qqu6;Z@EPW z{A0|p=-q_=RDl$RaNz>%b0}`!eSLj}`T3FkL&RK`YG=VHo4wKG0c`Ei^|J| z)l{p@$1*PuW_dU{W#Zr?utZuYWJH2CqR{!fxY(GOT#f?~lJ&j!O8IJP*x5HXHc_yi zaIci25M9!V4#aNX{hqRqLR7RJ^2U0-uBUQ`f|0t>Nka$qK=Z6FO<9u-L|i2-AElU| z(#gcsMCl}^L4>ujvAMrKF&ro=DfvmGCrTmH0d5~U^RJR)XMQIqr;0|$fob_ZA4yZ3jDYV zr1-_5udgrQcIL%r9_@I=^*7@&@qA|KF zNqL*g$%R~w)9PgT9o>`oEdMb`EumA$~W7HBN|>IhtQ?(ma_4W~>&avCyE!6B84v z?H%4d;rI-ir9}`(prB`hJ9RF&2ylo+Y0$lNUWduad`(09bp$HuC@Pdt230`^9-8%fP)VKVK2*qOHoMKl^=6u`uM3!X532 z1=*=J4W8U8_3>wc1q8C+T+ZH=rKYa_*?;VhBzkw{N^yTUiFZT!UPz8*YA%AG_5_!@ z`64RwyWg`usgDp_!tfl1=JNgKQj5cQ7sFrAzk;y-uogmRUBpCV$Wfk3#P5j3Wvs0a z8>==eI@Uw1%A5EZ804|!>M4<7$I_a11^YOp1-LSH6Fw8|Y|{jjEPX0W6Pt=bv4eNf zR>$YUMwY40D}J4h?`>d5fqi=Gol84dJ?x;TprGLG?d|C3C?^-r(EIi@a1A@1IT^a= z*rexBRfSm?gq>@VDq#;_K}|a=|2-#g;GG~MvG*`Df|fW2!u@h~h=l!av+u4-kKxnb zWM$!}KXX-|Ri45%1Sdnm(L9D)lo8-FX*+J!%|4kd%{c@H@$d!(3(OKRQds1dR@UNk zOneRT5K$mNF(NfaSDXf^CN(w9hfG<~=4j%lw15ErKJmW5f`GSgN3XSqDjvR6tSQm3 zM}M{(e+EclRaI5x*#!b15I=w~=p;KwO*K2m-*f5hogH(NJabcwq5i?|5s~;ml6Ui3 z%tQ)iv_p?1XmPTM-=C)UoP)fWNn70tLVot?qz<1!gOig}bc;z&aFXWV;$gld4Q)05 ziIcZ&+Da;h7WDS@AgC*SGGocn*o(Qf6^PGZ-{IyG&$JiV!){%utE-EQj0C}4!mH(Q zI9GLdcPC0QzP!8)g~I7ru>5+utk5;OAs%kl$W)e-larN|RZ<%5@26#aEd=t`qy_Yx z2xRC}-58Adp7+K35#)WEI!uPtmiv@~l!cRAwBWXNtE-nMr zrL?HLJQNsJ75r_YS!_3=V%X_Q=8T^=eup`24{Nu&JBW#ixw>*6{xl*y_0^r}=AiSE z@;91YlU4r+7x1g)r1kz699C_8eePovMSnJ8Fe4w|ZK_o{9ScjSl5Tiz(W~Zit4?Ax zx30%CwhscL5ZdtB0~>2=y(;6L5uQ~&EiFRZ#Lzm=?}x`f9(%g_$HqFI?hXw79xuB? zv92#J=7h<;kh&!*)~hrGc!#~^=+o2p5B4j-FC`@4-BSlph@d~r21%ldf1^O|>FH@$ zavYk>m;e0vbN6D+1-sq4rl$w%nz}09!Tvrb32&vtX0OY^45_fU7G4ib#o!x4Ls_xg$KY*8n^@oO3L-KK5{z?i&$zDl8qSam7%R%3rfxJ8S&)@&3&65 zAjgjioU|SuS{Fhs1Pl;8ciKs{=*P}2EG#(8hPA+we0+osGjeGwYaoz?sMj1nv-bJ9 z)FhyEoD(SRtD9%7_s1=7-@c`}(ig>s-_sObi2L|q#pfE7;iD;uuQ^{|mLF!j*~9Mc z?Ro5ExS0+AfFXB>eDP%I(m(&)7v-}ZJyz@D+8_RgF1!?i7?I7cj6RJ)rB*BDA~gIG zOZxCv09?c?CP)5^KnnB=TTsT;!XhGcD?fEMV;pcP;-*ZLYt5PF)^Dv0eX_H$@p*o{ zW@%i5{n7doVcXLEyW7Hrh2KNPGo%#q7YR1JfIbycqob+2beY{qyuML>z)#ZL+uIAy zV_>uu%l~Zil*(<($do1 zlKuu|wX?Ic^Rdk5$srauSGVF+OBdBo`mcu5jF#7`m>I$cQ(rl|xjnCA`4O@iOX8)U zgPOoqS$7kFL{?N`_emH$(?xwE=vpH!*&xG6jp4SUx%sZV<(QtHKHPTx&8w7@lw?ES z+XWY^ZjN^tx_cNBKby_`lY3QMB4s;r*a;pCpYLX?KKH%94iLvSQF#%FPe5=zDbF%g z{U*Gcnl3KSzuhYJC7C1!R=#*exw6Qk^WQ%Mr6t33d%DlwVrniQ zsz#TVn05ZRMCUXyw+x+=RW&X(X!E?-ohUj14Cd88)O0v^z2c>eO5b7<*1|ZnW-^;Y z@VNq=NENT+Zog#FwuR{~oplxATODd3tK0DsOZ!dN!DYxB?D5^ntI4@!L5)>8Md;Ph zB9G}Ho=h(JuD%#H_3L$yrwgd^^IA8SkFTF7djG4qt<(vn5O6miFK34a2T3sdu+?0N z=dRbgL!Y0nJ1UBcQKZt!tVnmAtdnaNR8jS3hlYm0sRz!@%~5(?V8e$YQz&qlkLoqp zW{mB?MF%h<*@eHU(WU)?8ol1$)O35?axywHLONJ&3uln}r5t3 z<33mw4=L4l@Iv|W$-TdSuNg^V=h?l`7d{{6C@CqCQsw-r|LQFr#ZVk%FdV1Ve1P4I zp1OY~l-!pWyw*5X0yXh2l-2v@)O08*z&?%A;9h+?pP7J6 z7hcR4Ia~PW`^dZ9pV2Ag@8e8kmwz;tdvdQ+B(8aS}oI(~St0kQbMtCoq8&g?O&Q z_ekX4*vQ`)`| zcIbq&tg4h*Jwx~#1lV@{7S{~lyS#F|^lTh*1VHHM~#h zWz|j8Rqw>z`y{O&_(6yiMg*UQCA`4RjWm6S2Kzzt-7kZRNQY+ zFn?0se)iu?N=Uf)SM%!X=t|cJY&FniC3u|GXSuqOg~!B8{X8yN=;-L++FVUk{NLgK zWdd}|*topyYGLuDk&2Ekg-?r?l7z)RxRSW9dkx*n6$j+^x`tKX`{U&nhgZANlIqiN zNSI$zxqa>~e#{X@O|G4p70Sax%|~F!JF_q>6;e6FUOR(bfj}@X7GA;={T9W%e5e@{ zR#T@N2&ZO%K&Bb*IQqkZ0M>9Xg zWlP@AtTh>zSrg9s(?NbA_4V~x&ks9uzB39mYW-AY|Yy z-Ss};S&XEruGff&0q4}c!sL@ zvT=h$Lu0&~-vbP|0x5#ImkR!b~&kk4_GD!pvu zXrhR8YnIOg%D#@5T(e)A#oPlvpO<=w({jyyvxGf%Ud|4-PfSd7Tk)73&1gYf8ncaF z2_Z)i`xY6gsH{vb?CtLD-TLd_PYMd(BO*3=mEx#XaimmDNZl?)MGwE&+1Xg(!{z4E zo>H!|4z;Wz!^4gVUF*5JUTq~C9y>Pi8vhdhE2v9Y8jV3-nDCF{KuXFzujb8>yyyEx z*}hrhny82&rwG`sI1N2{bu~JOJ^HU@(4`b;smQtL(*$#Jap`*<)ur*+k%`iw@YK@f zv$3&33zb>u=~H13(A#6FjN- zw==x&3r zjVWAnCqKBh6}S$oAyakr>1xxV?ctR8xVQ~xek4gwqhta`t?`A0-Vm?dn5*(oo%gS= z>}?dGPn!eJ_Dy<^tupXph*ZHb>FMc;Y1{}luG)zvJ(g3Lk>1)D36!#vedK*7@L$IXomM3YsI%#O<$-Nl6k^~gjnF=e)0Qk23su1O#8^2^JQ2j6NMn^6a% zyNJU2KHsm_%zUb*E-qyvERj}!s~$hBAoE^Is^>cq$KAaDSDT)`zN|L;b*G`Eqm`Pz zBZnaGlGH&)7b(-9OMKUB}!X9R5KRmq$t#xHbNpXG1cft+n+L{vNctaor#bT}yzVniq%P1d#^LM$qJPcwsx^ao@z*OuiR+Da~`X<Mh#*Ob`#=D z;g07MyXVL69HTNaGP_w+aA}PI%1NnyNKny(VZ1&aCQ^h7-`;G3iz&l`A{HZtL}>!B zA~BB*I%~cP9)7dP!m-&nrgl;^37_M5sd5n#ji z&-hqZu~|%A(wZS9;jdcR{Ayh%i;5p%@h;MK;La01eJRlG+pS}=KVpFBXlr`_#7=+* zK6@l1Cx4Fidw?#zm0vR2n$jtRKyqnU?7K0(v{Lnt!)wB7Ha>p5+uZJRe+49lpN&{APshIhFfljpcksN}(vs!NI}6U^P{w3Q*4N?QI~1D5(fF0Y3@m zjeR2le8A7CHIynVmyu_cOsWLOg#GH$C7?pHd|+S*!}>D$~=D9s7?B4^i@mf_v7dxJj1DY!^0QIBZl5pa&dSV{GmiMrl_Vo%YrJp^|l#;rtI8! z5-~aAptynqqSi)m(41q^TN0akx|{6)2>znmnx3YnX zY3-O)c{vovsasJzZT#W;=kBZSkC9=x5mY|>)WLdg%PyrwTGO1%*7&katFcD>#hy#Y zhRfbH&@J&D^gwi!=aE2I8W%3*6T(SG-hoTcSHVEo;v$!gQ%>YI&I%1OV9 zi&L-m%(KM0Q)DWT#9{)1vDx$U^0vQ}8KU3h`x_>~hBHDGw8}nFEr|d!02H*?mRzK! z6fUc)t0Mu0*_Z+bNI5;f0&>;QuWMAWWuZv64fl~eE4+^v(2pNKB8cre^}U?C1X2}1 zDF1Ykqa2yin&!mMpXY20oELvJXnL*v3P|BFzgmy;glQ4{+>73@__@h+m|E#aaanbI zOfEz?%WeQ)p4A3XESE$Ko6PI5%AfWQ_Q{`G{Hd{&zP>SNtKIARcn?}AZA(PK`B6@e z+j;@y3DE>osgG0HjaI2SkY=Xi^&{f9vN%!>LrF{o`p&V?y~AnB>ai*jZvb|H3LQ3k zVEQ5WU762>y0JyVE-r8q_Z!In8$ZImF2BEZc0`L z{ucaY-14yRK}`YV7B zP2azN?^n+xus2Bgb9<1Xf)Tut9x2q=%q4d%FqH9i;!*stXQa^ek`p%Jx8D%=OcrKl zgAQMx(-@0!R~p)bHILachOpz~V>FglRva`@Y?C!2kj7_@7F&G{DX?QCRgGyGsXkPB z4#~uE0)mCCof?b{5-0#U#1*qNvZxn!YB{a!v?t@HqBC{l@cDy-gVoj5otq#0Nv;Os zY3x>7Q%oBfRQ6GO1$OC%ij1OOD#laGs!xN+<8aI*>ZE+Q?|5$M+e9&dFJ4<>-z=%O zlApZol4!=0XkD1LvOk zymNnY-{U{Kb9v|eMgmnv%z@x-3ejKCjRHIOEv6AyyxPf8qg;=U0YV-0)xyPgN@AnD zq#7avI)7t==jgS$B*6$t^%2_xG+EXb7Hp4{!}DbylSojulr&z5CFQxTdhoEYu!tkg zY&i;@-E5A#tniNzUO7x_MRP>YdR^1vGEoAD=2^3O1FL47qmw`uya4wiRp?<1&rK#OZe^5<+01`+pJNa5Nz<<4zV~v-?zv=>nJ#>RC z5xw~r-lRx6n|ai2qM1({9RVjp&Ik`LmZzsDpi?j&$386<;xb}J>j(t;@NFpCpB)Al zs?!w5j_xHwF@-=00!6-1^lEnr)!z_7vE_qG1e=dhr$ zN*G1}`*3NL4!e)u1i|B7=g4Si%zSHYU2|Jha~XYFSnLk5w6X%);hi@$IA*H0_Ule& zT6+30ZWPs2HX^NnG`c0xE>Io+2{+G=tIrmxGdothr0!P`T2(jFo?OmZ`MOG#n#XVk(5oXix~Qd zrdW&UDnK6ujeuD}R(5YukvL@i%pK@c_)-N^pZs=Iy4pw=X$jS_b82Z#UWhr6>tKuH z;NsFK{Jb^DG-nOVn#ma+c~&+$C-EjKt*R*_{AIb#*Kf-tm={(O0yl&FNVm*{A+eyNm6C!YlKF)?5dI&q^Er-(DLlX3$r{r; z;vX7kJ4`-A{sUAu%~CBGiR3%`1GrI;-!wEekwT|fwd8Oxd>@hH-SuZlG5bJ!Xm+QN zQ_jN5%1S~4ZtUEZUhRf>?KTczFP`rRMA#tsu(M~Sq@-N6$HQFHDUgqjju-`6PU0y( ze)zCGDj*NVTOL1Dl*k(!tzK>S@d7Re&Xl81?7)}7XxP5QACFL>2cP6V+C>4cbg7w>O?otRo$(z9>8hU_(b zya2r!Yb1wN=4_C3d1#V<4)-Y0nHPKtp)Y*!eNJN1ZEk7#IVP2Du>f2L;QRHyHPwc$ zG?Mjizdk1QaBkKT`LtWeEZm0VeXtxi((dk!B3;}`dyj~XcTEXz(xs3oNEAXPu|fLH z-Tm0f$EU6QAXe+wa7tl*9<^AATfciP*{R^_(o!p6qNj3MyDy^b)j<>Ka{crRJ)2bK zsr>_r8;H(p59$=`YPS=EB7_>2ur7VhW~1MIeT$>~)cm3jb=`RjNB03oT}C;s0c+2>x1P1N>IC7UVW=bw?Xd(J!O!Gr zME`78H#av|7pXUWf_!{@0s?6q=MuD44a43iBq#>5B)q5UwXT(c* zsIX?7O9vsDDY4pGT5}DrIDe~wc%wju#VFCJS$*+HRTE;mu2%2`w#TRFh2a9w0BGL6 zy`03fOnUqn#kT~6gEv(#l7|vO9jql$%n~i~dvy~bNaiNh(+AdG zfF63UZW(TEWmUo`8_T+?$DG`~j{J7#mk<|jo7fE|dHL~q^;{=her<7aaaC~uRk|%9 zw%k|Z^wVryJBPF1G8e8`@UT}RK?bCvM);V8a6Lk);2!7za@+U&w|6%&wJbL}6^_NW zwzf9tXwsNyiDxRD#?Bs0)P6JBLnj0GnE3VUlpmq6X>I&m+kgH9XCe3ro~o+kp_X5r zauliCh~nb@qoY4Bhxy?_Dy09F%>C|NZ@C(}--K^ou_d*f1YJH65s_JI)O=Vlnw_zf z5vO#Potp}bty{$`G5s3<6MGNY>O=zyM)Txavg_<=T=fIG z=iH>{)1oS8;o;E)f!Hg$8k57ySy)QBI{-*dlx zDO}&uqML*d><9j{L1WChSNc}00Rhr@GR=nr< zi|BY_=ME`gM|CeRFAwE;7nO+i z)Q+zfn`HQtpfJ`Xo~zkNr{p1Wq*5I_h0($IH9C&{Kdqd5 zQ%laUqAPyv-eg#xKMv0D`B`j890)?RlBO+Sagkz%3NfE^5?YjlhfAhYZZ30KFfb4{ z!Ir(57GUC(8QPVv;tsW4dN&X!M)eZ9{fYHk8|8rq#}dxgab*rtW>F|%Fkvu9V@%uo z(hsWKB`Ki_JIKD!uw|v5%J^jz6k#%9Z^=Av{vR^(@@PYekX$NERw4Ee*@qxiF92>z~x|TjXdDB8V($cU-no7rV!#VlLI2aYfzlB%JNzXscUztgyRh|n}KrDlGgs9+PbwC2rR$bFcucQ8h zZl#6TDo3X%BjaD2B)sbE<>f_zjjVrPvtm>l&1ZW1nvTdwRa!E?JYu_-x`y-G&5^_0u5%v?H#H2a(XkjiM2=+2V^T zEP#=wt9zSmM;Xk?)8O7JJb0FXlZnf_Pmy5G(?yhCUfjgjEhkqXS+$dl&F=W8C^z>_ zQXTB$0&U42Dp1wotyC5kCdvlEVmJYacodtjy}{wR?29ovd(l&tDN@;N)XpuDGr>gNtbfnXIVL#{ zr+i^C_=+tAkldECLrWlU*3;;jGk5KIy|fQBEG^E^1GU!;19~e2HhEXd3oUC z3SJYdtojhb=;jVMDK)bppdF)1tSb7ynCFUMczB1dE-r2!pW2sc+5NK{v6w&x02Jad zSJG>RxY3U;AK-zAu&_O+#gq>V^Ycx9&%*LG!%D$sO?c4%!Q1^BMDV$y0NK1rXg&_a|9Y;J{d}J^_$Lv=S(TQ(|VbWHSU=5}s z*g@3nTtJ2yAt8q>yvzlS-oeEXb2citCy;pIDzWeVB4Me~&=zIN^XHcdR)KQfTP zZYrmS`8hYi%*-@0i?wsDNV|B5S%>ydfKHf19ao)n6v^Is!BxT}Fz_THnHu5-C-zj= zs*!%A{D}q_!q@VH)B^2#e)9rI<=ND5;Pxa3dt7`xm0Dk|)oeLH-MFDJ^YrnlN&I$C ztw5Ne#dYgv&S1@PS2KcH_uy#BLJ@5F#J*}q`2IGa;!~WvPuQh@^D9q; z1(J)x12@PN!G2P%L0aqT@O^7jVRg;~-xV?^GlYeCWl7N4*xEk*9pb_iy9wcDfBUfz z*dql8jUj7}bZJ}+!jzr(>o3>PQ3@VELQP7N!kIy3jY*dsLVPRp`=&xo=90(be zXI-@8Kt`Z)nD9r(`-dsISpkISjlxBKiHuczECyX>EtAPS{Rl$J7`i46UX8`dD*CS= z7R>UB`1_{d#+#DzW_n>^VH_($QRa?~K@f13Dw#L|QmP(7n;21q2|qMdmf9ch`T6;k zNLJ4_Q+OS47z~jO$gp!ILeSHOMu5>!;U@(6-y$~zrznxb@3CP6lSW2Hd^^a)wJ0DK znD&)8Z?sWNB`JczeO5R0?(}Y{)npOR#J7AM!shq%AkWgqY1suym@GA#cUy94fNq(L z650n)xKsw?qa)LG3+iR;qke=Jmr40BM0kk2ZAQz>D3TT%R4Vf=kA;y)<*?H<5( zT^*h5Gi#M)460V193w(NJ{9EUBhu^3AB;_2zv!BmwU(4bY?eyGsWSe=Yt<3gjBIk?{2C zrRnKQz|K6{3|#X?`7AGAnd4y1yQdaB0$wk4(t576R}2Y`0Wp+6tH)vd8<~L$rMX|F zYtiO)kjTAvJJ@oNelBib)8_N>H368_T<(#PG_qn&6+~An+l^rlr|0S7r8QHH>=!OQbu~a*50rop6!$`%< zT>eENc4+Gj>(c;PswJuU%v|UpP(c#fWyF=>dwlvk&mVx54MfEuON}}!QS}lBJ3HvV z<2L=~mEc+N+Gz{9dtbPgJ&e_z&Tf`b%B7Qt3%_0f%zMDFG36}u^_RibM-Y2~!~-R~UwLtFd%V;HGDNIyZzmkm^V5v3^@cF<+Q1W2-1q}( zO`W=&iRf1Z$zqa$5jcG634xp(ookBadIv`O=zM{e!{rP2p< zJ7h|O@s&*@`^kv}JrAd3OINxQY92pz#y>tiZuzW?BNAzWV zji>1}nGezFha~lXPNQx$x>+$HAHGsaTV{G4H_3b#4P;UxG!$g_qc-OXKyUl)iP8y0 z!qT{CycbT2Kxh27C#QK8T2c6N42&obKwx{uxI{e1CE+&=BZuBBRBujPb?6SNZ#E*)~dH#eaB0pp@)UY zZ671h=`e+l%K^N&z{A*21Y&-=M6=`J?@)MdsznM(UINK#yat8++cO|PMv;s7vK|PG zodYkM=Wb4@oV>ZHPZBY;(>-U#&fOa{zqbvPiiU7%3zh+xr5jvtpkF>XKbQuxMz{Er<;w$)lF#0zL@?6@d z+cCeDMu(rB`=&Ny0esI`u&tHMUBKnATh&L#z|2gRey0THc!uwe?0nshb9)k~S}Cx} za#3>f9Q)zF-bzbH&j1tR^WEbU@(9---s5nFr0-1hxglgzZZ+{SLTPCj@@(JB7V80I z`lOk%3kQ@q*ws^I;oIK_m#Haq34h5gA8NRaP^F}&6HlUJ^|_bWZ@JaOi8{_iwD{vksBPPkD~t~2+@_Ai$r`)?D|)l*kGj|Ju{J{mo+xA)x<~ z1RJ+Y0O!j_F&LNE-&y#!xV6`a~+=DtR%aOt;+b zv)Hftvp^{WIG(I(8S)MsSlhn+OfXN02ZmkqOb;_4J=xeC`D0;icnAv!++$%qt?!pq zOgncI)h5iZ*Wko{n^*2-RNnGfIl9vGd&B@nk;w75HTI1sByz^r9Iu-bt2)TW9NGA+ z*yT^`grUn z()Luy=xIqOU}SO6sNZXOV5QRs3^Pf(w)%CJ>cQ+LYwpba&w&SajVoJ@$boRbGo;jW z8qmk$DEQRXSA6e}K$+Y3T%|F71BVG}_*d*VVIP2kYGeeH=gK<9SZ5hFEH1a`9$n-h zjr>yTtUs0tTA)lU?0w^)s%Pw)zRpBYpMn|}18l9Wfq{X*w7R{$-CuS@JR=Bv$TXSm z8=&C4ghxvEJ9Patvv9ZTED;eQA)&my((Z`m3{{?sgoLS?q@<(82I&P3 zc{(A-KQf?RArR>8+BdrIx|oi{*W0+-fBA)qV9-A>(sYv>p(ZuoWTh#x+d_ey0BXl1 zfuZ-=g?5daqUjdw6p6Ie^e1h;n5oS7)u5}~a@<@3MZ=K=hGx()A5Oc>Y+0cqQJZ(E`JSfBv3=OXQ-#>Fcy6@niIfCpKx z*0 zbG_d50M|?3{t031;VcC_dbfELO>pRULxq8jEp8hNWlg=Nd_o zzWn?`H&^H5xvHP!)gR|-3xa?Q18fRo{r#+38TcmNbMFL+B?nV$v7?YKbDbY<&H$ru z;LmVzaltk{k_ZcpmXnunIqM}R2c-qbct~TMt)y8Lg64cnbnyA>n#juiz_4ovrhy`f zsdSN1{p!-}o-QZzXOTO%ShE!D`HY-Cmf`*ZsS1$@%m_;|0hh!-9KCGori?}1gk-mD z;8s{zxVU-jdfg~w+c5hPyt{_RtDJWyZJ**r^po`*2acD$9$^M{(1Le5kWA>@vBFNP zsRwc7|8Uo=0XL4w?VorsH8M|rxj74ma*2R|@O<8dMw*cU(_v@~Pd&jBCd}uKMq5oQ zpZi8k&ez@}Ja2!YS24|OBo%*8&+(=u-RCNBd&X7u1GQ|Tsd}QiqPjCMfU8c!g_EgF z`9s2W8*G(iWuumivEhl_Me#-j&xZlEwAC1y+B`mU7X;IwLz~A}jb2N(4Si6M{Lef= zw+U+JB2iiCG|E>9xvQj=74}nEgHwRQIm&D(b|&YI!^wV7pC&*3SJAbCf16!i;hqtXHcb2uctnsG3kmhR91GZ$W&D;y#hmuI;@W20NBoV9{wE-5Z!rZLl7 zru$YZ`K^AEy0(Uz`iz>?=Z*3&Fj8pWH$Vy9&74TQ&YhlA-Lv7H{V{i)u2<-y)7L>uS3oL^a~2o!Dz=? z;7xFF(zY3>jX|Hw1jkn0p^9VAd&--A%HtS8I`KT&MEs>+FKyUXS&(7-96!4rFL~VC zG@Puo2~1|_{^y-f0_gCfdI$kFBQEFsH9Sobky3uT*g$=mZkGE)81iMU7$O$!@weh_ zMMXulSBwBuRV^NgJIAo^u$jItJXlB=_^6|+%Ro;LJcmX$`WYD|niWd2N@V(Kc*M9? zmu0n1XCu_u_I+s_v@)kch+LB#lLW&e_y25xa&my?I<_pYf9~u6qq!z|g)Ks4p%OB( zijht|@LX|dA|v+R_RtX0!$i|tV2^Cvj1mCNu=^Yug0b^_4znfDLj^8*Sl;LE-x-_e zUKqYv&tD0iYAv9>|9{=c=5CtG3JQ!&Okc^4T@RK|Wz&MGFvVzTY3Ewp>?9;4I^HHC zJv?OyxXI;e>FB&BAh6qQw&_lDqL~Y%v)k?tMb*!bO_K0boFZB|=lu$LI+K(R(3#YT~; z-zz!)-CT~bl_(}(g+e2#r7hD3slE9!YO$ue`mpWk7>yzrIWXqO4_=GOynh`!fQnMf z8NzcRkOIrpf`RJ8>-#J)uyAqV9NPVIVDqI8exCyvjk2{mNx)Z(y#a&QpN$A=wKs& zZgLUrggDM=^ZfKk?t5dTsycDH-mT3R+xY=|gy(`Es+Cx#q01>3Cd$si!K~%|l%H(y znt*(EZjLrAuE1jT052c%ufzVYzJQT4hHxMhx%a=}LYGjVo&|$?z;z48aSlSM;D$yA z1|AR6?7+Zfm4m!SYw%^QQBq5%&O6*t;Oe%#?8Xwo35)gZr-1Jv<%z7X=@Sb3B(5Ta&?Yda?yPmX=!OmN$-#Y%ZXh%D~fXQZ1Z<5pEH_zN#4JsgzRiQ$Jl^LM;0jdg&+p2O-AzL>O z?Vu~V^>lv%4lfspGX3x{oxs4`;p%Wc6;c)S!Uu~t^Cw9TcAEc{X$Gmj+meI1jSUmO zNeD9SalgZjcRMiZ_3W@jMMV5$o#bS63JJ9&{en3hJ6~hQ#m((|Ij#9vOfYV24J5x7 z*CTdxet^Xq8+oZ16)ab}aU>q`8pq?U@xmyOwY#eU2X@*{=K~YUcv4k$bqI309m&6W zxVdfTDs!r;&f=9tjHuuepHeI(k;9^+J2poJ>>Ae*B?<$7Nf6O<%oX!}OY<+uC)CGM$`E?PdPyO-t%P5Ec=?0#BI!VGm=C|H@?q?eS zW&m#$fLr!c!P2H3MA}IV^qAs^s?X^=Yv|6PPyBqv(-9dhPf^N7R2$*{TD!!EeVsSc z@lQlz$|w|YVa|XL0J|JDbUfOC zk2{#Kh|s~LZ(G2dY|tFnt})Bc&#%eO&u}G7WifIyA08MW67hWiS`C;>zSCYc!T?^# z^D&W%@^S!fl>VC*LAxH)atdXX4NYQ5QdAxx7)fdkr1(zE12|qe??GwJr%4M6?A@{K zGdCFi`hSi~AOUgH_8~lrx_TLLT*u^u`%)sFI167M$AT)8jpuu0S%WOPCBo!YbEms%~oHqoM-juy5V|N5KWk z4zaO$U+59+AK1>Yk#+m;WI}i8PNQ@Hu_-H`?Css2&AwHUyxT1E`Xc|&vkd|oV`?~j zol$$IXuc*xXNS}F`ROl6u6a2*WQjgS;4c!SlPE&z85jV!;c(gK)aOws;VEM3BRX-# zTIuTQLcx7^xY$rpR%Ro*zkot1oCWkJ@5j4KfV@G$XFZePA?5m=WM`>!N_jF8hE|XO zjJ$s$3E2SS_c-6dBIYgy!_zf2HJV-_CzxXer;q&+FTVV;hbrTl+tQ8VySio5oty41Y3W89X%Q3w0TmGGPNhMl@8I0;{622hob!z_o*Duj zQ;VCRqEaYH0kv@Y zkyW#n1$nuBK0eYoP*xsyUF?$C+S3#Tz~1b_cYFd zrn`cooZK5;hxy`kgUsv{P&6Vzr_j{K$7RWoq}=|gc|Op&j2NDMoF zNI%>Uss*J1<4yOCYNEL~Se%yUa!m0Ur5!25xtE)wh`rP9#>+d&dh`61{zh-a( zW=vV?D3)f2<^wBkvf7}<`05-~Ho%T2=8}_;*whFyO%K+6m)8!#E78@z#`^oEYNVKp zS@`M?nA2V_@dG#-DE79p^0yDo#3h>%&5M^#pQz)7lCrXFL`S`+9ha8vuNdUq+)hB= z3B@oTIg)G`0QFy5pv@>RXUFuLZY&3z|B~@@L{oyhnkGrUIsr2O+TA8nDzWMRubfaO z;RSmXY+ty7+F!C*6OtBJ3A1wjT-J$tk0)T!Z72NLFa_7w14M)HM56+~tAc{=spCR1 zy~KqdDI7Z50p@dxfJ-MecfotM)%XI~k8=t5;w&?Ne*b>(%QU`Pr|zC07N$AGSUAXz zHG;OUudfdTroV#7Sq|KrBgsr+UDeiAk9_l^tSGTlcl>ni!umEHAFS~#$^`6Cx0RFKO!MmMDyU(4mKJ-%>>GFl zERsE0lqPzS#}QR8adS6-2rpI2a0W4b4xo$nE$zj{($6RMlNYdM5FvoEFI@g@X<2Y; zA6k3#rre%l-|!q-O=Ci#(OdP9$AVwWQ~AwUqUGD)hi5Q3S*@QA6_L}jROuMeJk#X1 zl;s3Ej;0MD{-XXL`8Tr1=I4Koj~CGo8lms@v{ryPcqLvG#M})Fd#cKciYoG`vf)!- zu056a{EB}tYDSpr1CPKACS174!7$fWGc)|ffBy-1EA|5R17BS(6EOL1v*|RkWvhSu zG>3Uve*wy{%bD&gec%KZNEueOz{-DCgOGTORRsAVLSFSlYxPJKKS`gW@APmLsj>Ki zCa=TmC|hL%Fyt%$8?n9c`&3e6jnG_Izv}h}GogJ89Vaz-Ig&%n337{KYs*iv$$Xmv z4Ms*s^I&^|-dTaFxo<2?jI>WoOoWCOhGUVeqgOIu?W`Ph5qsC)pEELta~LSTYq&PwaK2W7{ta+Qj|H3V z(Nn10-sv?xV;1f-8ZZ0xhS0f#m)3VJP;3!S*^9+s>?bas37U`K()2+Z1TSebT#70g z94Hl+_m?~wg-L~av`^@oG|k`YpMMO+=m(hrlr(VUd45%z`Ses?!^`Vz;ree&PP=<~ zW?EuyA|j7+za#bXVFRnBr|lTmek5L!`RK<_n8@TZvBYkm!oQ7l_|vZQmV%G=>;GbJ@78@G^Z1RbWfxh0jwP~XCR?}@ZiDQ z$tO0#rseU^&!;LwF-DIb)8XeieI|mX3^^k9Ek0^qR)01no|{i>Kd=1zXO0@RZ?$N( zaeREguB?-kx{AWNtO7$rVkhZEa1%Q{EQ5y!r(|u6-0&cQ@h7Z<4ItmmKF)iH5K{MM)K`;AQ`;*2n67BjVRKb&?Qbt-Vw9 zc;+keT@K95v@{;Lsek;?kWV5eCItaGz;hp-c!>-)Z3-b z2XbgL^je+G{r#^uzZar}8wt?AUwKBivrm1A9*-jaM#$^%)^CxLqNS-xE&pTDsSoL| zHKa{UH`y!%-1tez+1uNjm=tu%cDiFN4%{!k18FJkFKeJLGISZ{2Eqpz7{F<>+EG5x z!9mr1bA3VD@6|bfvDe)VqS{n4eg^toxrGl$zAs)(0_BY>O~&u6rJ(RkHnQ5j^O4X; zX@o))6#eTXIr`e#>#hQjKnB_f)U5js7_P7>E1p*Z`?7oDZNT!v+gqUMl7z{FmiUr|*F&py$C4^y- zJR}#QN0(4hQL(kH>WSGjjGg$?Pj|*-ZFmZ5w$V52D*$FdJPg>G?d|NYexjlVicvmk z50}D#H$YKXVGUR?7Rk?xj(N~>GuETH;>ZVdFH8HMdPw$?OImer&%b=F7MauOsgB!XKX(qqH~csGb4K7afHw6a zbA^vr@taAh&`{0NpG!3iKFZM0P^ig3SED}sCw;JqLA4(4}x-0{HTZ@PWlhsBKm+QUD`Iyp(E zQGRE;# zVE(tluyFV0kqblmvt z7WmaM>CdPCf?^=LVex4~Xh>~(v2lh5t!r3UF{Y-1BEj1+(YX@l9EmZk&6zH?(6luC zO}2#EP~;zcR@#D)ToAd!Jbxgn z(C8k&kNeMly}JY`l)GY=M!JwKa0#oD->-xU7z$3#F(bCJxl8M#?PFuf>Bo2cIF#if zM}P_U0ca-jMtRXrRD`qPsJ&$wEiLJx_vGe&jzHdNuoN+{yD;IV<6rxrPS2H*wA^tx z$jFSZsHxd7`8y|ggVRXm?(#!l*KvRExD}Cz<_Z-ZtWsiFG-E%1!ciqmjvc#TS`}QX zpVq&+?3K&P%-jj#E#QJJGSBTzosTdJet()3{9%K|k^ zF5DJybDf);+tDE;KoNXytUP6N#mK6xib`lIj);nmW`(^3jC9uUI&}R|cD1(7DWGrt z{#`gz4KE9>81i^D(?r_Y=Zwk)Z|S>^ha-s5ALnkYkG`t)J}HKm1K|caHriMpX7Bcq z{i9d&GHA5?KH2rR3I3x#LgXzo!%o?w8yKj;6he|5e0-ju**{%a$Vp17hj^4e=O;bL z>`ZjeyJT4~5dF?vPWf||6Zm1An5H~gaNqOVO}oyyO&Z4l{G|B$W>h>9JG{5Evl9>$ z-@VRLD0T3)nWQj37MPWUc~05e4xXUpZ1qPFjy=Pfp3UjR==^f zHV3`M=|X^LPLElWo*hrtOwPJHbm%2|)%dvtoByoTaYf~DRo@|HW{z}T{ndvrj1~S7 z@>y8Yql>vY$51iLpN*=JF-EcDOXHYkCnvkW`%{n41S{;)lJNlMUQa41g1862l*o z6O)k*4}NtnZEIrACg8G-WZ%%}{yIDigzW^J_~2v+4-ZE}Lo0Qd7(s!O3$9PcZLQ8J zTNY;K)Qwmhe4?>FV$Sxaq9QY3`=E{h%Co^0`NKSovp*kyYuMr^<4=euBM`1zc+zN`V~fc1O16t%0M zk(bjm+qp$OMqfroy8hG;iTtgLZ=Kr(@D8L`{5r%G4?^|y^q`8uL;qJZ`!YD#tY-%nplkHzSGS2L?)I?qAZlKWHK*lQm5yozBYwHWBuxd76kIT!78M`JOc1g4Dmm%m~VxPm5NHWDo z@u5wEBufBdSr{2P2{BhzRt(>HsK^Tq)q^$=p0io68t_TfVDTw!^AhYviSZnnXQfmj zeAy$FJaz(p=e9r$MF_%{xFkbJ04SLsPSLi#T}g9v5K&^8nv zyolZ}@`nE$`DTCpdJj*((jOlm8w<9X@5a?Q_s7s(lsp&<+eLRFbl4e>kAuZ)e;WSA!RWg_zF@CKYP>JIm{Co)k7v{R(m***}GV1_}34$4T^ zo#`eh)BtCMQ`SWmi;j*C#6_T1s&icWn4L|^J<1|1M~JFf$S(CnHhO%XF6@_W^Cw08 zJi$ryv(u+&@Cz%|mac)RKZU%J5Y%q#uQkuwOMYc9@jWy(VIws3KTCza)z#g7#50y( zf}RkwIZyQF^IyOl9Y{!yfXFBw#@he_{9HXQGr0-CLEthM@>gZ4xKhctrTr4JnS2xp-6hU39vXc7Zg*DZ(iF5v37>t#84eZhJT z91Y+HoO7^U;1S?hc5WqAL1RTHjb@`wT4o7X&?~cYb8#~FDW0^KVyMqGh_L~kLS65ej z?7MHI#byIB#6EdL7y+bcsp;uuKBJh{ig7O!?PU`hJv}+OTBQ>`n|S9h9lXHDlvGlR z4F}f#x1QYj+!WmKq12BU9deG1S-lrLU$s=Cq5)u@ zaCF?4#BI4@@P+fofUPX*_km-@XZBYU0iry%vxEI z$M=IZmkS{iH5sWP5=m%>_U*VfUFHCAyjKkt%J%lVU%to%1l;^lX)P>C3dg2^j=0V2 z6AgZ)@;Tm@Yh-rzc3-I^1Aiy=x;bM}-+vG-mRJ1=sR1QI(D5#^_`ge}yjul{ii3?` z`{v{E5W#*Y^n5rsA#iU8{Hz$E5)G+Kp9oJ+PbnZopqh6X(7J0%;RAUEp5-P6kc2Gt^n~Afe|~Px56TB{ z_t`Mkzs3+)u348GrnuP;2O>z-+dE1ybi+%k*V9WfRlQ_R+ONOy46E89{gVlBD8um0 z2O#5%ZwdQd#XjH`J`*#7ukN50Sn8v5Utjt)_-u-)c}5LIy*MDa&%_F7$S?FBj5&_ zspi4KVg?q1!k3|ztg?ikbdsn-Om$0pGg32JA$tN8qPn`AY>co0)e#cFVJ!Sx=VcsZ ztfhBmB2bp9bq>Ys6nB99Z3m(AO)x?J%@>+oK}~9YqDm^N4jP_sj?+eLo*Y8jm9gf! z>`H`bJyOpCOHC-|0%J>Rn;NbV63}D8*U^1%x%BOu8oV1@nq?db=&U>a{DxkD(mhNf{p<*kFs^if4zF&0kQ3fT(Azl*GNWb8m%b|%N3=HIB#e0JX??#3@;S3UQ zbaY#h2hLpktbb3bb0vKugaBEac)z_1Clz7MoB5cpm?3W*buQ5v{M)AC0YMQso+Cts zo-HUmY|+FvxMkHQ{UQb{<4L|PACtxF4c|Ye6b)0h&?iD0d;KULA>g!(JbDD84DHI3 zrV~!ACVwD#MYZ(XCLM56$vNQ|Cet`OQ0CLr?E?x!{>h4 zzHng(tctIRdmC!|T$k|*ggUpo8>$BiGEW$>DWL}6y^+OX*fw^A_6TaQ9Fe*I5Xot| z_Dy=`;jR+noVrQ{w5SphQ?`)o2_hs#{y0I9Emx%^Q}8>3v8AxSUc=zIQi)u($?LJi)xt&Fj0ajn%>VtFgw5BI_t$X zz>GK{zn0Hvf_nbDKg=~C(ngT|pujD*RP%~O-$mC;+H0)U1^SZ%rtnhrGlG zWYy0N+jV7So2?uEF1@k5tuNsQeF&CPWgX>#FUPCv>lS97m_ckJBG-vKbg`O0!`aof8vU`cs*rV9TT_$U%Bsl##v4(06@XfPj1f5nhk`%x_YNg7;L(7GIa z9R+~Ok<h4)LA+0{$uEfVjS_ytfNjI{ zA9tS$Ms8NtJ=fkXkf{c+*+3FF;I}S-p6AZbL(ih+QK5GbiOcZfdrHX{9z)W8KtF2q znyfuN|NWV2aEe}#Ru^*H)+!Ha{wOQCI%itk83NgX0PsMqE$K|Fe%DFW5Fz2-?l)xM zVW8o5zu&2`Fj2Y@*;(NeSL}m;%M}X1dVe!N=lV4Z`s&V`zYeH{iHRyaIG>8PRj#lJ zn`FarJcE0TYNx7lT}K`A0+t|qG8ZS=&{|U4rKqH>ZQ7O#GA=VPWn!cm^znXcyk%Q! zp4sUFY~HWyAerv>${3cIPZ$uoHSd=nl#Z1%M~wfuWBL&hQ%8x;lVu0!fol{~g6Dtl z>9_$(gEX4>Ipg&6mpUI29~FAaU!o6GTesvjqr1xAa{5bIX8X)D;rSbCFSs_(Df&K#x zR{AI$4?Dri`hWpK%rGeEW7sy${QT%y{5o9UhfmqoR8(jrbFz=VO*dI`u%gB)|`4(29Zh->!-D8ru3$R&nQdkCt`Hb!6&)auCNtoT2ySLH`LkyF%(!oZ#+z zfYDLJ#iAUdi-@u&*W(=G# zn9TuiF+|Ges;N_vIDps-mz;H9F`b#4C>B7SSk)M%>ftmd-K6w%0?i|q^nFG}*lbIV zp3u%Gc&W<40fs~TEj1;@ca8BCJCSlsiMfH@VY+}8b(>L>-Ypsn+}7{vKj`@o#DoKclzB?pto4^qU36sFBpoo8q@@ZsHo_e67 zs3_>Vucl}dJzsfm89Ty7sK7=DsX%9tBt`ll z6wv4@?Mi{qQo;zDoZ#YC|Hcntk`bUE9^N+tS<+`?R4S?>DApm0rJx{a`e0}W!&*;9 zw_(`AlyDuA$G-Wim@L=hz>L)XOmEztA{a;i@%BW5?Yj9wQ#=4D z+}q&QXvHWixeD8^rR1AKefHAfuv`&dv`logM)e&fylZ&Xsn6zMG8vH$5B#n z32xJ}q4`wfNj8EdNzF9mf0&%7)8w5>h`*(T#_)+{O@NTQR#K+$U@k{6pyQ<9n^7H9 z`@G#}!;CfWpAbHv%GE@>7*c+&v7M($T!n67Z4>XmWAkdJ)n&sr7A-%n9q=o-69I@G zcTu+^D=6l|oTI12#Fq~H=J6&2pbb7Ipv^bygb|Y9$$*}dCjI53hv9`Ndoh>#+Oobn zRX@cjYr%KaIf>Lqgg7liqFOyDy7}Gh|BOyhmSM+;i%PKIuAs!%&COr&0gcZmYiJ-G z$fOlkWV7v*ME^&I8NaBzg`XqSe|iE#M*+I{n*r4$Ikh31w z!PYO+SyM!*IoSJ!qyp$*$l$r|!S;6qHeia_j2J0(3ot2AO9M$@sW7zWEU(~7k!+0I zqxQV1zV5&l5)Nu?3bQ^y9vZ+1$DJA+dz#yP?4MP_3tNlVK!4s=gBHO_xjTR&0Z59? zI}rC}HVKV>Pr6?%NN9r7e}E}@>A%o0DpYxjHoxV1y?;QFmX<0bp{OmNLp^XKHhAGe zN|5N8mb0&Xxrip}xm{igTX7?(s?sQCG@D=Z)vIn8=g=}!T@|}n-eo#H?Ug*BrLC>! zvMSeo?ZSzQhIR`)!zH*?D(S|=N84x70C48a;!&<#-(L-+`(Uy(?C`8JLKGc1N3adj zk(xe}&=8~<^K3p?3=BR;`GQUby>ck4zwy-JS^@nJlrOAwSL!PYO8daoK+x0Ktjj3M zy}(w^uq=tm+nbKncL@XSA?e!*2Iq%nQSBHx7Q|-?xK3M_KOohcjgvDuXZa4pje z=qsVDaO5N9^bg?~R=7cZQ%jGJKdj$qndr}WcNtr1S6&t@V%04R+pc_s#hjMgA+28w z?v37t=l^Q5Nv~l)1oDsEe>JiE`7bRb8iE?jtNY%-*#e+p1@yvXiUA7M_}G{WEd&h)*_eY%D&kSn*WJl?uT(3^O_`XtGB;0N!nF;C zo$R(}_pWg$omHP~G7U$C;sMEvjPqn-yPPDz6+3~0L!ixOlI4mN+B;~)Zp9+Xw1t=3 zgEpMR@%YoyXZ`8d|Hj8uArbnk`CLB4*x2~M`Q{G?hj*u`^&^>9+{6XtC*8keCQPo5 zZ_bVn*M>}|-&$k?sXaZdX9u2EPPb^>n6ToZbXb0yEtr%ea6AwMQNDDaDkhC4R3ePg z36sR^S1BK-r7g_g?TZZ4^j0=0WM5%!nZ2crvN1Vyc6p@7c@2JuZ@;x?BLoS?-_AY1 zcoq7bp=A}S5Qy2mf+oikOrzx`ip8=aY;WjO6P;`BC#&(LWMrJ}I0VcXv2A)-h9oR4 zEx~POZTD#WsLYq6KZM5e;G;CaJuWxOs3Me+Xi1SRfNN}2QKE@K@6Z>)=kqK z3@E}x$GP9LO%$tAGFNXNU2|ryj=SKsL1t~^5_qaC#68?+=geV<`aRZ*>Cu*csWtYqjKf3c z^r4d%C46@Yh+rYyo$F;{%1+PBe0K6yhVckJgX8Jhp?^0#W ze6UUkHEYDg)5JEtHX6==YhPjfJAD+0)29XX^*_uw(fR(`TV3eLTUiR*edwN zQ$o(NznkAd&;2syqKqH}@dy(&;Iy4UPk*L}87oX%Qo%vw3Xf7kjgORv5OdPdr@F=; z8>$oKjZ7={M>{HMlX9Vk&lGZAaE+!e|1iBPmJM~)gR+KQkh!h?;60zxP6%VKTdLhW zy&xf0xw!HdPC^Pv09YSVp?>r@yg`jaX1xCdH}SIe-)@`-sM=m%PP|T!i;KIRT@plD zHkQd{AcaZBLld?9IhMr5*}1VW#{vCYy=}h}PMRy|0 zAp_n(`Auz(1opB;%>ta0WKIGtJSRoul%HCChnGyeg1k}KULI~x+ zBXR3&>2az@rga4!FXK_}IHAIW)s6jfVxbiEV6bjQ*=cIzWTJ1u^!h!HM`u{cp#mWuAC+FT9h*xh3re2G|KP2^G`0 zozc1)b0Y5J-5Z1Y}vQs)RP)CwgFlz*+$J5~wC)x&xt*TD)b&B*j*^#kutFzUgUt04GTzi`i(?a31=W2Pr* zIhZOSR21vJPcD)-`kSmU(w}Lguf&t-u>{!9oMTa5_T63>JXx`tv?6C(f;7*!!LKc7Nr!G@LO>aB{+*LPA2~B9c(1v=n*zsE=BLq#e=` zb5u%IGBSV!?&<0Ia&ix!{UN7R3JaOz_y>(BB%r@xsR!}_;J_reQakl!BO^b3HG-<@ z7|poPS!e^}A6j_Wv8R5-AwflynfS zVLxI&F!P3#Sy#OeeSOGH*rtSO-i-rm34=;dSN%!(W_4F=_71F20L=0EWFteOLkw%N zH`(50Xe$BX_>E|Iywc!^zNtWJV`ikKx%sKP!&53VBcr#)#WVseNCTSxiVi9gPoFV` z*}1p(Zhf=D%2)fcab&8rnuBfbVqnA3d(<&y`}6<)$^)IWQ~}F~pVTq)2^iVM0oiOgKujMsb zIV#SCrfxdTL0YH-5nMn@=Y4i;88y)-D--fHZ03=uf_Z!x~LzImWbf!2+!hYnfn()+aWT#`gv) zU!?MW&gJ}DR{7M{*=ewKlM)?P)CoQmA%}?S>qeoa{ZzAk0`(`=HSbh2#!07JVhxZ2Z!&%Vn4jxe}&=bbQd;n3u$B%FHT1yQ#2JQ^Ul#^gD+fmE z#;lhDqp>64q2y=H84ap4zj?nNNV@|KXHD9I0sRqmKt^sFlLhTDhjiU|_lM^x5$t## z!mH0KUe*r<3TV6|PcS)p+?ut)pR?|YbbEyz~+gE}6m@#!gDxlM^cwI1)*rq`mc+R8{LSiWnI> zTMtyVE?@r_O(-|?6s5<=(bF?zw@95UkV>OhJ}&F*9^W(lZ5=HdGVzpyo5RLV2&y3p z*lLYD6nU&X5x_+fRwsu}L`42wbv9Opy7#S>m7hSweffMbPg`3|9xYaPvDwEI#I2f& zk9RgQ9^TdH_jsdpTc!2iWBUxXv-Pw=EdG}F<^oR^bDcr276%81PFi$aLQWEmsT4yn zj{pzx(NHE%nPFwR{c+1Er352e>eWO<7X;&E_*qe2!;y@_^;`KjP9!Q#T6~`q-0boD z8PCRHqSv?(#|ew9f(7kMwS@G%yu4qMr&~*`h8+95!-!hdPKi|E|`;|cylF+}xz{I4bk2TI4A`)oy(d802J_m<)Dj1IK zbI^7j#}tg9CsL$!f$q-L*45FGJ}>}B{$7NS#(#+It*wI#q>8Fa!3>S^7@S zzAwLi=|EVM`(ssIvBq&f=veM|S8pG9Iy=`-)UfPC;!`~}NhAmX<<63^L1U_6UdFHm zgLr>gx|*vky0N7V6Q>YYQSuolLKQZ0=)pmxkt2siR$R&4MA#`9K+020!l*)G;^XrK zJ>r@_)3XZ}rt+xcMn^^6Vw)eVJVMm0tjdQ{D>}pH)da(J`67;x5G>G~k#>W%d>M4bu@Rl`$gf#*B$Q2fZ+U_9C zY&=Hv^OIo$kQJ-25kBIe`VYa8p=54ej2|7T6f6>s_~`V~&aPSoW6`UtsHktv*(zF% zy8EtDihKoAZ!MSwCjYO1#lO533%C)3eRhnVf5c^(N`AEpWe{};c&f&&J>1ODm`_VW zdYABq(nnbtG7etkQG*f?!gwDCZMF;ht>#mr10m(+M(5V%zH}Q`z)<}~imbkD#|J&U z7r6KPzE>$ix63*N(Y>%RJxx3JnH{xbo75j{P`NeHSa+z*p!Gf88jJd5yLz*|Ug3=N z+5(0SJ(spQd4%H42Z4t)G@lw*UX3lhODi@JN5qi}{~Q}D)ciPS%R@jwpxUVyG1o&* zNJvOJUa5^1L3i7TQki+0MQrd}@iqOl(9X>jN3YXif(o;SY_{fnN@&K3n+dqZhH{9IxVRB; zkGv}7X2Ud1jhIrOH2gr-2hK+8;nToqg4?z)e<{YpHoGld2`gKg`zRNDVH_E*QPVII z;P1{{_^P_7v-LgMG2}#1piYj~hwWSR<59ligT-i_GY{N!8QZHWGgrkK-~IRgM|a_>osizXXkv*7b9BL`N9wzR{e)09 z|NcH$gjx#(J1H^gQ@#?Nkdj4nKixLhEi#YUgBPW8ue82Nf+T7R@b3dL%C~-EHKJw_ zTyHbCwss&wrd?&|ZTB~@3^YttbtZRh91}mJ;t~*W-~48r^YhLv>iYBv0zI2gz2Dil zkx^@Eu&}G0aAjO;68f%Vgmwk+^vZN`c}9ts*sOX9vbD{#J}}k-+z*N|jSk=#0(#Dr zBcq_R)%At0o3R+vCE|?a+xA0p0pfW}ul2+k`LJCHks{RpKS_P(8 z_ia`gbmkBq9@Dq0rHhyw?^Pfr;2{j*wqcoYz#?2Zb?+oNMkADvjfl_)u3A+p=Q=iu z0cpX8$Z2a(&}FoMXXy{GUMyY+0)Iw`X)X9=yhdvq!7m`t<~Q>f!ql%y%vKe@(HgHk zRVb=TZ2Rp`2KG9C$qqc0hs22c8*3(;YdRwW?9TnU7;Cxc=#Yi zUA(O-A6T-Q>!)~5MGMsY3ux={WeD)idxm<*e91 z2&YrC;m39?82?xU&Z5EAR7}qJ9XnY3-*)e-P6$oG3KAc7GUY!FS^G<4<=|rCyA1r` zn2*I4V7*^0l7FUZwiza8P8Nwz6-1DXWT!@zLm`3;TO&xD3+_=~Uf!`$evOZ2G**-z zirQLQDXFPj=ll!6g8<1lbks4PAYX=2V{tD#JMVsYFHhE0emHq6$jgD&auDVWY7`$7 z`1AMT`f35bREZ<~Q#=yu)ExS5^f-|-Fyw%p^t+opuqdFHy07eRWfyA6!{jVX@ZiF) zYkHb^p)bT=u5HH#s!PJCy0iBSv1(qzE>AzrwIXVgb2))0;!JB zXk{qZ_a2-Fgez+FtIqs;?R)buI(~HRQgFBd?85<_7rXlQo!kYBKPYV(w9=8>y+e!6 z-6zJ!J0RuG`&|X-&FScPaUUoL$eoKWA^SU8?vKlNVh;VMYrI}MhvY&^-2DQC#!UF! z8rs_kD@o0#^)A15+~`!dx2Yg`0Q{+SzGobj!wMVHa)L46Gh8z14P9Fe;KRrIbiN+R z&Iw*eb`^GKu;|`e&#Ka9tQIz3b=C|sFVrt*8hX+PD%z{d4N+0A!y^B3_cZez_X{!) zU+1kK3f?m67_$>W;KU!W^xmlUFkJX65V8}A9O3gL5J5~q!OF-8($!q&&^X-IlrgFh zMAq>1Gr3?OENQYP^pxpJSr}d6e}e17b73LxhO15 z@Dncl`^o#Rb5~CsVRn)-`W)!Nb0zh@f(Q^azH(UT${~KV+4xLC4wS(+XQFwabgSt9 zuQ^zr8+WG7jnVCV!ixQWw4e-}G!xcf*Qn;;l@!EAuVe`$9h&(0bGvhLa^8R5q^^R& zzo<&;f>f_2@_wCOb^5#RY(z;{mtngwU-Y-?pn4Px^6P18AT$c+qqr zMD`eYucf93oDIDSlMuj9)jaFE`Ww)8J4@D|JqA&zvZkG9!FHQRmh}4;%ToSQo$CMSIJJ+NuUN zqTnNEi`TE;={C^9uv5Tc3kwSmoD#Ln=S=EuJqTbJxB&}ZEGjig|S!6O6N!6*^ z?#rjQKy3neDpfPCuLYnkd&X8Ly@B~PcNc7pk? zec`G`$VM$-7yPd9Z_j*1%K2msuuXKO$^tvakN5wrh;Hxg#rJyucZ*LIm2rw_AT!qNdg#n z1kB(Qzap4eP9V}HtR`xj*#WrmHlYg$oKzTutUk&*+#!$|l|SX)^h7#06o9Rhxjp|q zkmGaK7j?HU0;~n`p$!^|n6i(h2Cd?^+dKZiu-gq7CiM_C*hvQu@fsE+K}dqb#}^tJ zNy#~!P2&dV`tl5HnDsMcvhmx7Jd){LLPDTce*-d`e9CJwwucl4;ZnslFSE9$L%OyklmQIxQCTJe`7sWIX=R3_>~h{6(8+An!63q zn;ixrGPAQ+z5EA~XoN-;`R%1sb*u^Hxw#7LL9P8iF^Gt+=AfiB3r61YKU}Jnkz7OHa-ylZo^+UT{K`JGBt7o%&UcWqFCA7z;Mc5$@`cl}A9QCb^^(bs3` z915s1&flbTrt{41=LbAaTa!|^wd6ayZE3*!7g_54NJ3$KwEO1hlq4q1kuPR7)Oh*{ zV@5hVQ-1?Gie&zc|KDzSFU!q5l-FV-!c*eta1_1sumzrtwEqze1NDrO6{)(0rh$9= z3Zz{)?P^jGk@0&S!f4IXIeQ4<`~cOdRCH!V ztXMKDKJD{9p!?~eWvT-!7NOOpp@+J%>e5C;eU~M5}wuo5Dyd`>uJN<7?aF#acdocS4&%Tbl(55Uj7aB@c0w4@f_uU!gdb0OF{ZF-|<9G;0e*s zM@=dE3Ue~o_?R-`V_xUSTYUcv4HAG3fi#6&eivFoOg$$2hAAfWd&3h-2gK2t^a)!M zqC3}w#gfz4I!&#LBfa0u1cUih&{2b-w}N4%Kj1gHWQIqMhDm*`TU}oh`L~!Jr5MGH z_7_A&*fuu865Z;~hQv=SFVH^K^M2<2TxlJ8#Nce?zn(M|lK3(`; zL-6Z5Ea3z@Rn%Lpy#xSoZ=g~tD@$nGUb`^uH4Z`UNk~oxN8V#J$+~WFdR(~gZ7eMz zxDK4%Y#B=6BQaw8*6PQ#BQxEz4`~t8{b7 zZu3~7zXi;IS*MnghDKWJo5hQW>{pSfhtAWHUuX{rqX*3~j1fb1OsxA6;>^|608Ds7 z_>7H>b@{2-B`4b$b_bX=z~m7*Y@e<`#;);0R*s+Qy9?>)?dgAW{>}pksjokSZrQSj zef5yh0NyHK0BK4-N)cU05`KJwe;@>0>rKv{Z)tnGPjkAE^lPu&Q3$VLW;SSZwbIs3 zgTZxr)d1`wkkd>P#}lQ-)DC%NOx#*MbB&%ndX$N&a(-1n|M1~M7^j^r;&%?3P`H6l zygSEU**6>MHot%W&*s&|Ir!8pmk?AYT#ueBduIZ150hKkP$8co;o6 z?(GKU8ngZ`SB4Tn1(}(Z^8ah^%-^95+deK*2#qWa#;&wjW+H1M$(m$jUs9IPVlQii zq9IFUNXSTqk%+REH4%m}l3jL_QCYLpJm>Vj??3S#&-~;##xcu1*L_{*^8J2Jm4M+9 zxIz#+e;yqj9dK05doVF?_FC5XyC*ES5ddFK`lx2ztdVP(_Epa!E6cW)%6_zN{sv+j z5n*8@awWK~sp)iab>sP;Ohjvs<)slX_Lxj4zeF+<&`w zMPyQCG;k77(tCC?zp3^Vf2AFB1kGB=2IVL~kX8T7anEOJ@drpzm?GTJP-j*-c5Hil z`9sDkW!b;81sQT+4j(E0bDuz5iwle`HofAG)}@;AmxB2yJ)P~Y&Ls?r`)-AnxrUqa zVobmegbrX0`{7=YcO@kq!r?TdIE+`bW;a0V0sx}qY(fN7jznVbA|_UY-(3&MgLPkC zejy;XZ)dUp)7^2UpL`Z(I#u>^bJzZ!5Su+WUtZ05UE!($v&7VDutMdzRRaapTn+g> z^rxYVemOOuS$k2_c2qQ@hvQFfZZ1PA;N!QlbB6o89E&|ubT>2xz}XeKCa3|&$IpXDWVobsYe-0?l^-GDqMZ5e+Q8BP>2})tzQ-3io>yS}7avjz z5LMPeKpnaWR7kbk_7S4s+zEdKTSRm>%)V1n%RqkTx8Y(vRk=d$12!e-i6$3Lq?{0s z2B$NGrJDt&VK@t!j`qDKK|)a+<(^$TDh-?o4TTS!RF$PFMY^~|VH z+|4*dU90v7zJj@Sfb*y*ejs2^T3h1fI)%6aRTFRx0tyUCH{Q56_26K}lz8@}Y0k;z zVy`fmP^Ax*(*};%YjD5noS?~QEZGP+T`iHUZZl*;i!#%MYKLMV_8rAUT z3JHSe>X94Sv${M*_Y^5OoWV>LXOl#;E%8fxzOjb1TL_JvIUuxlI?YZLAd#M>+O34b z0c6lkwo9rB_|UL{Qp}GOt&p%&n)1q_cfO?!oo+xJ3ONQ_ zM5s7O9ifA1WHd8GQ$Ew0@lvq*NhH1|X6GsEN^b_p3|um3WOPTpe#Cn)=*^nbmlRaO zx83r3nvIro*mAc}vX6poW(GdF@?JhKj`-c_j*5bk79f@X9c zBw$1+p6_g_H*#hn{zKW#Kk88p5!Uydk}cKA`hA7S z!%m6AVxY8H9`?FTuNjLfg9b-^Oay`q#=T)Ow(1$#?ag8NZ-fqB#Go#pYK`nphPD5j3kaFfD?%4~wl|71a)@^U@BFezp`jEUaDS{R&DJ`P4mfC`Z!z9P37Q_z z*d3hPw&Y`@#@E?RjI&9&m=7vC%_!sUH>P+I;wTgkp zZ?7i?iWYozbBdIbCG?sDx8BkimJ4lqJyCQA`rW_RN5(-%4qgKM1l%!En~3g?2sfi$ z7TK|q_pQ$w`{AupH5L*8+~$N<9nwd?dwNWctr60-4=ec!at@Yl%c$fr&9h^AuD7Iu zMf1%|YbPf^1pVF6f=y?f=@3{KVL|3aU>2!VaAGwB+Q~tFf1NpRm9xjpk%w2V8?R?{ z&Vb2=}e@L%|)UV7F1Jp0b$CR+bJ6@al%A!h+Q+ zNY_+QNL*{Zs;8*vDd0GQY)0;h1VeHqoYXMZ1Q-Y<{Y3-mCf;+GfLKlkU_3CwKZD06 zC>zicI6dBA0hbFuLaW+SUD=w-7Qp@vyaV9hnfCYhlfn`&I|&%#5_u83VgjtI;RFE6hf zH@ZH5?&a?PlHiR?_?kutGUMzmTLc!GgKrxS+C{(-dYonO?2zLK-CsRRW!Sq3Fp#To z*q6x~CtlF-llnNj$GKw(74}ISsXnLyocH^TIN#TIU#iIO8tE>5w)B9P%F{s2^Y49t zcdQ=ttT=ZSk8f2mb`_64!{*%RBWtnkZ4C&hsZnZJfXw&ckdV{4aYO4y7M`aNCqzjT zEpZIbH4ewocAEESB3YB&X5x{bMDiQjim|}PN13&S0wC;IL1I!r$MZV^PBF#Af5pwt zihjpTJl>SmVN>`_Oqir(pHGp+5Ru?^O?({ar<(FG^k!;(%YwH#?hJ+Uoj=W$(Y;l; zSQ@-a#o>Ij_e%@5u9WF&3PmFydnJZSoi1;xbC8oNz;b6^#YAHTJgOO zW`~CZ*ddIX`g~h0!tzU}+cMCtUcH~8Li!`w4ETgdOr2hGn%qae;@+wFsdxq9CA91@ zB&X)ch^f}<3V>8+Dap+HdBhhZ9A7?s2Rkb)H+VB3q?o|L znsjq>BO|hv)eoDdFPWP8yy%(nPU7n?16~O1N6-O&|NcFg0>P~x7-;?7@;Mjb${OPG z?f0M;sj>(6K2%M>xD1d7_`|ZuLcPz4P~nw&Z{qOdgcnvVh&J~K*8y+UP%5RM-!c5p z*Pc8EFH&tcYeyLT2qU?YuF9EEr!E12PkXyjOp`?4sX{t42(k;L3y9eBu?Sr3)3G07 z!ouHaG+^)278YO_h0vo|kN$W;>7^Z38h`BOvUWu^z!jOE6~K3peg@9)^XFIfLJVz_ zX8X#xC5}wgAP(%$??2#wnP0P{C-2KOKK;LALqj!wvt3Zn2l1@-t$jZe8Ck^MLB9{M zJ;92oC@)XtDCPU4f3>bpDNcR!bnm#-SoeuL`L~s!@FXMwO{*tb;eVkU58o&E{9NSK z%JD&Y+2?G(PrkSQYipE`9Zdt?!f?2s)AoCn2eQY=OiXO}12zaj&DvZ*X* z6A3O&L_CJH42S0ySU4edgfOH)kmZoZ|9&Kk0&omLXQ>Jhbmiqr3C`0zf6Zc;^*W7@ zh7k<}*&B_bs!FAQc?RVZ)0M7>aPEQ_2P(&;94*igP*;NicnD~=TE`#$3Oi{wx=Ri& zBHSTvmd~MFX-mu)uz{a$K0BV%RA}O>w{_L<2+g}%Ied)|}|HWCw6)V&j&=qT7me(621gJa#Q#If#j3$C`- zKI+b5#W6yxhs1*;bC0Wh3lsG9c|4YdS7hAtLhfCHU{@URGq?_PHnMjx+;?>voRUta zOsc{_D%eV71>$<)-Atek%8E-_%v{e2QFRCRh2MeM5dD@AiS~8D0cK{&_InQT@Y)oy zuOkm94p8Ga9QhL_%h|l%_EZ{gMO$eRsF{b&)z^!ox+Qo9s&F=bXS_QI?na4%_wtbur8 zuR+t#8Cza~Byc^l_%UC<*+RQ%Wi<7VcRRTGto4|ZBzZmBv5z%n z%9p}|<+C4gkn?w{UZQ*rZ7SOSxD#vU$_`f1u8Mr3*y&eB8e=1ED+R6 zp0;byk*TmP{sMQur)S|%R$B5mpDKHg`FZXaz3xgJ*pPikO+pA*d5z=ktMe-dn^t+kledYgI88n5G5)kghIhdNt>S+6cSPo|65CSX314YtH41cZ&`k%M3rS3 zHfVPxbrd&Lx8Dt5?d7eEaGf{ubi698idir Lightnode + +
### Premium Supporters 🥇 @@ -94,8 +96,10 @@ For detailed documentation, visit [docs.dokploy.com](https://docs.dokploy.com). Startupfame Itsdb-center Openalternative +Synexa
+ ### Community Backers 🤝
From 125e44812b6926a00c11b5b1f03d0cf0b88c7095 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sun, 23 Feb 2025 18:11:21 -0600 Subject: [PATCH 109/126] refactor(security): remove permit root login status row --- .../dashboard/settings/servers/security-audit.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx index 475f2b8ff..1a90859f1 100644 --- a/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/security-audit.tsx @@ -145,15 +145,6 @@ export const SecurityAudit = ({ serverId }: Props) => { : "Enabled (Password Authentication should be disabled)" } /> - Date: Sun, 23 Feb 2025 18:11:27 -0600 Subject: [PATCH 110/126] refactor(ui): improve log highlighting and template formatting --- .../dashboard/docker/logs/terminal-line.tsx | 46 +++++++------- apps/dokploy/templates/convex/index.ts | 60 +++++++++---------- apps/dokploy/templates/templates.ts | 1 - 3 files changed, 53 insertions(+), 54 deletions(-) diff --git a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx index 116efedf3..359fbfa93 100644 --- a/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx +++ b/apps/dokploy/components/dashboard/docker/logs/terminal-line.tsx @@ -35,34 +35,34 @@ export function TerminalLine({ log, noTimestamp, searchTerm }: LogLineProps) { }) : "--- No time found ---"; - const highlightMessage = (text: string, term: string) => { - if (!term) { - return ( - - ); - } - - const htmlContent = fancyAnsi.toHtml(text); - const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); - - const modifiedContent = htmlContent.replace( - searchRegex, - (match) => - `${match}`, - ); - + const highlightMessage = (text: string, term: string) => { + if (!term) { return ( ); - }; + } + + const htmlContent = fancyAnsi.toHtml(text); + const searchRegex = new RegExp(`(${escapeRegExp(term)})`, "gi"); + + const modifiedContent = htmlContent.replace( + searchRegex, + (match) => + `${match}`, + ); + + return ( + + ); + }; const tooltip = (color: string, timestamp: string | null) => { const square = ( diff --git a/apps/dokploy/templates/convex/index.ts b/apps/dokploy/templates/convex/index.ts index 6a112cdee..badfe7320 100644 --- a/apps/dokploy/templates/convex/index.ts +++ b/apps/dokploy/templates/convex/index.ts @@ -1,38 +1,38 @@ import { - type DomainSchema, - type Schema, - type Template, - generateRandomDomain, + type DomainSchema, + type Schema, + type Template, + generateRandomDomain, } from "../utils"; export function generate(schema: Schema): Template { - const dashboardDomain = generateRandomDomain(schema); - const backendDomain = generateRandomDomain(schema); - const actionsDomain = generateRandomDomain(schema); + const dashboardDomain = generateRandomDomain(schema); + const backendDomain = generateRandomDomain(schema); + const actionsDomain = generateRandomDomain(schema); - const domains: DomainSchema[] = [ - { - host: dashboardDomain, - port: 6791, - serviceName: "dashboard", - }, - { - host: backendDomain, - port: 3210, - serviceName: "backend", - }, - { - host: actionsDomain, - port: 3211, - serviceName: "backend", - }, - ]; + const domains: DomainSchema[] = [ + { + host: dashboardDomain, + port: 6791, + serviceName: "dashboard", + }, + { + host: backendDomain, + port: 3210, + serviceName: "backend", + }, + { + host: actionsDomain, + port: 3211, + serviceName: "backend", + }, + ]; - const envs = [ - `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`, - `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`, - `CONVEX_SITE_ORIGIN=http://${actionsDomain}`, - ]; + const envs = [ + `NEXT_PUBLIC_DEPLOYMENT_URL=http://${backendDomain}`, + `CONVEX_CLOUD_ORIGIN=http://${backendDomain}`, + `CONVEX_SITE_ORIGIN=http://${actionsDomain}`, + ]; - return { envs, domains }; + return { envs, domains }; } diff --git a/apps/dokploy/templates/templates.ts b/apps/dokploy/templates/templates.ts index dc5205efc..d39465a8e 100644 --- a/apps/dokploy/templates/templates.ts +++ b/apps/dokploy/templates/templates.ts @@ -1619,4 +1619,3 @@ export const templates: TemplateData[] = [ load: () => import("./wikijs/index").then((m) => m.generate), }, ]; - From d3c59ff8afbea242c851da5110dfb7bfed7b01dd Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:13:55 -0600 Subject: [PATCH 111/126] refactor(ui): enhance sidebar layout with responsive design and collapsed state --- apps/dokploy/components/layouts/side.tsx | 61 +++++++++++++++++++----- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 9c61fb396..054988713 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -525,34 +525,63 @@ function SidebarLogo() {
) : ( - - + + {/* Organization Logo and Selector */} + -
-
+
+
-
+

{activeOrganization?.name ?? "Select Organization"}

- + + + {/* Notification Bell */} + -
+
+ + setLogo(e.target.value)} + placeholder="https://example.com/logo.png" + className="col-span-3" + /> +
diff --git a/apps/dokploy/components/shared/logo.tsx b/apps/dokploy/components/shared/logo.tsx index 086ef3b0c..bad6346ce 100644 --- a/apps/dokploy/components/shared/logo.tsx +++ b/apps/dokploy/components/shared/logo.tsx @@ -1,8 +1,20 @@ interface Props { className?: string; + logoUrl?: string; } -export const Logo = ({ className = "size-14" }: Props) => { +export const Logo = ({ className = "size-14", logoUrl }: Props) => { + if (logoUrl) { + return ( + Organization Logo + ); + } + return ( { @@ -81,6 +82,7 @@ export const organizationRouter = createTRPCRouter({ z.object({ organizationId: z.string(), name: z.string(), + logo: z.string().optional(), }), ) .mutation(async ({ ctx, input }) => { @@ -92,7 +94,10 @@ export const organizationRouter = createTRPCRouter({ } const result = await db .update(organization) - .set({ name: input.name }) + .set({ + name: input.name, + logo: input.logo, + }) .where(eq(organization.id, input.organizationId)) .returning(); return result[0]; From 8c2707c4ea439c2387901e8586943a36a7438cf7 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:33:26 -0600 Subject: [PATCH 113/126] refactor(organization): migrate to react-hook-form with zod validation --- .../organization/handle-organization.tsx | 133 ++++++++++++------ apps/dokploy/components/layouts/side.tsx | 4 +- apps/dokploy/components/shared/logo.tsx | 5 +- 3 files changed, 96 insertions(+), 46 deletions(-) diff --git a/apps/dokploy/components/dashboard/organization/handle-organization.tsx b/apps/dokploy/components/dashboard/organization/handle-organization.tsx index 249b8ccd0..014c37df1 100644 --- a/apps/dokploy/components/dashboard/organization/handle-organization.tsx +++ b/apps/dokploy/components/dashboard/organization/handle-organization.tsx @@ -9,18 +9,39 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { DropdownMenuItem } from "@/components/ui/dropdown-menu"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; import { api } from "@/utils/api"; +import { zodResolver } from "@hookform/resolvers/zod"; import { PenBoxIcon, Plus } from "lucide-react"; import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; import { toast } from "sonner"; +import { z } from "zod"; + +const organizationSchema = z.object({ + name: z.string().min(1, { + message: "Organization name is required", + }), + logo: z.string().optional(), +}); + +type OrganizationFormValues = z.infer; interface Props { organizationId?: string; children?: React.ReactNode; } + export function AddOrganization({ organizationId }: Props) { + const [open, setOpen] = useState(false); const utils = api.useUtils(); const { data: organization } = api.organization.one.useQuery( { @@ -33,24 +54,37 @@ export function AddOrganization({ organizationId }: Props) { const { mutateAsync, isLoading } = organizationId ? api.organization.update.useMutation() : api.organization.create.useMutation(); - const [open, setOpen] = useState(false); - const [name, setName] = useState(""); - const [logo, setLogo] = useState(""); + + const form = useForm({ + resolver: zodResolver(organizationSchema), + defaultValues: { + name: "", + logo: "", + }, + }); useEffect(() => { if (organization) { - setName(organization.name); - setLogo(organization.logo || ""); + form.reset({ + name: organization.name, + logo: organization.logo || "", + }); } - }, [organization]); - const handleSubmit = async () => { - await mutateAsync({ name, logo, organizationId: organizationId ?? "" }) + }, [organization, form]); + + const onSubmit = async (values: OrganizationFormValues) => { + await mutateAsync({ + name: values.name, + logo: values.logo, + organizationId: organizationId ?? "", + }) .then(() => { - setOpen(false); + form.reset(); toast.success( `Organization ${organizationId ? "updated" : "created"} successfully`, ); utils.organization.all.invalidate(); + setOpen(false); }) .catch((error) => { console.error(error); @@ -59,6 +93,7 @@ export function AddOrganization({ organizationId }: Props) { ); }); }; + return ( @@ -67,14 +102,11 @@ export function AddOrganization({ organizationId }: Props) { className="group cursor-pointer hover:bg-blue-500/10" onSelect={(e) => e.preventDefault()} > - + ) : ( { - setOpen(true); - }} onSelect={(e) => e.preventDefault()} >
@@ -97,36 +129,53 @@ export function AddOrganization({ organizationId }: Props) { : "Create a new organization to manage your projects."} -
-
- - setName(e.target.value)} - className="col-span-3" + + + ( + + Name + + + + + + )} /> -
-
- - setLogo(e.target.value)} - placeholder="https://example.com/logo.png" - className="col-span-3" + ( + + Logo URL + + + + + + )} /> -
-
- - - + + + + +
); diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 58638509e..805fe8dc7 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -534,7 +534,7 @@ function SidebarLogo() { )} > {/* Organization Logo and Selector */} - +
{ Organization Logo ); } From cbec0603bdd4572831bb6cf2b85103bd7bebbb17 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Tue, 25 Feb 2025 23:36:53 -0600 Subject: [PATCH 114/126] feat(ui): add loading state to sidebar layout --- apps/dokploy/components/layouts/side.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index 805fe8dc7..f7a63064e 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -751,6 +751,7 @@ export default function Page({ children }: Props) { const [defaultOpen, setDefaultOpen] = useState( undefined, ); + const [isLoaded, setIsLoaded] = useState(false); useEffect(() => { const cookieValue = document.cookie @@ -759,6 +760,7 @@ export default function Page({ children }: Props) { ?.split("=")[1]; setDefaultOpen(cookieValue === undefined ? true : cookieValue === "true"); + setIsLoaded(true); }, []); const router = useRouter(); @@ -780,9 +782,9 @@ export default function Page({ children }: Props) { pathname, ); - // const showProjectsButton = - // currentPath === "/dashboard/projects" && - // (auth?.rol === "owner" || user?.canCreateProjects); + if (!isLoaded) { + return
; // Placeholder mientras se carga + } return ( Date: Tue, 25 Feb 2025 23:37:22 -0600 Subject: [PATCH 115/126] refactor(ui): remove loading text in sidebar layout --- apps/dokploy/components/layouts/side.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/dokploy/components/layouts/side.tsx b/apps/dokploy/components/layouts/side.tsx index f7a63064e..19ef6f445 100644 --- a/apps/dokploy/components/layouts/side.tsx +++ b/apps/dokploy/components/layouts/side.tsx @@ -521,7 +521,6 @@ function SidebarLogo() { <> {isLoading ? (
- Loading...
) : ( From 55686298391eade54406b439c81caa63f6b50442 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 14:45:50 -0600 Subject: [PATCH 116/126] refactor: organize imports and improve template utility modules --- apps/dokploy/drizzle/0066_yielding_echo.sql | 35 +++- apps/dokploy/drizzle/meta/0066_snapshot.json | 177 ++++++++++++++++++- apps/dokploy/drizzle/meta/_journal.json | 7 + apps/dokploy/package.json | 2 +- packages/server/auth-schema.ts | 36 ++++ packages/server/package.json | 2 +- packages/server/src/db/schema/account.ts | 37 +++- packages/server/src/lib/auth.ts | 11 +- pnpm-lock.yaml | 54 ++++-- 9 files changed, 320 insertions(+), 41 deletions(-) diff --git a/apps/dokploy/drizzle/0066_yielding_echo.sql b/apps/dokploy/drizzle/0066_yielding_echo.sql index 4fd425387..a8a2501a8 100644 --- a/apps/dokploy/drizzle/0066_yielding_echo.sql +++ b/apps/dokploy/drizzle/0066_yielding_echo.sql @@ -72,7 +72,8 @@ CREATE TABLE "invitation" ( "role" text, "status" text NOT NULL, "expires_at" timestamp NOT NULL, - "inviter_id" text NOT NULL + "inviter_id" text NOT NULL, + "team_id" text ); --> statement-breakpoint CREATE TABLE "member" ( @@ -81,7 +82,6 @@ CREATE TABLE "member" ( "user_id" text NOT NULL, "role" text NOT NULL, "created_at" timestamp NOT NULL, - "token" text NOT NULL, "canCreateProjects" boolean DEFAULT false NOT NULL, "canAccessToSSHKeys" boolean DEFAULT false NOT NULL, "canCreateServices" boolean DEFAULT false NOT NULL, @@ -92,7 +92,8 @@ CREATE TABLE "member" ( "canAccessToGitProviders" boolean DEFAULT false NOT NULL, "canAccessToTraefikFiles" boolean DEFAULT false NOT NULL, "accesedProjects" text[] DEFAULT ARRAY[]::text[] NOT NULL, - "accesedServices" text[] DEFAULT ARRAY[]::text[] NOT NULL + "accesedServices" text[] DEFAULT ARRAY[]::text[] NOT NULL, + "team_id" text ); --> statement-breakpoint CREATE TABLE "organization" ( @@ -121,6 +122,30 @@ CREATE TABLE "two_factor" ( "backup_codes" text NOT NULL, "user_id" text NOT NULL ); + +CREATE TABLE "apikey" ( + "id" text PRIMARY KEY NOT NULL, + "name" text, + "start" text, + "prefix" text, + "key" text NOT NULL, + "user_id" text NOT NULL, + "refill_interval" integer, + "refill_amount" integer, + "last_refill_at" timestamp, + "enabled" boolean, + "rate_limit_enabled" boolean, + "rate_limit_time_window" integer, + "rate_limit_max" integer, + "request_count" integer, + "remaining" integer, + "last_request" timestamp, + "expires_at" timestamp, + "created_at" timestamp NOT NULL, + "updated_at" timestamp NOT NULL, + "permissions" text, + "metadata" text +); --> statement-breakpoint ALTER TABLE "certificate" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint ALTER TABLE "notification" ALTER COLUMN "adminId" SET NOT NULL;--> statement-breakpoint @@ -134,9 +159,7 @@ ALTER TABLE "member" ADD CONSTRAINT "member_organization_id_organization_id_fk" ALTER TABLE "member" ADD CONSTRAINT "member_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint ALTER TABLE "organization" ADD CONSTRAINT "organization_owner_id_user_temp_id_fk" FOREIGN KEY ("owner_id") REFERENCES "public"."user_temp"("id") ON DELETE no action ON UPDATE no action; ALTER TABLE "two_factor" ADD CONSTRAINT "two_factor_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint - - - +ALTER TABLE "apikey" ADD CONSTRAINT "apikey_user_id_user_temp_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint -- Data Migration diff --git a/apps/dokploy/drizzle/meta/0066_snapshot.json b/apps/dokploy/drizzle/meta/0066_snapshot.json index 2264814c2..55804f0cc 100644 --- a/apps/dokploy/drizzle/meta/0066_snapshot.json +++ b/apps/dokploy/drizzle/meta/0066_snapshot.json @@ -4295,6 +4295,159 @@ "checkConstraints": {}, "isRLSEnabled": false }, + "public.apikey": { + "name": "apikey", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "last_request": { + "name": "last_request", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "apikey_user_id_user_temp_id_fk": { + "name": "apikey_user_id_user_temp_id_fk", + "tableFrom": "apikey", + "tableTo": "user_temp", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, "public.invitation": { "name": "invitation", "schema": "", @@ -4340,6 +4493,13 @@ "type": "text", "primaryKey": false, "notNull": true + }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + } }, "indexes": {}, @@ -4411,6 +4571,12 @@ "primaryKey": false, "notNull": true }, + "team_id": { + "name": "team_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, "canCreateProjects": { "name": "canCreateProjects", "type": "boolean", @@ -4487,13 +4653,6 @@ "primaryKey": false, "notNull": true, "default": "ARRAY[]::text[]" - }, - "token": { - "name": "token", - "type": "text", - "primaryKey": false, - "notNull": true, - "default": "''" } }, "indexes": {}, @@ -4508,7 +4667,7 @@ "columnsTo": [ "id" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" }, "member_user_id_user_temp_id_fk": { @@ -4521,7 +4680,7 @@ "columnsTo": [ "id" ], - "onDelete": "no action", + "onDelete": "cascade", "onUpdate": "no action" } }, diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 8bf49b3a0..29a356e0e 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -470,6 +470,13 @@ "when": 1739426913392, "tag": "0066_yielding_echo", "breakpoints": true + }, + { + "idx": 67, + "version": "7", + "when": 1740860314823, + "tag": "0067_goofy_red_skull", + "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index 5f59efec0..a1626fc43 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -36,7 +36,7 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { - "better-auth": "1.1.16", + "better-auth": "beta", "bl": "6.0.11", "rotating-file-stream": "3.2.3", "qrcode": "^1.5.3", diff --git a/packages/server/auth-schema.ts b/packages/server/auth-schema.ts index b9b682fbf..a58290467 100644 --- a/packages/server/auth-schema.ts +++ b/packages/server/auth-schema.ts @@ -1,3 +1,11 @@ +// import { +// pgTable, +// text, +// integer, +// timestamp, +// boolean, +// } from "drizzle-orm/pg-core"; + // export const users_temp = pgTable("users_temp", { // id: text("id").primaryKey(), // name: text("name").notNull(), @@ -52,6 +60,32 @@ // updatedAt: timestamp("updated_at"), // }); +// export const apikey = pgTable("apikey", { +// id: text("id").primaryKey(), +// name: text("name"), +// start: text("start"), +// prefix: text("prefix"), +// key: text("key").notNull(), +// userId: text("user_id") +// .notNull() +// .references(() => user.id, { onDelete: "cascade" }), +// refillInterval: integer("refill_interval"), +// refillAmount: integer("refill_amount"), +// lastRefillAt: timestamp("last_refill_at"), +// enabled: boolean("enabled"), +// rateLimitEnabled: boolean("rate_limit_enabled"), +// rateLimitTimeWindow: integer("rate_limit_time_window"), +// rateLimitMax: integer("rate_limit_max"), +// requestCount: integer("request_count"), +// remaining: integer("remaining"), +// lastRequest: timestamp("last_request"), +// expiresAt: timestamp("expires_at"), +// createdAt: timestamp("created_at").notNull(), +// updatedAt: timestamp("updated_at").notNull(), +// permissions: text("permissions"), +// metadata: text("metadata"), +// }); + // export const twoFactor = pgTable("two_factor", { // id: text("id").primaryKey(), // secret: text("secret").notNull(), @@ -79,6 +113,7 @@ // .notNull() // .references(() => user.id, { onDelete: "cascade" }), // role: text("role").notNull(), +// teamId: text("team_id"), // createdAt: timestamp("created_at").notNull(), // }); @@ -89,6 +124,7 @@ // .references(() => organization.id, { onDelete: "cascade" }), // email: text("email").notNull(), // role: text("role"), +// teamId: text("team_id"), // status: text("status").notNull(), // expiresAt: timestamp("expires_at").notNull(), // inviterId: text("inviter_id") diff --git a/packages/server/package.json b/packages/server/package.json index dbc24375e..bd2d9ed06 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -32,7 +32,7 @@ "@oslojs/encoding":"1.1.0", "@oslojs/crypto":"1.0.1", "drizzle-dbml-generator":"0.10.0", - "better-auth":"1.1.16", + "better-auth":"beta", "rotating-file-stream": "3.2.3", "@faker-js/faker": "^8.4.1", "@lucia-auth/adapter-drizzle": "1.0.7", diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index 9d55016d9..e5f2dab77 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -1,5 +1,11 @@ import { relations, sql } from "drizzle-orm"; -import { boolean, pgTable, text, timestamp } from "drizzle-orm/pg-core"; +import { + boolean, + integer, + pgTable, + text, + timestamp, +} from "drizzle-orm/pg-core"; import { nanoid } from "nanoid"; import { projects } from "./project"; import { server } from "./server"; @@ -87,7 +93,7 @@ export const member = pgTable("member", { .references(() => users_temp.id, { onDelete: "cascade" }), role: text("role").notNull().$type<"owner" | "member" | "admin">(), createdAt: timestamp("created_at").notNull(), - + teamId: text("team_id"), // Permissions canCreateProjects: boolean("canCreateProjects").notNull().default(false), canAccessToSSHKeys: boolean("canAccessToSSHKeys").notNull().default(false), @@ -135,6 +141,7 @@ export const invitation = pgTable("invitation", { inviterId: text("inviter_id") .notNull() .references(() => users_temp.id, { onDelete: "cascade" }), + teamId: text("team_id"), }); export const invitationRelations = relations(invitation, ({ one }) => ({ @@ -152,3 +159,29 @@ export const twoFactor = pgTable("two_factor", { .notNull() .references(() => users_temp.id, { onDelete: "cascade" }), }); + +export const apikey = pgTable("apikey", { + id: text("id").primaryKey(), + name: text("name"), + start: text("start"), + prefix: text("prefix"), + key: text("key").notNull(), + userId: text("user_id") + .notNull() + .references(() => users_temp.id, { onDelete: "cascade" }), + refillInterval: integer("refill_interval"), + refillAmount: integer("refill_amount"), + lastRefillAt: timestamp("last_refill_at"), + enabled: boolean("enabled"), + rateLimitEnabled: boolean("rate_limit_enabled"), + rateLimitTimeWindow: integer("rate_limit_time_window"), + rateLimitMax: integer("rate_limit_max"), + requestCount: integer("request_count"), + remaining: integer("remaining"), + lastRequest: timestamp("last_request"), + expiresAt: timestamp("expires_at"), + createdAt: timestamp("created_at").notNull(), + updatedAt: timestamp("updated_at").notNull(), + permissions: text("permissions"), + metadata: text("metadata"), +}); diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 24e23bfd7..bebcd54ee 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -2,14 +2,14 @@ import type { IncomingMessage } from "node:http"; import * as bcrypt from "bcrypt"; import { betterAuth } from "better-auth"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; -import { organization, twoFactor } from "better-auth/plugins"; +import { organization, twoFactor, apiKey } from "better-auth/plugins"; import { and, desc, eq } from "drizzle-orm"; import { db } from "../db"; import * as schema from "../db/schema"; import { sendEmail } from "../verification/send-verification-email"; import { IS_CLOUD } from "../constants"; -const { handler, api } = betterAuth({ +export const auth = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: schema, @@ -126,6 +126,7 @@ const { handler, api } = betterAuth({ }, plugins: [ + apiKey(), twoFactor(), organization({ async sendInvitationEmail(data, _request) { @@ -144,9 +145,9 @@ const { handler, api } = betterAuth({ ], }); -export const auth = { - handler, -}; +// export const auth = { +// handler, +// }; export const validateRequest = async (request: IncomingMessage) => { const session = await api.getSession({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ee68b3995..6e1c088b8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -239,8 +239,8 @@ importers: specifier: 5.1.1 version: 5.1.1(encoding@0.1.13) better-auth: - specifier: 1.1.16 - version: 1.1.16 + specifier: beta + version: 1.2.0-beta.18(typescript@5.5.3) bl: specifier: 6.0.11 version: 6.0.11 @@ -580,8 +580,8 @@ importers: specifier: 5.1.1 version: 5.1.1(encoding@0.1.13) better-auth: - specifier: 1.1.16 - version: 1.1.16 + specifier: beta + version: 1.2.0-beta.18(typescript@5.5.3) bl: specifier: 6.0.11 version: 6.0.11 @@ -769,8 +769,8 @@ packages: '@better-auth/utils@0.2.3': resolution: {integrity: sha512-Ap1GaSmo6JYhJhxJOpUB0HobkKPTNzfta+bLV89HfpyCAHN7p8ntCrmNFHNAVD0F6v0mywFVEUg1FUhNCc81Rw==} - '@better-fetch/fetch@1.1.12': - resolution: {integrity: sha512-B3bfloI/2UBQWIATRN6qmlORrvx3Mp0kkNjmXLv0b+DtbtR+pP4/I5kQA/rDUv+OReLywCCldf6co4LdDmh8JA==} + '@better-fetch/fetch@1.1.15': + resolution: {integrity: sha512-0Bl8YYj1f8qCTNHeSn5+1DWv2hy7rLBrQ8rS8Y9XYloiwZEfc3k4yspIG0llRxafxqhGCwlGRg+F8q1HZRCMXA==} '@biomejs/biome@1.9.4': resolution: {integrity: sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog==} @@ -3879,11 +3879,11 @@ packages: before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} - better-auth@1.1.16: - resolution: {integrity: sha512-Xc5pxafKZw4QVU8WYfkV2z4Hd8KCXXbphrgOpe2gA/EfanysLBhE1G/F7cEi5e0bW2pGR+vw6gf0ARHA7VFihg==} + better-auth@1.2.0-beta.18: + resolution: {integrity: sha512-gEjNxmrkFiATTSTcE47rkyTT9vMFMLTjtLNun4W0IWmeqfi4pIbbWpo97foY1DNXXRDkDuajquoD58dzAatQxQ==} - better-call@0.3.3: - resolution: {integrity: sha512-N4lDVm0NGmFfDJ0XMQ4O83Zm/3dPlvIQdxvwvgSLSkjFX5PM4GUYSVAuxNzXN27QZMHDkrJTWUqxBrm4tPC3eA==} + better-call@1.0.3: + resolution: {integrity: sha512-DUKImKoDIy5UtCvQbHTg0wuBRse6gu1Yvznn7+1B3I5TeY8sclRPFce0HI+4WF2bcb+9PqmkET8nXZubrHQh9A==} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -6567,6 +6567,9 @@ packages: set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -7114,6 +7117,14 @@ packages: v8-compile-cache-lib@3.0.1: resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} + valibot@1.0.0-beta.15: + resolution: {integrity: sha512-BKy8XosZkDHWmYC+cJG74LBzP++Gfntwi33pP3D3RKztz2XV9jmFWnkOi21GoqARP8wAWARwhV6eTr1JcWzjGw==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} @@ -7383,7 +7394,7 @@ snapshots: dependencies: uncrypto: 0.1.3 - '@better-fetch/fetch@1.1.12': {} + '@better-fetch/fetch@1.1.15': {} '@biomejs/biome@1.9.4': optionalDependencies: @@ -10543,27 +10554,30 @@ snapshots: before-after-hook@2.2.3: {} - better-auth@1.1.16: + better-auth@1.2.0-beta.18(typescript@5.5.3): dependencies: '@better-auth/utils': 0.2.3 - '@better-fetch/fetch': 1.1.12 + '@better-fetch/fetch': 1.1.15 '@noble/ciphers': 0.6.0 '@noble/hashes': 1.7.1 '@simplewebauthn/browser': 13.1.0 '@simplewebauthn/server': 13.1.1 - better-call: 0.3.3 + better-call: 1.0.3 defu: 6.1.4 jose: 5.9.6 kysely: 0.27.5 nanostores: 0.11.3 + valibot: 1.0.0-beta.15(typescript@5.5.3) zod: 3.24.1 + transitivePeerDependencies: + - typescript - better-call@0.3.3: + better-call@1.0.3: dependencies: - '@better-fetch/fetch': 1.1.12 + '@better-fetch/fetch': 1.1.15 rou3: 0.5.1 + set-cookie-parser: 2.7.1 uncrypto: 0.1.3 - zod: 3.24.1 binary-extensions@2.3.0: {} @@ -13338,6 +13352,8 @@ snapshots: set-blocking@2.0.0: {} + set-cookie-parser@2.7.1: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -13970,6 +13986,10 @@ snapshots: v8-compile-cache-lib@3.0.1: optional: true + valibot@1.0.0-beta.15(typescript@5.5.3): + optionalDependencies: + typescript: 5.5.3 + victory-vendor@36.9.2: dependencies: '@types/d3-array': 3.2.1 From 5dc5292928b5732556517d000251c3c1ba3bf8bf Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 19:58:15 -0600 Subject: [PATCH 117/126] feat(api): implement advanced API key management with granular controls --- .../dashboard/settings/api/add-api-key.tsx | 468 ++++++++++++++++++ .../dashboard/settings/api/show-api-keys.tsx | 142 ++++++ .../settings/profile/generate-token.tsx | 77 --- apps/dokploy/drizzle/0066_yielding_echo.sql | 29 +- apps/dokploy/drizzle/meta/_journal.json | 7 - apps/dokploy/lib/auth-client.ts | 3 +- apps/dokploy/package.json | 2 +- .../pages/dashboard/settings/profile.tsx | 4 +- apps/dokploy/pages/swagger.tsx | 36 +- apps/dokploy/server/api/routers/settings.ts | 20 +- apps/dokploy/server/api/routers/user.ts | 66 ++- packages/server/package.json | 2 +- packages/server/src/db/schema/account.ts | 7 + packages/server/src/db/schema/user.ts | 3 +- packages/server/src/lib/auth.ts | 112 ++++- packages/server/src/services/user.ts | 46 +- pnpm-lock.yaml | 14 +- 17 files changed, 926 insertions(+), 112 deletions(-) create mode 100644 apps/dokploy/components/dashboard/settings/api/add-api-key.tsx create mode 100644 apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx delete mode 100644 apps/dokploy/components/dashboard/settings/profile/generate-token.tsx diff --git a/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx b/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx new file mode 100644 index 000000000..a82a9b356 --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/api/add-api-key.tsx @@ -0,0 +1,468 @@ +import { Button } from "@/components/ui/button"; +import { api } from "@/utils/api"; +import { toast } from "sonner"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogDescription, +} from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, + FormDescription, +} from "@/components/ui/form"; +import { Switch } from "@/components/ui/switch"; + +const formSchema = z.object({ + name: z.string().min(1, "Name is required"), + prefix: z.string().optional(), + expiresIn: z.number().nullable(), + organizationId: z.string().min(1, "Organization is required"), + // Rate limiting fields + rateLimitEnabled: z.boolean().optional(), + rateLimitTimeWindow: z.number().nullable(), + rateLimitMax: z.number().nullable(), + // Request limiting fields + remaining: z.number().nullable().optional(), + refillAmount: z.number().nullable().optional(), + refillInterval: z.number().nullable().optional(), +}); + +type FormValues = z.infer; + +const EXPIRATION_OPTIONS = [ + { label: "Never", value: "0" }, + { label: "1 day", value: String(60 * 60 * 24) }, + { label: "7 days", value: String(60 * 60 * 24 * 7) }, + { label: "30 days", value: String(60 * 60 * 24 * 30) }, + { label: "90 days", value: String(60 * 60 * 24 * 90) }, + { label: "1 year", value: String(60 * 60 * 24 * 365) }, +]; + +const TIME_WINDOW_OPTIONS = [ + { label: "1 minute", value: String(60 * 1000) }, + { label: "5 minutes", value: String(5 * 60 * 1000) }, + { label: "15 minutes", value: String(15 * 60 * 1000) }, + { label: "30 minutes", value: String(30 * 60 * 1000) }, + { label: "1 hour", value: String(60 * 60 * 1000) }, + { label: "1 day", value: String(24 * 60 * 60 * 1000) }, +]; + +const REFILL_INTERVAL_OPTIONS = [ + { label: "1 hour", value: String(60 * 60 * 1000) }, + { label: "6 hours", value: String(6 * 60 * 60 * 1000) }, + { label: "12 hours", value: String(12 * 60 * 60 * 1000) }, + { label: "1 day", value: String(24 * 60 * 60 * 1000) }, + { label: "7 days", value: String(7 * 24 * 60 * 60 * 1000) }, + { label: "30 days", value: String(30 * 24 * 60 * 60 * 1000) }, +]; + +export const AddApiKey = () => { + const [open, setOpen] = useState(false); + const [showSuccessModal, setShowSuccessModal] = useState(false); + const [newApiKey, setNewApiKey] = useState(""); + const { refetch } = api.user.get.useQuery(); + const { data: organizations } = api.organization.all.useQuery(); + const createApiKey = api.user.createApiKey.useMutation({ + onSuccess: (data) => { + if (!data) return; + + setNewApiKey(data.key); + setOpen(false); + setShowSuccessModal(true); + form.reset(); + void refetch(); + }, + onError: () => { + toast.error("Failed to generate API key"); + }, + }); + + const form = useForm({ + resolver: zodResolver(formSchema), + defaultValues: { + name: "", + prefix: "", + expiresIn: null, + organizationId: "", + rateLimitEnabled: false, + rateLimitTimeWindow: null, + rateLimitMax: null, + remaining: null, + refillAmount: null, + refillInterval: null, + }, + }); + + const rateLimitEnabled = form.watch("rateLimitEnabled"); + + const onSubmit = async (values: FormValues) => { + createApiKey.mutate({ + name: values.name, + expiresIn: values.expiresIn || undefined, + prefix: values.prefix || undefined, + metadata: { + organizationId: values.organizationId, + }, + // Rate limiting + rateLimitEnabled: values.rateLimitEnabled, + rateLimitTimeWindow: values.rateLimitTimeWindow || undefined, + rateLimitMax: values.rateLimitMax || undefined, + // Request limiting + remaining: values.remaining || undefined, + refillAmount: values.refillAmount || undefined, + refillInterval: values.refillInterval || undefined, + }); + }; + + return ( + <> + + + + + + + Generate API Key + + Create a new API key for accessing the API. You can set an + expiration date and a custom prefix for better organization. + + +
+ + ( + + Name + + + + + + )} + /> + ( + + Prefix + + + + + + )} + /> + ( + + Expiration + + + + )} + /> + ( + + Organization + + + + )} + /> + + {/* Rate Limiting Section */} +
+

Rate Limiting

+ ( + +
+ Enable Rate Limiting + + Limit the number of requests within a time window + +
+ + + +
+ )} + /> + + {rateLimitEnabled && ( + <> + ( + + Time Window + + + The duration in which requests are counted + + + + )} + /> + ( + + Maximum Requests + + + field.onChange( + e.target.value + ? Number.parseInt(e.target.value, 10) + : null, + ) + } + /> + + + Maximum number of requests allowed within the time + window + + + + )} + /> + + )} +
+ + {/* Request Limiting Section */} +
+

Request Limiting

+ ( + + Total Request Limit + + + field.onChange( + e.target.value + ? Number.parseInt(e.target.value, 10) + : null, + ) + } + /> + + + Total number of requests allowed (leave empty for + unlimited) + + + + )} + /> + + ( + + Refill Amount + + + field.onChange( + e.target.value + ? Number.parseInt(e.target.value, 10) + : null, + ) + } + /> + + + Number of requests to add on each refill + + + + )} + /> + + ( + + Refill Interval + + + How often to refill the request limit + + + + )} + /> +
+ +
+ + +
+ + +
+
+ + + + + API Key Generated Successfully + + Please copy your API key now. You won't be able to see it again! + + +
+
+ {newApiKey} +
+
+ + +
+
+
+
+ + ); +}; diff --git a/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx b/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx new file mode 100644 index 000000000..6744f1dea --- /dev/null +++ b/apps/dokploy/components/dashboard/settings/api/show-api-keys.tsx @@ -0,0 +1,142 @@ +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { api } from "@/utils/api"; +import { ExternalLinkIcon, KeyIcon, Trash2, Clock, Tag } from "lucide-react"; +import Link from "next/link"; +import { toast } from "sonner"; +import { formatDistanceToNow } from "date-fns"; +import { DialogAction } from "@/components/shared/dialog-action"; +import { AddApiKey } from "./add-api-key"; +import { Badge } from "@/components/ui/badge"; + +export const ShowApiKeys = () => { + const { data, refetch } = api.user.get.useQuery(); + const { mutateAsync: deleteApiKey, isLoading: isLoadingDelete } = + api.user.deleteApiKey.useMutation(); + + return ( +
+ +
+ +
+ + + API/CLI Keys + + + Generate and manage API keys to access the API/CLI + +
+
+ + Swagger API: + + + View + + +
+
+ +
+ {data?.user.apiKeys && data.user.apiKeys.length > 0 ? ( + data.user.apiKeys.map((apiKey) => ( +
+
+
+ {apiKey.name} +
+ + + Created{" "} + {formatDistanceToNow(new Date(apiKey.createdAt))}{" "} + ago + + {apiKey.prefix && ( + + + {apiKey.prefix} + + )} + {apiKey.expiresAt && ( + + + Expires in{" "} + {formatDistanceToNow( + new Date(apiKey.expiresAt), + )}{" "} + + )} +
+
+ { + try { + await deleteApiKey({ + apiKeyId: apiKey.id, + }); + await refetch(); + toast.success("API key deleted successfully"); + } catch (error) { + toast.error( + error instanceof Error + ? error.message + : "Error deleting API key", + ); + } + }} + > + + +
+
+ )) + ) : ( +
+ + + No API keys found + +
+ )} +
+ + {/* Generate new API key */} +
+ +
+
+
+
+
+ ); +}; diff --git a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx b/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx deleted file mode 100644 index 5213c0b9d..000000000 --- a/apps/dokploy/components/dashboard/settings/profile/generate-token.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { ToggleVisibilityInput } from "@/components/shared/toggle-visibility-input"; -import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { Label } from "@/components/ui/label"; -import { api } from "@/utils/api"; -import { ExternalLinkIcon } from "lucide-react"; -import Link from "next/link"; -import { toast } from "sonner"; - -export const GenerateToken = () => { - const { data, refetch } = api.user.get.useQuery(); - - const { mutateAsync: generateToken, isLoading: isLoadingToken } = - api.user.generateToken.useMutation(); - - return ( -
- -
- -
- API/CLI - - Generate a token to access the API/CLI - -
-
- - Swagger API: - - - View - - -
-
- -
-
-
- - -
-
- -
-
-
-
-
- ); -}; diff --git a/apps/dokploy/drizzle/0066_yielding_echo.sql b/apps/dokploy/drizzle/0066_yielding_echo.sql index a8a2501a8..bb5c2511c 100644 --- a/apps/dokploy/drizzle/0066_yielding_echo.sql +++ b/apps/dokploy/drizzle/0066_yielding_echo.sql @@ -321,7 +321,6 @@ inserted_admin_members AS ( "user_id", role, "created_at", - "token", "canAccessToAPI", "canAccessToDocker", "canAccessToGitProviders", @@ -340,7 +339,6 @@ inserted_admin_members AS ( a."adminId", 'owner', NOW(), - COALESCE(auth.token, ''), true, -- Los admins tienen todos los permisos por defecto true, true, @@ -364,7 +362,6 @@ INSERT INTO member ( "user_id", role, "created_at", - "token", "canAccessToAPI", "canAccessToDocker", "canAccessToGitProviders", @@ -383,7 +380,6 @@ SELECT u."userId", 'member', NOW(), - COALESCE(auth.token, ''), COALESCE(u."canAccessToAPI", false), COALESCE(u."canAccessToDocker", false), COALESCE(u."canAccessToGitProviders", false), @@ -400,6 +396,29 @@ JOIN admin a ON u."adminId" = a."adminId" JOIN inserted_orgs o ON o."owner_id" = a."adminId" JOIN auth ON auth.id = u."authId"; +-- Migrar tokens de auth a apikey +INSERT INTO apikey ( + id, + name, + key, + user_id, + enabled, + created_at, + updated_at +) +SELECT + gen_random_uuid(), + 'Legacy Token', + auth.token, +user_temp.id, + true, + NOW(), + NOW() +FROM auth +JOIN admin ON auth.id = admin."authId" +JOIN user_temp ON user_temp.id = admin."adminId" +WHERE auth.token IS NOT NULL AND auth.token != ''; + -- Migration tables foreign keys ALTER TABLE "project" RENAME COLUMN "adminId" TO "userId";--> statement-breakpoint @@ -436,7 +455,6 @@ ALTER TABLE "git_provider" ADD CONSTRAINT "git_provider_userId_user_temp_id_fk" ALTER TABLE "server" ADD CONSTRAINT "server_userId_user_temp_id_fk" FOREIGN KEY ("userId") REFERENCES "public"."user_temp"("id") ON DELETE cascade ON UPDATE no action; -ALTER TABLE "member" ALTER COLUMN "token" SET DEFAULT '';--> statement-breakpoint ALTER TABLE "user_temp" ADD COLUMN "created_at" timestamp DEFAULT now(); @@ -635,7 +653,6 @@ ALTER TABLE "git_provider" DROP COLUMN "userId";--> statement-breakpoint ALTER TABLE "server" DROP COLUMN "userId"; -- Drop tables ---> statement-breakpoint DROP TABLE "user" CASCADE;--> statement-breakpoint DROP TABLE "admin" CASCADE;--> statement-breakpoint DROP TABLE "auth" CASCADE;--> statement-breakpoint diff --git a/apps/dokploy/drizzle/meta/_journal.json b/apps/dokploy/drizzle/meta/_journal.json index 29a356e0e..8bf49b3a0 100644 --- a/apps/dokploy/drizzle/meta/_journal.json +++ b/apps/dokploy/drizzle/meta/_journal.json @@ -470,13 +470,6 @@ "when": 1739426913392, "tag": "0066_yielding_echo", "breakpoints": true - }, - { - "idx": 67, - "version": "7", - "when": 1740860314823, - "tag": "0067_goofy_red_skull", - "breakpoints": true } ] } \ No newline at end of file diff --git a/apps/dokploy/lib/auth-client.ts b/apps/dokploy/lib/auth-client.ts index 9a184959b..f1088e73f 100644 --- a/apps/dokploy/lib/auth-client.ts +++ b/apps/dokploy/lib/auth-client.ts @@ -1,8 +1,9 @@ import { organizationClient } from "better-auth/client/plugins"; import { twoFactorClient } from "better-auth/client/plugins"; +import { apiKeyClient } from "better-auth/client/plugins"; import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient({ // baseURL: "http://localhost:3000", // the base url of your auth server - plugins: [organizationClient(), twoFactorClient()], + plugins: [organizationClient(), twoFactorClient(), apiKeyClient()], }); diff --git a/apps/dokploy/package.json b/apps/dokploy/package.json index a1626fc43..3c7b05284 100644 --- a/apps/dokploy/package.json +++ b/apps/dokploy/package.json @@ -36,7 +36,7 @@ "test": "vitest --config __test__/vitest.config.ts" }, "dependencies": { - "better-auth": "beta", + "better-auth": "1.2.0", "bl": "6.0.11", "rotating-file-stream": "3.2.3", "qrcode": "^1.5.3", diff --git a/apps/dokploy/pages/dashboard/settings/profile.tsx b/apps/dokploy/pages/dashboard/settings/profile.tsx index 404d400c8..83ff5624c 100644 --- a/apps/dokploy/pages/dashboard/settings/profile.tsx +++ b/apps/dokploy/pages/dashboard/settings/profile.tsx @@ -1,4 +1,4 @@ -import { GenerateToken } from "@/components/dashboard/settings/profile/generate-token"; +import { ShowApiKeys } from "@/components/dashboard/settings/api/show-api-keys"; import { ProfileForm } from "@/components/dashboard/settings/profile/profile-form"; import { DashboardLayout } from "@/components/layouts/dashboard-layout"; @@ -19,7 +19,7 @@ const Page = () => {
- {(data?.canAccessToAPI || data?.role === "owner") && } + {(data?.canAccessToAPI || data?.role === "owner") && } {/* {isCloud && } */}
diff --git a/apps/dokploy/pages/swagger.tsx b/apps/dokploy/pages/swagger.tsx index 3d8cc01d9..11ea0731d 100644 --- a/apps/dokploy/pages/swagger.tsx +++ b/apps/dokploy/pages/swagger.tsx @@ -30,7 +30,41 @@ const Home: NextPage = () => { return (
- + (args: any) => { + const result = ori(args); + const apiKey = args?.apiKey?.value; + if (apiKey) { + localStorage.setItem("swagger_api_key", apiKey); + } + return result; + }, + logout: (ori: any) => (args: any) => { + const result = ori(args); + localStorage.removeItem("swagger_api_key"); + return result; + }, + }, + }, + }, + }, + ]} + requestInterceptor={(request: any) => { + const apiKey = localStorage.getItem("swagger_api_key"); + if (apiKey) { + request.headers = request.headers || {}; + request.headers["x-api-key"] = apiKey; + } + return request; + }} + />
); }; diff --git a/apps/dokploy/server/api/routers/settings.ts b/apps/dokploy/server/api/routers/settings.ts index d2455fdb7..fc1255fcf 100644 --- a/apps/dokploy/server/api/routers/settings.ts +++ b/apps/dokploy/server/api/routers/settings.ts @@ -482,10 +482,28 @@ export const settingsRouter = createTRPCRouter({ openApiDocument.info = { title: "Dokploy API", description: "Endpoints for dokploy", - // TODO: get version from package.json version: "1.0.0", }; + // Add security schemes configuration + openApiDocument.components = { + ...openApiDocument.components, + securitySchemes: { + apiKey: { + type: "apiKey", + in: "header", + name: "x-api-key", + description: "API key authentication", + }, + }, + }; + + // Apply security globally to all endpoints + openApiDocument.security = [ + { + apiKey: [], + }, + ]; return openApiDocument; }, ), diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 1dac65fef..5a84742a0 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -5,6 +5,7 @@ import { getUserByToken, removeUserById, updateUser, + createApiKey, } from "@dokploy/server"; import { db } from "@dokploy/server/db"; import { @@ -14,6 +15,7 @@ import { apiUpdateUser, invitation, member, + apikey, } from "@dokploy/server/db/schema"; import * as bcrypt from "bcrypt"; import { TRPCError } from "@trpc/server"; @@ -25,6 +27,24 @@ import { protectedProcedure, publicProcedure, } from "../trpc"; + +const apiCreateApiKey = z.object({ + name: z.string().min(1), + prefix: z.string().optional(), + expiresIn: z.number().optional(), + metadata: z.object({ + organizationId: z.string(), + }), + // Rate limiting + rateLimitEnabled: z.boolean().optional(), + rateLimitTimeWindow: z.number().optional(), + rateLimitMax: z.number().optional(), + // Request limiting + remaining: z.number().optional(), + refillAmount: z.number().optional(), + refillInterval: z.number().optional(), +}); + export const userRouter = createTRPCRouter({ all: adminProcedure.query(async ({ ctx }) => { return await db.query.member.findMany({ @@ -61,7 +81,11 @@ export const userRouter = createTRPCRouter({ eq(member.organizationId, ctx.session?.activeOrganizationId || ""), ), with: { - user: true, + user: { + with: { + apiKeys: true, + }, + }, }, }); @@ -249,4 +273,44 @@ export const userRouter = createTRPCRouter({ generateToken: protectedProcedure.mutation(async () => { return "token"; }), + + deleteApiKey: protectedProcedure + .input( + z.object({ + apiKeyId: z.string(), + }), + ) + .mutation(async ({ input, ctx }) => { + try { + const apiKeyToDelete = await db.query.apikey.findFirst({ + where: eq(apikey.id, input.apiKeyId), + }); + + if (!apiKeyToDelete) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "API key not found", + }); + } + + if (apiKeyToDelete.userId !== ctx.user.id) { + throw new TRPCError({ + code: "UNAUTHORIZED", + message: "You are not authorized to delete this API key", + }); + } + + await db.delete(apikey).where(eq(apikey.id, input.apiKeyId)); + return true; + } catch (error) { + throw error; + } + }), + + createApiKey: protectedProcedure + .input(apiCreateApiKey) + .mutation(async ({ input, ctx }) => { + const apiKey = await createApiKey(ctx.user.id, input); + return apiKey; + }), }); diff --git a/packages/server/package.json b/packages/server/package.json index bd2d9ed06..d99f5c248 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -32,7 +32,7 @@ "@oslojs/encoding":"1.1.0", "@oslojs/crypto":"1.0.1", "drizzle-dbml-generator":"0.10.0", - "better-auth":"beta", + "better-auth":"1.2.0", "rotating-file-stream": "3.2.3", "@faker-js/faker": "^8.4.1", "@lucia-auth/adapter-drizzle": "1.0.7", diff --git a/packages/server/src/db/schema/account.ts b/packages/server/src/db/schema/account.ts index e5f2dab77..8291ea4d6 100644 --- a/packages/server/src/db/schema/account.ts +++ b/packages/server/src/db/schema/account.ts @@ -185,3 +185,10 @@ export const apikey = pgTable("apikey", { permissions: text("permissions"), metadata: text("metadata"), }); + +export const apikeyRelations = relations(apikey, ({ one }) => ({ + user: one(users_temp, { + fields: [apikey.userId], + references: [users_temp.id], + }), +})); diff --git a/packages/server/src/db/schema/user.ts b/packages/server/src/db/schema/user.ts index 3916f1e70..9307127a1 100644 --- a/packages/server/src/db/schema/user.ts +++ b/packages/server/src/db/schema/user.ts @@ -10,7 +10,7 @@ import { import { createInsertSchema } from "drizzle-zod"; import { nanoid } from "nanoid"; import { z } from "zod"; -import { account, organization } from "./account"; +import { account, organization, apikey } from "./account"; import { projects } from "./project"; import { certificateType } from "./shared"; /** @@ -123,6 +123,7 @@ export const usersRelations = relations(users_temp, ({ one, many }) => ({ }), organizations: many(organization), projects: many(projects), + apiKeys: many(apikey), })); const createSchema = createInsertSchema(users_temp, { diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index bebcd54ee..3089bb1d0 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -9,7 +9,7 @@ import * as schema from "../db/schema"; import { sendEmail } from "../verification/send-verification-email"; import { IS_CLOUD } from "../constants"; -export const auth = betterAuth({ +const { handler, api } = betterAuth({ database: drizzleAdapter(db, { provider: "pg", schema: schema, @@ -126,7 +126,9 @@ export const auth = betterAuth({ }, plugins: [ - apiKey(), + apiKey({ + enableMetadata: true, + }), twoFactor(), organization({ async sendInvitationEmail(data, _request) { @@ -145,11 +147,111 @@ export const auth = betterAuth({ ], }); -// export const auth = { -// handler, -// }; +export const auth = { + handler, + api, +}; export const validateRequest = async (request: IncomingMessage) => { + const apiKey = request.headers["x-api-key"] as string; + if (apiKey) { + try { + const { valid, key, error } = await api.verifyApiKey({ + body: { + key: apiKey, + }, + }); + + if (error) { + throw new Error(error.message || "Error verifying API key"); + } + if (!valid || !key) { + return { + session: null, + user: null, + }; + } + + const apiKeyRecord = await db.query.apikey.findFirst({ + where: eq(schema.apikey.id, key.id), + with: { + user: true, + }, + }); + + if (!apiKeyRecord) { + return { + session: null, + user: null, + }; + } + + const organizationId = JSON.parse( + apiKeyRecord.metadata || "{}", + ).organizationId; + + if (!organizationId) { + return { + session: null, + user: null, + }; + } + + const member = await db.query.member.findFirst({ + where: and( + eq(schema.member.userId, apiKeyRecord.user.id), + eq(schema.member.organizationId, organizationId), + ), + with: { + organization: true, + }, + }); + + const { + id, + name, + email, + emailVerified, + image, + createdAt, + updatedAt, + twoFactorEnabled, + } = apiKeyRecord.user; + + const mockSession = { + session: { + user: { + id: apiKeyRecord.user.id, + email: apiKeyRecord.user.email, + name: apiKeyRecord.user.name, + }, + activeOrganizationId: organizationId || "", + }, + user: { + id, + name, + email, + emailVerified, + image, + createdAt, + updatedAt, + twoFactorEnabled, + role: member?.role || "member", + ownerId: member?.organization.ownerId || apiKeyRecord.user.id, + }, + }; + + return mockSession; + } catch (error) { + console.error("Error verifying API key", error); + return { + session: null, + user: null, + }; + } + } + + // If no API key, proceed with normal session validation const session = await api.getSession({ headers: new Headers({ cookie: request.headers.cookie || "", diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index a1901d716..312753ffc 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -1,7 +1,8 @@ import { db } from "@dokploy/server/db"; -import { member, users_temp } from "@dokploy/server/db/schema"; +import { apikey, member, users_temp } from "@dokploy/server/db/schema"; import { TRPCError } from "@trpc/server"; import { and, eq } from "drizzle-orm"; +import { auth } from "../lib/auth"; export type User = typeof users_temp.$inferSelect; @@ -248,3 +249,46 @@ export const updateUser = async (userId: string, userData: Partial) => { return user; }; + +export const createApiKey = async ( + userId: string, + input: { + name: string; + prefix?: string; + expiresIn?: number; + metadata: { + organizationId: string; + }; + rateLimitEnabled?: boolean; + rateLimitTimeWindow?: number; + rateLimitMax?: number; + remaining?: number; + refillAmount?: number; + refillInterval?: number; + }, +) => { + const apiKey = await auth.api.createApiKey({ + body: { + name: input.name, + expiresIn: input.expiresIn, + prefix: input.prefix, + rateLimitEnabled: input.rateLimitEnabled, + rateLimitTimeWindow: input.rateLimitTimeWindow, + rateLimitMax: input.rateLimitMax, + remaining: input.remaining, + refillAmount: input.refillAmount, + refillInterval: input.refillInterval, + userId, + }, + }); + + if (input.metadata) { + await db + .update(apikey) + .set({ + metadata: JSON.stringify(input.metadata), + }) + .where(eq(apikey.id, apiKey.id)); + } + return apiKey; +}; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6e1c088b8..03fb11f7c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -239,8 +239,8 @@ importers: specifier: 5.1.1 version: 5.1.1(encoding@0.1.13) better-auth: - specifier: beta - version: 1.2.0-beta.18(typescript@5.5.3) + specifier: 1.2.0 + version: 1.2.0(typescript@5.5.3) bl: specifier: 6.0.11 version: 6.0.11 @@ -580,8 +580,8 @@ importers: specifier: 5.1.1 version: 5.1.1(encoding@0.1.13) better-auth: - specifier: beta - version: 1.2.0-beta.18(typescript@5.5.3) + specifier: 1.2.0 + version: 1.2.0(typescript@5.5.3) bl: specifier: 6.0.11 version: 6.0.11 @@ -3879,8 +3879,8 @@ packages: before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} - better-auth@1.2.0-beta.18: - resolution: {integrity: sha512-gEjNxmrkFiATTSTcE47rkyTT9vMFMLTjtLNun4W0IWmeqfi4pIbbWpo97foY1DNXXRDkDuajquoD58dzAatQxQ==} + better-auth@1.2.0: + resolution: {integrity: sha512-eIRGOXfix25bh4fgs8jslZAZssufpIkxfEeEokQu5G4wICoDee1wPctkFb8v80PvhtI4dPm28SuAoZaAdRc6Wg==} better-call@1.0.3: resolution: {integrity: sha512-DUKImKoDIy5UtCvQbHTg0wuBRse6gu1Yvznn7+1B3I5TeY8sclRPFce0HI+4WF2bcb+9PqmkET8nXZubrHQh9A==} @@ -10554,7 +10554,7 @@ snapshots: before-after-hook@2.2.3: {} - better-auth@1.2.0-beta.18(typescript@5.5.3): + better-auth@1.2.0(typescript@5.5.3): dependencies: '@better-auth/utils': 0.2.3 '@better-fetch/fetch': 1.1.15 From 0ad923308712447e8e92a8c2acc9430111581c1c Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 20:55:18 -0600 Subject: [PATCH 118/126] feat(logs): enable dynamic log rotation with database state management --- .../server/src/utils/access-log/handler.ts | 54 +++++++++---------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/server/src/utils/access-log/handler.ts b/packages/server/src/utils/access-log/handler.ts index d05d805f3..69d0cc68b 100644 --- a/packages/server/src/utils/access-log/handler.ts +++ b/packages/server/src/utils/access-log/handler.ts @@ -1,6 +1,8 @@ import { IS_CLOUD, paths } from "@dokploy/server/constants"; import { type RotatingFileStream, createStream } from "rotating-file-stream"; import { execAsync } from "../process/execAsync"; +import { findAdmin } from "@dokploy/server/services/admin"; +import { updateUser } from "@dokploy/server/services/user"; class LogRotationManager { private static instance: LogRotationManager; @@ -28,19 +30,18 @@ class LogRotationManager { } private async getStateFromDB(): Promise { - // const setting = await db.query.admins.findFirst({}); - // return setting?.enableLogRotation ?? false; - return false; + const admin = await findAdmin(); + return admin?.user.enableLogRotation ?? false; } - private async setStateInDB(_active: boolean): Promise { - // const admin = await db.query.admins.findFirst({}); - // if (!admin) { - // return; - // } - // await updateAdmin(admin?.authId, { - // enableLogRotation: active, - // }); + private async setStateInDB(active: boolean): Promise { + const admin = await findAdmin(); + if (!admin) { + return; + } + await updateUser(admin.user.id, { + enableLogRotation: active, + }); } private async activateStream(): Promise { @@ -74,26 +75,26 @@ class LogRotationManager { } public async activate(): Promise { - // const currentState = await this.getStateFromDB(); - // if (currentState) { - // return true; - // } + const currentState = await this.getStateFromDB(); + if (currentState) { + return true; + } - // await this.setStateInDB(true); - // await this.activateStream(); + await this.setStateInDB(true); + await this.activateStream(); return true; } public async deactivate(): Promise { console.log("Deactivating log rotation..."); - // const currentState = await this.getStateFromDB(); - // if (!currentState) { - // console.log("Log rotation is already inactive in DB"); - // return true; - // } + const currentState = await this.getStateFromDB(); + if (!currentState) { + console.log("Log rotation is already inactive in DB"); + return true; + } - // await this.setStateInDB(false); - // await this.deactivateStream(); + await this.setStateInDB(false); + await this.deactivateStream(); console.log("Log rotation deactivated successfully"); return true; } @@ -113,9 +114,8 @@ class LogRotationManager { } } public async getStatus(): Promise { - // const dbState = await this.getStateFromDB(); - // return dbState; - return false; + const dbState = await this.getStateFromDB(); + return dbState; } } export const logRotationManager = LogRotationManager.getInstance(); From a3362e0b1513d29ca81f3850a1c056bc2fa73fa9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:30:30 -0600 Subject: [PATCH 119/126] feat(auth): add Google sign-in support for cloud environment --- .../pages/dashboard/settings/users.tsx | 1 - apps/dokploy/pages/index.tsx | 50 ++++++++++++++++++- apps/dokploy/pages/invitation.tsx | 9 ++++ packages/server/src/lib/auth.ts | 31 +++++++----- 4 files changed, 77 insertions(+), 14 deletions(-) diff --git a/apps/dokploy/pages/dashboard/settings/users.tsx b/apps/dokploy/pages/dashboard/settings/users.tsx index 226153145..16f90abb1 100644 --- a/apps/dokploy/pages/dashboard/settings/users.tsx +++ b/apps/dokploy/pages/dashboard/settings/users.tsx @@ -29,7 +29,6 @@ export async function getServerSideProps( const { req, res } = ctx; const { user, session } = await validateRequest(req); - console.log("user", user, session); if (!user || user.role === "member") { return { redirect: { diff --git a/apps/dokploy/pages/index.tsx b/apps/dokploy/pages/index.tsx index 768498e8c..70aa2f10a 100644 --- a/apps/dokploy/pages/index.tsx +++ b/apps/dokploy/pages/index.tsx @@ -63,7 +63,7 @@ export default function Home({ IS_CLOUD }: Props) { const [isBackupCodeModalOpen, setIsBackupCodeModalOpen] = useState(false); const [backupCode, setBackupCode] = useState(""); const [isGithubLoading, setIsGithubLoading] = useState(false); - + const [isGoogleLoading, setIsGoogleLoading] = useState(false); const loginForm = useForm({ resolver: zodResolver(LoginSchema), defaultValues: { @@ -181,6 +181,25 @@ export default function Home({ IS_CLOUD }: Props) { } }; + const handleGoogleSignIn = async () => { + setIsGoogleLoading(true); + try { + const { error } = await authClient.signIn.social({ + provider: "google", + }); + + if (error) { + toast.error(error.message); + return; + } + } catch (error) { + toast.error("An error occurred while signing in with Google", { + description: error instanceof Error ? error.message : "Unknown error", + }); + } finally { + setIsGoogleLoading(false); + } + }; return ( <>
@@ -219,6 +238,35 @@ export default function Home({ IS_CLOUD }: Props) { Sign in with GitHub )} + {IS_CLOUD && ( + + )}
Click the link to verify your email: Verify Email

+ `, }); }, }, @@ -53,7 +55,9 @@ const { handler, api } = betterAuth({ await sendEmail({ email: user.email, subject: "Reset your password", - text: `Click the link to reset your password: ${url}`, + text: ` +

Click the link to reset your password: Reset Password

+ `, }); }, }, @@ -132,16 +136,19 @@ const { handler, api } = betterAuth({ twoFactor(), organization({ async sendInvitationEmail(data, _request) { - const inviteLink = `https://example.com/accept-invitation/${data.id}`; - // https://example.com/accept-invitation/8jlBi9Tb9isDb8mc8Sb85u1BaJYklKB2 - // sendOrganizationInvitation({ - // email: data.email, - // invitedByUsername: data.inviter.user.name, - // invitedByEmail: data.inviter.user.email, - // teamName: data.organization.name, - // inviteLink - // }) - console.log("Invitation link", inviteLink); + if (IS_CLOUD) { + const inviteLink = `http://localhost:3000/invitation?token=${data.id}`; + + console.log("Invitation link", inviteLink); + + await sendEmail({ + email: data.email, + subject: "Invitation to join organization", + text: ` +

You are invited to join ${data.organization.name} on Dokploy. Click the link to accept the invitation: Accept Invitation

+ `, + }); + } }, }), ], From 5c38a8265f1a905f67ee73af2b2a8f8921f46b7f Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:47:31 -0600 Subject: [PATCH 120/126] feat(auth): improve email verification and invitation link generation for cloud environment --- packages/server/src/lib/auth.ts | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index b4605959c..3d96942b7 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -29,14 +29,15 @@ const { handler, api } = betterAuth({ sendOnSignUp: true, autoSignInAfterVerification: true, sendVerificationEmail: async ({ user, url }) => { - console.log("Sending verification email to", user.email); - await sendEmail({ - email: user.email, - subject: "Verify your email", - text: ` + if (IS_CLOUD) { + await sendEmail({ + email: user.email, + subject: "Verify your email", + text: `

Click the link to verify your email: Verify Email

`, - }); + }); + } }, }, emailAndPassword: { @@ -137,9 +138,11 @@ const { handler, api } = betterAuth({ organization({ async sendInvitationEmail(data, _request) { if (IS_CLOUD) { - const inviteLink = `http://localhost:3000/invitation?token=${data.id}`; - - console.log("Invitation link", inviteLink); + const host = + process.env.NODE_ENV === "development" + ? "http://localhost:3000" + : "https://dokploy.com"; + const inviteLink = `${host}/invitation?token=${data.id}`; await sendEmail({ email: data.email, From d7c94174b90cf50286a8572d53f3e0e840fb0bb2 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:49:56 -0600 Subject: [PATCH 121/126] refactor(auth): simplify API key export in authentication module --- packages/server/src/lib/auth.ts | 2 +- packages/server/src/services/user.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 3d96942b7..6bd3ec2ff 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -159,7 +159,7 @@ const { handler, api } = betterAuth({ export const auth = { handler, - api, + createApiKey: api.createApiKey, }; export const validateRequest = async (request: IncomingMessage) => { diff --git a/packages/server/src/services/user.ts b/packages/server/src/services/user.ts index 312753ffc..39ac95cef 100644 --- a/packages/server/src/services/user.ts +++ b/packages/server/src/services/user.ts @@ -267,7 +267,7 @@ export const createApiKey = async ( refillInterval?: number; }, ) => { - const apiKey = await auth.api.createApiKey({ + const apiKey = await auth.createApiKey({ body: { name: input.name, expiresIn: input.expiresIn, From 43599e7a9724ef4d7980b115a8758000902f0a9a Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:54:47 -0600 Subject: [PATCH 122/126] fix(auth): handle null session and user with TypeScript ignore --- apps/dokploy/server/api/trpc.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 8e8206427..4627155f8 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -80,12 +80,14 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { return createInnerTRPCContext({ req, res, + // @ts-ignore session: session ? { ...session, activeOrganizationId: session.activeOrganizationId || "", } : null, + // @ts-ignore user: user ? { ...user, From adeb8498f9d7dd53139edfe55908647d58de7ee9 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 21:56:22 -0600 Subject: [PATCH 123/126] refactor(auth): remove commented-out debug logging in TRPC context --- apps/dokploy/server/api/trpc.ts | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/dokploy/server/api/trpc.ts b/apps/dokploy/server/api/trpc.ts index 4627155f8..4c88eb22d 100644 --- a/apps/dokploy/server/api/trpc.ts +++ b/apps/dokploy/server/api/trpc.ts @@ -68,15 +68,6 @@ export const createTRPCContext = async (opts: CreateNextContextOptions) => { // Get from the request const { session, user } = await validateRequest(req); - // if (!session) { - // const cookieResult = await validateRequest(req); - // session = cookieResult.session; - // user = cookieResult.user; - // } - - // console.log("session", session); - // console.log("user", user); - return createInnerTRPCContext({ req, res, From 13eccaf8d9bb0562373f3ee3fb377a4480b391e0 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 22:14:12 -0600 Subject: [PATCH 124/126] feat(user): add organization count check before user deletion --- .../dashboard/settings/users/show-users.tsx | 30 +++++++++++++++++++ apps/dokploy/server/api/routers/user.ts | 14 +++++++++ 2 files changed, 44 insertions(+) diff --git a/apps/dokploy/components/dashboard/settings/users/show-users.tsx b/apps/dokploy/components/dashboard/settings/users/show-users.tsx index 6847558b7..9580240df 100644 --- a/apps/dokploy/components/dashboard/settings/users/show-users.tsx +++ b/apps/dokploy/components/dashboard/settings/users/show-users.tsx @@ -36,6 +36,7 @@ export const ShowUsers = () => { const { data: isCloud } = api.settings.isCloud.useQuery(); const { data, isLoading, refetch } = api.user.all.useQuery(); const { mutateAsync } = api.user.remove.useMutation(); + const utils = api.useUtils(); return (
@@ -172,6 +173,35 @@ export const ShowUsers = () => { description="Are you sure you want to unlink this user?" type="destructive" onClick={async () => { + if (!isCloud) { + const orgCount = + await utils.user.checkUserOrganizations.fetch( + { + userId: member.user.id, + }, + ); + + console.log(orgCount); + + if (orgCount === 1) { + await mutateAsync({ + userId: member.user.id, + }) + .then(() => { + toast.success( + "User deleted successfully", + ); + refetch(); + }) + .catch(() => { + toast.error( + "Error deleting user", + ); + }); + return; + } + } + const { error } = await authClient.organization.removeMember( { diff --git a/apps/dokploy/server/api/routers/user.ts b/apps/dokploy/server/api/routers/user.ts index 5a84742a0..0b740ab74 100644 --- a/apps/dokploy/server/api/routers/user.ts +++ b/apps/dokploy/server/api/routers/user.ts @@ -313,4 +313,18 @@ export const userRouter = createTRPCRouter({ const apiKey = await createApiKey(ctx.user.id, input); return apiKey; }), + + checkUserOrganizations: protectedProcedure + .input( + z.object({ + userId: z.string(), + }), + ) + .query(async ({ input }) => { + const organizations = await db.query.member.findMany({ + where: eq(member.userId, input.userId), + }); + + return organizations.length; + }), }); From c51d63a4df8816b27dedc3ce913fa6710ac0efbf Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 22:21:03 -0600 Subject: [PATCH 125/126] chore: remove TODO comments and clean up code annotations --- apps/dokploy/components/dashboard/project/add-database.tsx | 1 - apps/dokploy/server/api/routers/notification.ts | 7 ------- apps/dokploy/server/api/routers/ssh-key.ts | 1 - packages/server/src/db/schema/application.ts | 1 - packages/server/src/services/domain.ts | 1 - 5 files changed, 11 deletions(-) diff --git a/apps/dokploy/components/dashboard/project/add-database.tsx b/apps/dokploy/components/dashboard/project/add-database.tsx index cd75ba468..b14e2cfa0 100644 --- a/apps/dokploy/components/dashboard/project/add-database.tsx +++ b/apps/dokploy/components/dashboard/project/add-database.tsx @@ -48,7 +48,6 @@ import { z } from "zod"; type DbType = typeof mySchema._type.type; -// TODO: Change to a real docker images const dockerImageDefaultPlaceholder: Record = { mongo: "mongo:6", mariadb: "mariadb:11", diff --git a/apps/dokploy/server/api/routers/notification.ts b/apps/dokploy/server/api/routers/notification.ts index 48ef50b9d..23283d971 100644 --- a/apps/dokploy/server/api/routers/notification.ts +++ b/apps/dokploy/server/api/routers/notification.ts @@ -51,7 +51,6 @@ import { TRPCError } from "@trpc/server"; import { desc, eq, sql } from "drizzle-orm"; import { z } from "zod"; -// TODO: Uncomment the validations when is cloud ready export const notificationRouter = createTRPCRouter({ createSlack: adminProcedure .input(apiCreateSlack) @@ -75,7 +74,6 @@ export const notificationRouter = createTRPCRouter({ try { const notification = await findNotificationById(input.notificationId); if (notification.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this notification", @@ -129,7 +127,6 @@ export const notificationRouter = createTRPCRouter({ try { const notification = await findNotificationById(input.notificationId); if (notification.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this notification", @@ -184,7 +181,6 @@ export const notificationRouter = createTRPCRouter({ try { const notification = await findNotificationById(input.notificationId); if (notification.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this notification", @@ -247,7 +243,6 @@ export const notificationRouter = createTRPCRouter({ try { const notification = await findNotificationById(input.notificationId); if (notification.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to update this notification", @@ -289,7 +284,6 @@ export const notificationRouter = createTRPCRouter({ try { const notification = await findNotificationById(input.notificationId); if (notification.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to delete this notification", @@ -312,7 +306,6 @@ export const notificationRouter = createTRPCRouter({ .query(async ({ input, ctx }) => { const notification = await findNotificationById(input.notificationId); if (notification.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not authorized to access this notification", diff --git a/apps/dokploy/server/api/routers/ssh-key.ts b/apps/dokploy/server/api/routers/ssh-key.ts index fe321de4f..4663af8f2 100644 --- a/apps/dokploy/server/api/routers/ssh-key.ts +++ b/apps/dokploy/server/api/routers/ssh-key.ts @@ -41,7 +41,6 @@ export const sshRouter = createTRPCRouter({ try { const sshKey = await findSSHKeyById(input.sshKeyId); if (sshKey.organizationId !== ctx.session.activeOrganizationId) { - // TODO: Remove isCloud in the next versions of dokploy throw new TRPCError({ code: "UNAUTHORIZED", message: "You are not allowed to delete this SSH key", diff --git a/packages/server/src/db/schema/application.ts b/packages/server/src/db/schema/application.ts index 2437f59d6..e670e2e24 100644 --- a/packages/server/src/db/schema/application.ts +++ b/packages/server/src/db/schema/application.ts @@ -44,7 +44,6 @@ export const buildType = pgEnum("buildType", [ "static", ]); -// TODO: refactor this types export interface HealthCheckSwarm { Test?: string[] | undefined; Interval?: number | undefined; diff --git a/packages/server/src/services/domain.ts b/packages/server/src/services/domain.ts index fe068fc22..d2e23c06b 100644 --- a/packages/server/src/services/domain.ts +++ b/packages/server/src/services/domain.ts @@ -126,7 +126,6 @@ export const updateDomainById = async ( export const removeDomainById = async (domainId: string) => { await findDomainById(domainId); - // TODO: fix order const result = await db .delete(domains) .where(eq(domains.domainId, domainId)) From 5fb286666051e129c4871d0186f7fb9f36aef6e6 Mon Sep 17 00:00:00 2001 From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com> Date: Sat, 1 Mar 2025 22:48:09 -0600 Subject: [PATCH 126/126] feat(auth): conditionally disable authentication logger in production --- packages/server/src/lib/auth.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/server/src/lib/auth.ts b/packages/server/src/lib/auth.ts index 6bd3ec2ff..1efa17300 100644 --- a/packages/server/src/lib/auth.ts +++ b/packages/server/src/lib/auth.ts @@ -14,6 +14,9 @@ const { handler, api } = betterAuth({ provider: "pg", schema: schema, }), + logger: { + disabled: process.env.NODE_ENV === "production", + }, appName: "Dokploy", socialProviders: { github: {