Enhance API with OIDC support, including login and callback endpoints. Update environment variables for OIDC configuration in .env.example. Add new features to the catalog service for listing software families, Minecraft versions, and deployment regions. Implement server management actions such as kill, delete, and update in the servers module. Integrate feature flags for maintenance mode in server operations. Update pnpm-lock.yaml with new dependencies and versions.
Some checks failed
CI / Node — lint, typecheck, test, build (push) Failing after 12s
CI / Go — node-agent tests (push) Failing after 9s
CI / Go — edge-gateway build (push) Successful in 17s

This commit is contained in:
TheOnlyMace
2026-07-05 18:39:53 +02:00
parent bf36cb3159
commit 50cd4b3ffd
225 changed files with 17824 additions and 436 deletions

View File

@@ -0,0 +1,18 @@
-- Gap closure migration: permissions, catalog, compliance, WHMCS mappings, server states
-- AlterEnum
ALTER TYPE "GameServerStatus" ADD VALUE IF NOT EXISTS 'ALLOCATING';
ALTER TYPE "GameServerStatus" ADD VALUE IF NOT EXISTS 'MIGRATING';
ALTER TYPE "GameServerStatus" ADD VALUE IF NOT EXISTS 'SUSPENDED';
ALTER TYPE "GameServerStatus" ADD VALUE IF NOT EXISTS 'MAINTENANCE';
-- CreateEnum
CREATE TYPE "GameServerMemberRole" AS ENUM ('OWNER', 'ADMIN', 'OPERATOR', 'DEVELOPER', 'VIEWER');
CREATE TYPE "OrganizationMemberRole" AS ENUM ('OWNER', 'ADMIN', 'MEMBER');
CREATE TYPE "NotificationChannel" AS ENUM ('IN_APP', 'EMAIL', 'WEBHOOK', 'DISCORD');
CREATE TYPE "NotificationType" AS ENUM ('SERVER_READY', 'SERVER_START_FAILED', 'SERVER_STOPPED', 'IDLE_STOP_WARNING', 'BACKUP_SUCCESS', 'BACKUP_FAILED', 'RESTORE_SUCCESS', 'RESTORE_FAILED', 'CREDITS_LOW', 'BILLING_ISSUE', 'INVITE', 'NODE_INCIDENT', 'MAINTENANCE', 'SECURITY', 'NEW_LOGIN', 'TWO_FA_CHANGED');
CREATE TYPE "AbuseReportStatus" AS ENUM ('OPEN', 'INVESTIGATING', 'RESOLVED', 'DISMISSED');
CREATE TYPE "SupportTicketStatus" AS ENUM ('OPEN', 'IN_PROGRESS', 'WAITING', 'RESOLVED', 'CLOSED');
CREATE TYPE "SupportTicketPriority" AS ENUM ('LOW', 'NORMAL', 'HIGH', 'URGENT');
CREATE TYPE "ScheduleTaskType" AS ENUM ('START', 'STOP', 'RESTART', 'BACKUP', 'CONSOLE_COMMAND', 'UPDATE_CHECK', 'MAINTENANCE');
CREATE TYPE "CatalogProviderKind" AS ENUM ('MODRINTH', 'CURSEFORGE', 'MOJANG', 'PAPERMC', 'CUSTOM');

View File

@@ -0,0 +1,768 @@
-- Gap closure tables: organizations, catalog, schedules, notifications, compliance, billing, admin
-- CreateEnum
DO $$ BEGIN
CREATE TYPE "JobStatus" AS ENUM ('PENDING', 'RUNNING', 'COMPLETED', 'FAILED', 'CANCELLED');
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
-- CreateTable
CREATE TABLE IF NOT EXISTS "organizations" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "organizations_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "organization_members" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"role" "OrganizationMemberRole" NOT NULL DEFAULT 'MEMBER',
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "organization_members_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "organization_invites" (
"id" TEXT NOT NULL,
"organizationId" TEXT NOT NULL,
"email" TEXT NOT NULL,
"role" "OrganizationMemberRole" NOT NULL DEFAULT 'MEMBER',
"tokenHash" TEXT NOT NULL,
"invitedById" TEXT NOT NULL,
"expiresAt" TIMESTAMPTZ(3) NOT NULL,
"acceptedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "organization_invites_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "game_server_members" (
"id" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"role" "GameServerMemberRole" NOT NULL DEFAULT 'VIEWER',
"permissions" JSONB,
"invitedAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"acceptedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "game_server_members_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "game_server_properties" (
"id" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "game_server_properties_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "game_server_environment_variables" (
"id" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" TEXT NOT NULL,
"isSecret" BOOLEAN NOT NULL DEFAULT false,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "game_server_environment_variables_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "game_server_secrets" (
"id" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"valueCiphertext" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "game_server_secrets_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "game_node_capabilities" (
"id" TEXT NOT NULL,
"nodeId" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "game_node_capabilities_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "game_node_maintenance_windows" (
"id" TEXT NOT NULL,
"nodeId" TEXT NOT NULL,
"startsAt" TIMESTAMPTZ(3) NOT NULL,
"endsAt" TIMESTAMPTZ(3) NOT NULL,
"drainFirst" BOOLEAN NOT NULL DEFAULT true,
"message" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "game_node_maintenance_windows_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "minecraft_versions" (
"id" TEXT NOT NULL,
"version" TEXT NOT NULL,
"edition" "ServerEdition" NOT NULL DEFAULT 'JAVA',
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "minecraft_versions_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "software_builds" (
"id" TEXT NOT NULL,
"family" "SoftwareFamily" NOT NULL,
"minecraftVersionId" TEXT NOT NULL,
"downloadUrl" TEXT,
"sha256" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "software_builds_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "runtime_images" (
"id" TEXT NOT NULL,
"name" TEXT NOT NULL,
"imageRef" TEXT NOT NULL,
"digest" TEXT,
"javaVersion" TEXT,
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "runtime_images_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "catalog_providers" (
"id" TEXT NOT NULL,
"kind" "CatalogProviderKind" NOT NULL,
"name" TEXT NOT NULL,
"isEnabled" BOOLEAN NOT NULL DEFAULT false,
"config" JSONB,
"lastSyncAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "catalog_providers_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "catalog_projects" (
"id" TEXT NOT NULL,
"providerId" TEXT NOT NULL,
"externalId" TEXT NOT NULL,
"slug" TEXT NOT NULL,
"name" TEXT NOT NULL,
"projectType" TEXT NOT NULL,
"metadata" JSONB,
"syncedAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "catalog_projects_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "catalog_versions" (
"id" TEXT NOT NULL,
"projectId" TEXT NOT NULL,
"externalId" TEXT NOT NULL,
"versionNumber" TEXT NOT NULL,
"gameVersions" JSONB,
"loaders" JSONB,
"sha512" TEXT,
"downloadUrl" TEXT,
"syncedAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "catalog_versions_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "catalog_dependencies" (
"id" TEXT NOT NULL,
"versionId" TEXT NOT NULL,
"dependencyId" TEXT NOT NULL,
"dependencyType" TEXT NOT NULL,
CONSTRAINT "catalog_dependencies_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "worlds" (
"id" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"isActive" BOOLEAN NOT NULL DEFAULT false,
"sizeBytes" BIGINT,
"hasLevelDat" BOOLEAN NOT NULL DEFAULT false,
"seed" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "worlds_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "world_archives" (
"id" TEXT NOT NULL,
"worldId" TEXT NOT NULL,
"s3Key" TEXT NOT NULL,
"sha256" TEXT,
"sizeBytes" BIGINT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "world_archives_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "schedules" (
"id" TEXT NOT NULL,
"serverId" TEXT NOT NULL,
"name" TEXT NOT NULL,
"timezone" TEXT NOT NULL DEFAULT 'UTC',
"cronExpr" TEXT NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"nextRunAt" TIMESTAMPTZ(3),
"lastRunAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "schedules_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "scheduled_tasks" (
"id" TEXT NOT NULL,
"scheduleId" TEXT NOT NULL,
"taskType" "ScheduleTaskType" NOT NULL,
"payload" JSONB,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "scheduled_tasks_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "notifications" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"type" "NotificationType" NOT NULL,
"channel" "NotificationChannel" NOT NULL DEFAULT 'IN_APP',
"title" TEXT NOT NULL,
"body" TEXT NOT NULL,
"metadata" JSONB,
"readAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "notifications_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "notification_preferences" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"type" "NotificationType" NOT NULL,
"channel" "NotificationChannel" NOT NULL,
"enabled" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "notification_preferences_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "abuse_reports" (
"id" TEXT NOT NULL,
"reporterId" TEXT,
"targetUserId" TEXT,
"serverId" TEXT,
"status" "AbuseReportStatus" NOT NULL DEFAULT 'OPEN',
"reason" TEXT NOT NULL,
"details" TEXT,
"resolution" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "abuse_reports_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "support_tickets" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"serverId" TEXT,
"subject" TEXT NOT NULL,
"status" "SupportTicketStatus" NOT NULL DEFAULT 'OPEN',
"priority" "SupportTicketPriority" NOT NULL DEFAULT 'NORMAL',
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "support_tickets_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "support_ticket_messages" (
"id" TEXT NOT NULL,
"ticketId" TEXT NOT NULL,
"authorId" TEXT,
"isInternal" BOOLEAN NOT NULL DEFAULT false,
"body" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "support_ticket_messages_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "subscriptions" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"planId" TEXT NOT NULL,
"status" TEXT NOT NULL,
"startsAt" TIMESTAMPTZ(3) NOT NULL,
"endsAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "subscriptions_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "invoices" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"subscriptionId" TEXT,
"amountCents" INTEGER NOT NULL,
"currency" TEXT NOT NULL DEFAULT 'EUR',
"status" TEXT NOT NULL,
"externalRef" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "invoices_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "payments" (
"id" TEXT NOT NULL,
"invoiceId" TEXT NOT NULL,
"amountCents" INTEGER NOT NULL,
"provider" TEXT NOT NULL,
"externalRef" TEXT,
"status" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "payments_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "resource_quotas" (
"id" TEXT NOT NULL,
"userId" TEXT,
"planId" TEXT,
"key" TEXT NOT NULL,
"limit" INTEGER NOT NULL,
"used" INTEGER NOT NULL DEFAULT 0,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "resource_quotas_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "security_change_logs" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"action" TEXT NOT NULL,
"metadata" JSONB,
"ipAddress" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "security_change_logs_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "data_export_requests" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'PENDING',
"downloadUrl" TEXT,
"expiresAt" TIMESTAMPTZ(3),
"completedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "data_export_requests_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "account_deletion_requests" (
"id" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"status" TEXT NOT NULL DEFAULT 'PENDING',
"scheduledFor" TIMESTAMPTZ(3) NOT NULL,
"completedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "account_deletion_requests_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "whmcs_user_links" (
"id" TEXT NOT NULL,
"installationId" TEXT NOT NULL,
"externalUserId" TEXT NOT NULL,
"externalClientId" TEXT NOT NULL,
"userId" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "whmcs_user_links_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "whmcs_product_mappings" (
"id" TEXT NOT NULL,
"installationId" TEXT NOT NULL,
"externalProductId" TEXT NOT NULL,
"planSlug" TEXT NOT NULL,
"defaultEdition" "ServerEdition" NOT NULL DEFAULT 'JAVA',
"defaultSoftware" "SoftwareFamily" NOT NULL DEFAULT 'VANILLA',
"defaultVersion" TEXT NOT NULL DEFAULT '1.21.1',
"isActive" BOOLEAN NOT NULL DEFAULT true,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "whmcs_product_mappings_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "whmcs_option_mappings" (
"id" TEXT NOT NULL,
"installationId" TEXT NOT NULL,
"externalOptionId" TEXT NOT NULL,
"externalValueId" TEXT NOT NULL,
"resourceKey" TEXT NOT NULL,
"normalizedValue" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "whmcs_option_mappings_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "whmcs_addon_mappings" (
"id" TEXT NOT NULL,
"installationId" TEXT NOT NULL,
"externalAddonId" TEXT NOT NULL,
"featureKey" TEXT NOT NULL,
"deltaValue" TEXT NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "whmcs_addon_mappings_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "feature_flags" (
"id" TEXT NOT NULL,
"key" TEXT NOT NULL,
"description" TEXT,
"enabled" BOOLEAN NOT NULL DEFAULT false,
"metadata" JSONB,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "feature_flags_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "system_settings" (
"id" TEXT NOT NULL,
"key" TEXT NOT NULL,
"value" JSONB NOT NULL,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "system_settings_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "job_records" (
"id" TEXT NOT NULL,
"queue" TEXT NOT NULL,
"jobName" TEXT NOT NULL,
"status" "JobStatus" NOT NULL DEFAULT 'PENDING',
"payload" JSONB,
"result" JSONB,
"error" TEXT,
"attempts" INTEGER NOT NULL DEFAULT 0,
"startedAt" TIMESTAMPTZ(3),
"completedAt" TIMESTAMPTZ(3),
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMPTZ(3) NOT NULL,
CONSTRAINT "job_records_pkey" PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "audit_events" (
"id" TEXT NOT NULL,
"userId" TEXT,
"action" TEXT NOT NULL,
"entityType" TEXT,
"entityId" TEXT,
"metadata" JSONB,
"ipAddress" TEXT,
"createdAt" TIMESTAMPTZ(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "audit_events_pkey" PRIMARY KEY ("id")
);
-- CreateIndex
CREATE UNIQUE INDEX IF NOT EXISTS "organizations_slug_key" ON "organizations"("slug");
CREATE UNIQUE INDEX IF NOT EXISTS "organization_members_organizationId_userId_key" ON "organization_members"("organizationId", "userId");
CREATE INDEX IF NOT EXISTS "organization_members_userId_idx" ON "organization_members"("userId");
CREATE UNIQUE INDEX IF NOT EXISTS "organization_invites_tokenHash_key" ON "organization_invites"("tokenHash");
CREATE INDEX IF NOT EXISTS "organization_invites_organizationId_idx" ON "organization_invites"("organizationId");
CREATE INDEX IF NOT EXISTS "organization_invites_email_idx" ON "organization_invites"("email");
CREATE UNIQUE INDEX IF NOT EXISTS "game_server_members_serverId_userId_key" ON "game_server_members"("serverId", "userId");
CREATE INDEX IF NOT EXISTS "game_server_members_userId_idx" ON "game_server_members"("userId");
CREATE UNIQUE INDEX IF NOT EXISTS "game_server_properties_serverId_key_key" ON "game_server_properties"("serverId", "key");
CREATE UNIQUE INDEX IF NOT EXISTS "game_server_environment_variables_serverId_key_key" ON "game_server_environment_variables"("serverId", "key");
CREATE UNIQUE INDEX IF NOT EXISTS "game_server_secrets_serverId_name_key" ON "game_server_secrets"("serverId", "name");
CREATE UNIQUE INDEX IF NOT EXISTS "game_node_capabilities_nodeId_key_key" ON "game_node_capabilities"("nodeId", "key");
CREATE INDEX IF NOT EXISTS "game_node_maintenance_windows_nodeId_startsAt_idx" ON "game_node_maintenance_windows"("nodeId", "startsAt");
CREATE UNIQUE INDEX IF NOT EXISTS "minecraft_versions_version_key" ON "minecraft_versions"("version");
CREATE UNIQUE INDEX IF NOT EXISTS "software_builds_family_minecraftVersionId_key" ON "software_builds"("family", "minecraftVersionId");
CREATE UNIQUE INDEX IF NOT EXISTS "runtime_images_name_key" ON "runtime_images"("name");
CREATE UNIQUE INDEX IF NOT EXISTS "catalog_providers_kind_key" ON "catalog_providers"("kind");
CREATE UNIQUE INDEX IF NOT EXISTS "catalog_projects_providerId_externalId_key" ON "catalog_projects"("providerId", "externalId");
CREATE INDEX IF NOT EXISTS "catalog_projects_slug_idx" ON "catalog_projects"("slug");
CREATE UNIQUE INDEX IF NOT EXISTS "catalog_versions_projectId_externalId_key" ON "catalog_versions"("projectId", "externalId");
CREATE INDEX IF NOT EXISTS "catalog_dependencies_versionId_idx" ON "catalog_dependencies"("versionId");
CREATE UNIQUE INDEX IF NOT EXISTS "worlds_serverId_name_key" ON "worlds"("serverId", "name");
CREATE INDEX IF NOT EXISTS "world_archives_worldId_idx" ON "world_archives"("worldId");
CREATE INDEX IF NOT EXISTS "schedules_serverId_idx" ON "schedules"("serverId");
CREATE INDEX IF NOT EXISTS "scheduled_tasks_scheduleId_idx" ON "scheduled_tasks"("scheduleId");
CREATE INDEX IF NOT EXISTS "notifications_userId_readAt_idx" ON "notifications"("userId", "readAt");
CREATE INDEX IF NOT EXISTS "notifications_createdAt_idx" ON "notifications"("createdAt");
CREATE UNIQUE INDEX IF NOT EXISTS "notification_preferences_userId_type_channel_key" ON "notification_preferences"("userId", "type", "channel");
CREATE INDEX IF NOT EXISTS "abuse_reports_status_idx" ON "abuse_reports"("status");
CREATE INDEX IF NOT EXISTS "abuse_reports_serverId_idx" ON "abuse_reports"("serverId");
CREATE INDEX IF NOT EXISTS "support_tickets_userId_idx" ON "support_tickets"("userId");
CREATE INDEX IF NOT EXISTS "support_tickets_status_idx" ON "support_tickets"("status");
CREATE INDEX IF NOT EXISTS "support_ticket_messages_ticketId_idx" ON "support_ticket_messages"("ticketId");
CREATE INDEX IF NOT EXISTS "subscriptions_userId_idx" ON "subscriptions"("userId");
CREATE INDEX IF NOT EXISTS "invoices_userId_idx" ON "invoices"("userId");
CREATE INDEX IF NOT EXISTS "payments_invoiceId_idx" ON "payments"("invoiceId");
CREATE UNIQUE INDEX IF NOT EXISTS "resource_quotas_userId_planId_key_key" ON "resource_quotas"("userId", "planId", "key");
CREATE INDEX IF NOT EXISTS "security_change_logs_userId_createdAt_idx" ON "security_change_logs"("userId", "createdAt");
CREATE INDEX IF NOT EXISTS "data_export_requests_userId_idx" ON "data_export_requests"("userId");
CREATE INDEX IF NOT EXISTS "account_deletion_requests_userId_idx" ON "account_deletion_requests"("userId");
CREATE UNIQUE INDEX IF NOT EXISTS "whmcs_user_links_installationId_externalUserId_key" ON "whmcs_user_links"("installationId", "externalUserId");
CREATE INDEX IF NOT EXISTS "whmcs_user_links_userId_idx" ON "whmcs_user_links"("userId");
CREATE UNIQUE INDEX IF NOT EXISTS "whmcs_product_mappings_installationId_externalProductId_key" ON "whmcs_product_mappings"("installationId", "externalProductId");
CREATE UNIQUE INDEX IF NOT EXISTS "whmcs_option_mappings_installationId_externalOptionId_externalValueId_key" ON "whmcs_option_mappings"("installationId", "externalOptionId", "externalValueId");
CREATE UNIQUE INDEX IF NOT EXISTS "whmcs_addon_mappings_installationId_externalAddonId_key" ON "whmcs_addon_mappings"("installationId", "externalAddonId");
CREATE UNIQUE INDEX IF NOT EXISTS "feature_flags_key_key" ON "feature_flags"("key");
CREATE UNIQUE INDEX IF NOT EXISTS "system_settings_key_key" ON "system_settings"("key");
CREATE INDEX IF NOT EXISTS "job_records_queue_status_idx" ON "job_records"("queue", "status");
CREATE INDEX IF NOT EXISTS "job_records_createdAt_idx" ON "job_records"("createdAt");
CREATE INDEX IF NOT EXISTS "audit_events_userId_idx" ON "audit_events"("userId");
CREATE INDEX IF NOT EXISTS "audit_events_action_idx" ON "audit_events"("action");
CREATE INDEX IF NOT EXISTS "audit_events_entityType_entityId_idx" ON "audit_events"("entityType", "entityId");
CREATE INDEX IF NOT EXISTS "audit_events_createdAt_idx" ON "audit_events"("createdAt");
-- AddForeignKey
DO $$ BEGIN
ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "organization_members" ADD CONSTRAINT "organization_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "organization_invites" ADD CONSTRAINT "organization_invites_organizationId_fkey" FOREIGN KEY ("organizationId") REFERENCES "organizations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "organization_invites" ADD CONSTRAINT "organization_invites_invitedById_fkey" FOREIGN KEY ("invitedById") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_server_members" ADD CONSTRAINT "game_server_members_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "game_servers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_server_members" ADD CONSTRAINT "game_server_members_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_server_properties" ADD CONSTRAINT "game_server_properties_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "game_servers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_server_environment_variables" ADD CONSTRAINT "game_server_environment_variables_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "game_servers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_server_secrets" ADD CONSTRAINT "game_server_secrets_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "game_servers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_node_capabilities" ADD CONSTRAINT "game_node_capabilities_nodeId_fkey" FOREIGN KEY ("nodeId") REFERENCES "game_nodes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "game_node_maintenance_windows" ADD CONSTRAINT "game_node_maintenance_windows_nodeId_fkey" FOREIGN KEY ("nodeId") REFERENCES "game_nodes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "software_builds" ADD CONSTRAINT "software_builds_minecraftVersionId_fkey" FOREIGN KEY ("minecraftVersionId") REFERENCES "minecraft_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "catalog_projects" ADD CONSTRAINT "catalog_projects_providerId_fkey" FOREIGN KEY ("providerId") REFERENCES "catalog_providers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "catalog_versions" ADD CONSTRAINT "catalog_versions_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "catalog_projects"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "catalog_dependencies" ADD CONSTRAINT "catalog_dependencies_versionId_fkey" FOREIGN KEY ("versionId") REFERENCES "catalog_versions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "worlds" ADD CONSTRAINT "worlds_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "game_servers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "world_archives" ADD CONSTRAINT "world_archives_worldId_fkey" FOREIGN KEY ("worldId") REFERENCES "worlds"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "schedules" ADD CONSTRAINT "schedules_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "game_servers"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "scheduled_tasks" ADD CONSTRAINT "scheduled_tasks_scheduleId_fkey" FOREIGN KEY ("scheduleId") REFERENCES "schedules"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "notifications" ADD CONSTRAINT "notifications_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "notification_preferences" ADD CONSTRAINT "notification_preferences_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "abuse_reports" ADD CONSTRAINT "abuse_reports_reporterId_fkey" FOREIGN KEY ("reporterId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "support_tickets" ADD CONSTRAINT "support_tickets_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "support_ticket_messages" ADD CONSTRAINT "support_ticket_messages_ticketId_fkey" FOREIGN KEY ("ticketId") REFERENCES "support_tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "payments" ADD CONSTRAINT "payments_invoiceId_fkey" FOREIGN KEY ("invoiceId") REFERENCES "invoices"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "security_change_logs" ADD CONSTRAINT "security_change_logs_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "data_export_requests" ADD CONSTRAINT "data_export_requests_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "account_deletion_requests" ADD CONSTRAINT "account_deletion_requests_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "whmcs_user_links" ADD CONSTRAINT "whmcs_user_links_installationId_fkey" FOREIGN KEY ("installationId") REFERENCES "whmcs_installations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "whmcs_user_links" ADD CONSTRAINT "whmcs_user_links_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "whmcs_product_mappings" ADD CONSTRAINT "whmcs_product_mappings_installationId_fkey" FOREIGN KEY ("installationId") REFERENCES "whmcs_installations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "whmcs_option_mappings" ADD CONSTRAINT "whmcs_option_mappings_installationId_fkey" FOREIGN KEY ("installationId") REFERENCES "whmcs_installations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "whmcs_addon_mappings" ADD CONSTRAINT "whmcs_addon_mappings_installationId_fkey" FOREIGN KEY ("installationId") REFERENCES "whmcs_installations"("id") ON DELETE CASCADE ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;
DO $$ BEGIN
ALTER TABLE "audit_events" ADD CONSTRAINT "audit_events_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
EXCEPTION WHEN duplicate_object THEN NULL;
END $$;

View File

@@ -31,6 +31,7 @@ enum SoftwareFamily {
enum GameServerStatus {
DRAFT
ALLOCATING
PROVISIONING
INSTALLING
STOPPED
@@ -40,12 +41,95 @@ enum GameServerStatus {
STOPPING
BACKING_UP
RESTORING
MIGRATING
SUSPENDED
MAINTENANCE
UNKNOWN
ERROR
DELETING
DELETED
}
enum GameServerMemberRole {
OWNER
ADMIN
OPERATOR
DEVELOPER
VIEWER
}
enum OrganizationMemberRole {
OWNER
ADMIN
MEMBER
}
enum NotificationChannel {
IN_APP
EMAIL
WEBHOOK
DISCORD
}
enum NotificationType {
SERVER_READY
SERVER_START_FAILED
SERVER_STOPPED
IDLE_STOP_WARNING
BACKUP_SUCCESS
BACKUP_FAILED
RESTORE_SUCCESS
RESTORE_FAILED
CREDITS_LOW
BILLING_ISSUE
INVITE
NODE_INCIDENT
MAINTENANCE
SECURITY
NEW_LOGIN
TWO_FA_CHANGED
}
enum AbuseReportStatus {
OPEN
INVESTIGATING
RESOLVED
DISMISSED
}
enum SupportTicketStatus {
OPEN
IN_PROGRESS
WAITING
RESOLVED
CLOSED
}
enum SupportTicketPriority {
LOW
NORMAL
HIGH
URGENT
}
enum ScheduleTaskType {
START
STOP
RESTART
BACKUP
CONSOLE_COMMAND
UPDATE_CHECK
MAINTENANCE
}
enum CatalogProviderKind {
MODRINTH
CURSEFORGE
MOJANG
PAPERMC
CUSTOM
}
enum GameNodeStatus {
ONLINE
OFFLINE
@@ -194,7 +278,18 @@ model User {
totpCredential TotpCredential?
creditWallet CreditWallet?
whmcsClientLink WhmcsClientLink?
whmcsUserLinks WhmcsUserLink[]
ssoTickets SsoTicket[]
organizationMembers OrganizationMember[]
organizationInvites OrganizationInvite[] @relation("OrganizationInviteInviter")
gameServerMembers GameServerMember[]
notifications Notification[]
notificationPreferences NotificationPreference[]
abuseReports AbuseReport[]
supportTickets SupportTicket[]
securityChangeLogs SecurityChangeLog[]
dataExportRequests DataExportRequest[]
accountDeletionRequests AccountDeletionRequest[]
@@map("users")
}
@@ -361,6 +456,12 @@ model GameServer {
dnsRecords ServerDnsRecord[]
whmcsServiceLink WhmcsServiceLink?
ssoTickets SsoTicket[]
members GameServerMember[]
worlds World[]
schedules Schedule[]
properties GameServerProperty[]
environmentVars GameServerEnvironmentVariable[]
secrets GameServerSecret[]
@@index([userId])
@@index([nodeId])
@@ -430,6 +531,8 @@ model GameNode {
heartbeats GameNodeHeartbeat[]
allocations GameServerAllocation[]
startQueueEntries ServerStartQueueEntry[]
capabilities GameNodeCapability[]
maintenanceWindows GameNodeMaintenanceWindow[]
@@map("game_nodes")
}
@@ -743,7 +846,11 @@ model WhmcsInstallation {
updatedAt DateTime @updatedAt @db.Timestamptz(3)
clientLinks WhmcsClientLink[]
userLinks WhmcsUserLink[]
serviceLinks WhmcsServiceLink[]
productMappings WhmcsProductMapping[]
optionMappings WhmcsOptionMapping[]
addonMappings WhmcsAddonMapping[]
operations IntegrationOperation[]
ssoTickets SsoTicket[]
events IntegrationEvent[]
@@ -907,3 +1014,562 @@ model WhmcsUsageExport {
@@index([installationId, periodEnd])
@@map("whmcs_usage_exports")
}
model Organization {
id String @id @default(uuid())
name String
slug String @unique
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
members OrganizationMember[]
invites OrganizationInvite[]
@@map("organizations")
}
model OrganizationMember {
id String @id @default(uuid())
organizationId String
userId String
role OrganizationMemberRole @default(MEMBER)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([organizationId, userId])
@@index([userId])
@@map("organization_members")
}
model OrganizationInvite {
id String @id @default(uuid())
organizationId String
email String
role OrganizationMemberRole @default(MEMBER)
tokenHash String @unique
invitedById String
expiresAt DateTime @db.Timestamptz(3)
acceptedAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
organization Organization @relation(fields: [organizationId], references: [id], onDelete: Cascade)
invitedBy User @relation("OrganizationInviteInviter", fields: [invitedById], references: [id], onDelete: Cascade)
@@index([organizationId])
@@index([email])
@@map("organization_invites")
}
model GameServerMember {
id String @id @default(uuid())
serverId String
userId String
role GameServerMemberRole @default(VIEWER)
permissions Json?
invitedAt DateTime @default(now()) @db.Timestamptz(3)
acceptedAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([serverId, userId])
@@index([userId])
@@map("game_server_members")
}
model GameServerProperty {
id String @id @default(uuid())
serverId String
key String
value String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
@@unique([serverId, key])
@@map("game_server_properties")
}
model GameServerEnvironmentVariable {
id String @id @default(uuid())
serverId String
key String
value String
isSecret Boolean @default(false)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
@@unique([serverId, key])
@@map("game_server_environment_variables")
}
model GameServerSecret {
id String @id @default(uuid())
serverId String
name String
valueCiphertext String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
@@unique([serverId, name])
@@map("game_server_secrets")
}
model GameNodeCapability {
id String @id @default(uuid())
nodeId String
key String
value String?
createdAt DateTime @default(now()) @db.Timestamptz(3)
node GameNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
@@unique([nodeId, key])
@@map("game_node_capabilities")
}
model GameNodeMaintenanceWindow {
id String @id @default(uuid())
nodeId String
startsAt DateTime @db.Timestamptz(3)
endsAt DateTime @db.Timestamptz(3)
drainFirst Boolean @default(true)
message String?
createdAt DateTime @default(now()) @db.Timestamptz(3)
node GameNode @relation(fields: [nodeId], references: [id], onDelete: Cascade)
@@index([nodeId, startsAt])
@@map("game_node_maintenance_windows")
}
model MinecraftVersion {
id String @id @default(uuid())
version String @unique
edition ServerEdition @default(JAVA)
isActive Boolean @default(true)
createdAt DateTime @default(now()) @db.Timestamptz(3)
softwareBuilds SoftwareBuild[]
@@map("minecraft_versions")
}
model SoftwareBuild {
id String @id @default(uuid())
family SoftwareFamily
minecraftVersionId String
downloadUrl String?
sha256 String?
isActive Boolean @default(true)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
minecraftVersion MinecraftVersion @relation(fields: [minecraftVersionId], references: [id], onDelete: Cascade)
@@unique([family, minecraftVersionId])
@@map("software_builds")
}
model RuntimeImage {
id String @id @default(uuid())
name String @unique
imageRef String
digest String?
javaVersion String?
isActive Boolean @default(true)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
@@map("runtime_images")
}
model CatalogProvider {
id String @id @default(uuid())
kind CatalogProviderKind @unique
name String
isEnabled Boolean @default(false)
config Json?
lastSyncAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
projects CatalogProject[]
@@map("catalog_providers")
}
model CatalogProject {
id String @id @default(uuid())
providerId String
externalId String
slug String
name String
projectType String
metadata Json?
syncedAt DateTime @default(now()) @db.Timestamptz(3)
provider CatalogProvider @relation(fields: [providerId], references: [id], onDelete: Cascade)
versions CatalogVersion[]
@@unique([providerId, externalId])
@@index([slug])
@@map("catalog_projects")
}
model CatalogVersion {
id String @id @default(uuid())
projectId String
externalId String
versionNumber String
gameVersions Json?
loaders Json?
sha512 String?
downloadUrl String?
syncedAt DateTime @default(now()) @db.Timestamptz(3)
project CatalogProject @relation(fields: [projectId], references: [id], onDelete: Cascade)
dependencies CatalogDependency[] @relation("VersionDependencies")
@@unique([projectId, externalId])
@@map("catalog_versions")
}
model CatalogDependency {
id String @id @default(uuid())
versionId String
dependencyId String
dependencyType String
version CatalogVersion @relation("VersionDependencies", fields: [versionId], references: [id], onDelete: Cascade)
@@index([versionId])
@@map("catalog_dependencies")
}
model World {
id String @id @default(uuid())
serverId String
name String
isActive Boolean @default(false)
sizeBytes BigInt?
hasLevelDat Boolean @default(false)
seed String?
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
archives WorldArchive[]
@@unique([serverId, name])
@@map("worlds")
}
model WorldArchive {
id String @id @default(uuid())
worldId String
s3Key String
sha256 String?
sizeBytes BigInt?
createdAt DateTime @default(now()) @db.Timestamptz(3)
world World @relation(fields: [worldId], references: [id], onDelete: Cascade)
@@index([worldId])
@@map("world_archives")
}
model Schedule {
id String @id @default(uuid())
serverId String
name String
timezone String @default("UTC")
cronExpr String
enabled Boolean @default(true)
nextRunAt DateTime? @db.Timestamptz(3)
lastRunAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
server GameServer @relation(fields: [serverId], references: [id], onDelete: Cascade)
tasks ScheduledTask[]
@@index([serverId])
@@map("schedules")
}
model ScheduledTask {
id String @id @default(uuid())
scheduleId String
taskType ScheduleTaskType
payload Json?
createdAt DateTime @default(now()) @db.Timestamptz(3)
schedule Schedule @relation(fields: [scheduleId], references: [id], onDelete: Cascade)
@@index([scheduleId])
@@map("scheduled_tasks")
}
model Notification {
id String @id @default(uuid())
userId String
type NotificationType
channel NotificationChannel @default(IN_APP)
title String
body String
metadata Json?
readAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, readAt])
@@index([createdAt])
@@map("notifications")
}
model NotificationPreference {
id String @id @default(uuid())
userId String
type NotificationType
channel NotificationChannel
enabled Boolean @default(true)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([userId, type, channel])
@@map("notification_preferences")
}
model AbuseReport {
id String @id @default(uuid())
reporterId String?
targetUserId String?
serverId String?
status AbuseReportStatus @default(OPEN)
reason String
details String?
resolution String?
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
reporter User? @relation(fields: [reporterId], references: [id], onDelete: SetNull)
@@index([status])
@@index([serverId])
@@map("abuse_reports")
}
model SupportTicket {
id String @id @default(uuid())
userId String
serverId String?
subject String
status SupportTicketStatus @default(OPEN)
priority SupportTicketPriority @default(NORMAL)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
messages SupportTicketMessage[]
@@index([userId])
@@index([status])
@@map("support_tickets")
}
model SupportTicketMessage {
id String @id @default(uuid())
ticketId String
authorId String?
isInternal Boolean @default(false)
body String
createdAt DateTime @default(now()) @db.Timestamptz(3)
ticket SupportTicket @relation(fields: [ticketId], references: [id], onDelete: Cascade)
@@index([ticketId])
@@map("support_ticket_messages")
}
model Subscription {
id String @id @default(uuid())
userId String
planId String
status String
startsAt DateTime @db.Timestamptz(3)
endsAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
@@index([userId])
@@map("subscriptions")
}
model Invoice {
id String @id @default(uuid())
userId String
subscriptionId String?
amountCents Int
currency String @default("EUR")
status String
externalRef String?
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
payments Payment[]
@@index([userId])
@@map("invoices")
}
model Payment {
id String @id @default(uuid())
invoiceId String
amountCents Int
provider String
externalRef String?
status String
createdAt DateTime @default(now()) @db.Timestamptz(3)
invoice Invoice @relation(fields: [invoiceId], references: [id], onDelete: Cascade)
@@index([invoiceId])
@@map("payments")
}
model ResourceQuota {
id String @id @default(uuid())
userId String?
planId String?
key String
limit Int
used Int @default(0)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
@@unique([userId, planId, key])
@@map("resource_quotas")
}
model SecurityChangeLog {
id String @id @default(uuid())
userId String
action String
metadata Json?
ipAddress String?
createdAt DateTime @default(now()) @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId, createdAt])
@@map("security_change_logs")
}
model DataExportRequest {
id String @id @default(uuid())
userId String
status String @default("PENDING")
downloadUrl String?
expiresAt DateTime? @db.Timestamptz(3)
completedAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("data_export_requests")
}
model AccountDeletionRequest {
id String @id @default(uuid())
userId String
status String @default("PENDING")
scheduledFor DateTime @db.Timestamptz(3)
completedAt DateTime? @db.Timestamptz(3)
createdAt DateTime @default(now()) @db.Timestamptz(3)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@map("account_deletion_requests")
}
model WhmcsUserLink {
id String @id @default(uuid())
installationId String
externalUserId String
externalClientId String
userId String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
installation WhmcsInstallation @relation(fields: [installationId], references: [id], onDelete: Cascade)
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([installationId, externalUserId])
@@index([userId])
@@map("whmcs_user_links")
}
model WhmcsProductMapping {
id String @id @default(uuid())
installationId String
externalProductId String
planSlug String
defaultEdition ServerEdition @default(JAVA)
defaultSoftware SoftwareFamily @default(VANILLA)
defaultVersion String @default("1.21.1")
isActive Boolean @default(true)
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
installation WhmcsInstallation @relation(fields: [installationId], references: [id], onDelete: Cascade)
@@unique([installationId, externalProductId])
@@map("whmcs_product_mappings")
}
model WhmcsOptionMapping {
id String @id @default(uuid())
installationId String
externalOptionId String
externalValueId String
resourceKey String
normalizedValue String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
installation WhmcsInstallation @relation(fields: [installationId], references: [id], onDelete: Cascade)
@@unique([installationId, externalOptionId, externalValueId])
@@map("whmcs_option_mappings")
}
model WhmcsAddonMapping {
id String @id @default(uuid())
installationId String
externalAddonId String
featureKey String
deltaValue String
createdAt DateTime @default(now()) @db.Timestamptz(3)
updatedAt DateTime @updatedAt @db.Timestamptz(3)
installation WhmcsInstallation @relation(fields: [installationId], references: [id], onDelete: Cascade)
@@unique([installationId, externalAddonId])
@@map("whmcs_addon_mappings")
}

View File

@@ -38,6 +38,19 @@ export type {
SystemSetting,
AuditEvent,
JobRecord,
Schedule,
ScheduledTask,
Notification,
NotificationPreference,
DataExportRequest,
AccountDeletionRequest,
Organization,
OrganizationMember,
OrganizationMemberRole,
OrganizationInvite,
SupportTicket,
SupportTicketMessage,
AbuseReport,
} from '@prisma/client';
import { PrismaClient, Prisma } from '@prisma/client';