first commit
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
# 此目录用于存放 Incus mTLS 证书
|
||||
# 请将 client.crt 和 client.key 放置于此
|
||||
# 注意:证书文件不应提交到 Git
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
echo "🔄 Running database migrations..."
|
||||
cd /app/server
|
||||
npx prisma migrate deploy
|
||||
|
||||
echo "🚀 Starting application..."
|
||||
cd /app
|
||||
exec node server/dist/app.js
|
||||
Generated
+6829
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"name": "server",
|
||||
"version": "1.0.0",
|
||||
"description": "Incudal 后端服务",
|
||||
"type": "module",
|
||||
"main": "dist/app.js",
|
||||
"types": "dist/app.d.ts",
|
||||
"scripts": {
|
||||
"postinstall": "prisma generate",
|
||||
"dev": "cross-env NODE_ENV=development JWT_SECRET=dev-secret-12345678 PORT=8888 tsx watch src/app.ts",
|
||||
"start": "node dist/app.js",
|
||||
"build": "tsc",
|
||||
"type-check": "tsc --noEmit",
|
||||
"migrate:data": "tsx scripts/migrate-data.ts",
|
||||
"backfill:instance-network": "tsx scripts/backfill-instance-network-addresses.ts",
|
||||
"fix:migrated-billing": "tsx scripts/fix-migrated-instance-billing.ts",
|
||||
"reset:db": "tsx scripts/reset-db.ts",
|
||||
"seed:test-data": "tsx scripts/seed-test-data.ts",
|
||||
"seed:bulk-local-data": "tsx scripts/seed-bulk-local-data.ts",
|
||||
"update:admin-quota": "tsx scripts/update-admin-quota.ts",
|
||||
"test:prisma": "tsx scripts/test-prisma.ts",
|
||||
"test:app": "tsx scripts/test-app-start.ts",
|
||||
"test:agent-auth": "tsx scripts/test-agent-auth.ts",
|
||||
"test:security-config": "tsx scripts/test-security-config.ts",
|
||||
"test:api": "tsx scripts/test-api.ts",
|
||||
"analyze:queries": "tsx scripts/analyze-queries.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/cookie": "^11.0.2",
|
||||
"@fastify/cors": "^10.0.1",
|
||||
"@fastify/helmet": "^13.0.2",
|
||||
"@fastify/jwt": "^9.0.1",
|
||||
"@fastify/multipart": "^9.0.3",
|
||||
"@fastify/rate-limit": "^10.3.0",
|
||||
"@fastify/static": "^8.0.3",
|
||||
"@fastify/websocket": "^11.0.1",
|
||||
"@libsql/client": "^0.14.0",
|
||||
"@prisma/adapter-pg": "^7.1.0",
|
||||
"@prisma/client": "^7.1.0",
|
||||
"@types/ws": "^8.18.1",
|
||||
"basic-ftp": "^5.0.5",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"dotenv": "^17.2.3",
|
||||
"drizzle-orm": "^0.38.2",
|
||||
"fastify": "^5.1.0",
|
||||
"fastify-cloudflare-turnstile": "^2.0.2",
|
||||
"ip-address": "^10.1.0",
|
||||
"jsonwebtoken": "^9.0.3",
|
||||
"nanoid": "^5.1.6",
|
||||
"node-cron": "^4.2.1",
|
||||
"nodemailer": "^7.0.11",
|
||||
"otplib": "^12.0.1",
|
||||
"p-limit": "^7.2.0",
|
||||
"pg": "^8.16.3",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"qrcode": "^1.5.4",
|
||||
"ssh2-sftp-client": "^11.0.0",
|
||||
"undici": "^7.1.0",
|
||||
"webdav": "^5.8.0",
|
||||
"ws": "^8.18.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.10.1",
|
||||
"@types/node-cron": "^3.0.11",
|
||||
"@types/nodemailer": "^7.0.4",
|
||||
"@types/pg": "^8.15.6",
|
||||
"@types/qrcode": "^1.5.6",
|
||||
"@types/ssh2-sftp-client": "^9.0.4",
|
||||
"@typescript-eslint/eslint-plugin": "^8.48.1",
|
||||
"@typescript-eslint/parser": "^8.48.1",
|
||||
"cross-env": "^10.1.0",
|
||||
"drizzle-kit": "^0.30.1",
|
||||
"prisma": "^7.1.0",
|
||||
"tsx": "^4.21.0",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// This file was generated by Prisma and assumes you have installed the following:
|
||||
// npm install --save-dev prisma dotenv
|
||||
import "dotenv/config";
|
||||
import { defineConfig, env } from "prisma/config";
|
||||
|
||||
export default defineConfig({
|
||||
schema: "prisma/schema.prisma",
|
||||
migrations: {
|
||||
path: "prisma/migrations",
|
||||
},
|
||||
datasource: {
|
||||
url: env("DATABASE_URL"),
|
||||
provider: "postgresql",
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "email_verification_codes" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "email_verification_codes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "email_verification_codes_email_code_idx" ON "email_verification_codes"("email", "code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "email_verification_codes_expires_at_idx" ON "email_verification_codes"("expires_at");
|
||||
|
||||
@@ -0,0 +1,535 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "UserRole" AS ENUM ('admin', 'user');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "UserStatus" AS ENUM ('active', 'banned');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "HostStatus" AS ENUM ('online', 'offline', 'maintenance');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InstanceType" AS ENUM ('container', 'vm', 'both');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "NetworkMode" AS ENUM ('nat', 'ipv6', 'dual');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InstanceStatus" AS ENUM ('creating', 'running', 'stopped', 'error', 'deleted');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "Protocol" AS ENUM ('tcp', 'udp');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "BackupStatus" AS ENUM ('creating', 'ready', 'error', 'deleted');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "NotificationChannelType" AS ENUM ('telegram', 'discord', 'email', 'webhook');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "NotificationLogStatus" AS ENUM ('pending', 'sent', 'failed');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OAuthProvider" AS ENUM ('github', 'google');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "HostImageStatus" AS ENUM ('pending', 'syncing', 'ready', 'error');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "users" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"email" TEXT,
|
||||
"password_hash" TEXT NOT NULL,
|
||||
"role" "UserRole" NOT NULL DEFAULT 'user',
|
||||
"status" "UserStatus" NOT NULL DEFAULT 'active',
|
||||
"balance" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "users_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_quotas" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"cpu_limit" INTEGER NOT NULL DEFAULT 20,
|
||||
"cpu_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"memory_limit" INTEGER NOT NULL DEFAULT 102400,
|
||||
"memory_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"disk_limit" INTEGER NOT NULL DEFAULT 512000,
|
||||
"disk_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"instance_limit" INTEGER NOT NULL DEFAULT 10,
|
||||
"instance_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"port_limit" INTEGER NOT NULL DEFAULT 20,
|
||||
"port_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"snapshot_limit" INTEGER NOT NULL DEFAULT 50,
|
||||
"snapshot_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"backup_limit" INTEGER NOT NULL DEFAULT 30,
|
||||
"backup_used" INTEGER NOT NULL DEFAULT 0,
|
||||
|
||||
CONSTRAINT "user_quotas_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "invite_codes" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"created_by" INTEGER NOT NULL,
|
||||
"used_by" INTEGER,
|
||||
"used_at" TIMESTAMP(3),
|
||||
"expires_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "invite_codes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ssh_keys" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"public_key" TEXT NOT NULL,
|
||||
"fingerprint" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ssh_keys_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "node_groups" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"color" TEXT NOT NULL DEFAULT '#3b82f6',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "node_groups_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "hosts" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"url" TEXT NOT NULL,
|
||||
"location" TEXT,
|
||||
"country_code" TEXT NOT NULL DEFAULT 'us',
|
||||
"node_group_id" INTEGER,
|
||||
"status" "HostStatus" NOT NULL DEFAULT 'offline',
|
||||
"cert_path" TEXT,
|
||||
"key_path" TEXT,
|
||||
"nat_public_ip" TEXT,
|
||||
"nat_port_start" INTEGER,
|
||||
"nat_port_end" INTEGER,
|
||||
"cpu_total" INTEGER NOT NULL DEFAULT 0,
|
||||
"cpu_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"memory_total" INTEGER NOT NULL DEFAULT 0,
|
||||
"memory_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"disk_total" INTEGER NOT NULL DEFAULT 0,
|
||||
"disk_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"tags" JSONB DEFAULT '[]',
|
||||
"nat_ports_used_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"cpu_allowance_max" INTEGER NOT NULL DEFAULT 0,
|
||||
"memory_max" INTEGER NOT NULL DEFAULT 0,
|
||||
"instance_type" "InstanceType" NOT NULL DEFAULT 'container',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "hosts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "packages" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"cpu_max" INTEGER NOT NULL,
|
||||
"memory_max" INTEGER NOT NULL,
|
||||
"disk_max" INTEGER NOT NULL,
|
||||
"bandwidth_max" INTEGER,
|
||||
"network_mode" "NetworkMode" NOT NULL DEFAULT 'nat',
|
||||
"node_group_id" INTEGER,
|
||||
"node_selectors" JSONB DEFAULT '[]',
|
||||
"privileged" BOOLEAN NOT NULL DEFAULT false,
|
||||
"nested" BOOLEAN NOT NULL DEFAULT false,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"port_limit" INTEGER NOT NULL DEFAULT 20,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "packages_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "instances" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"incus_id" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"package_id" INTEGER,
|
||||
"image" TEXT NOT NULL,
|
||||
"status" "InstanceStatus" NOT NULL DEFAULT 'creating',
|
||||
"cpu" INTEGER NOT NULL,
|
||||
"memory" INTEGER NOT NULL,
|
||||
"disk" INTEGER NOT NULL,
|
||||
"ipv4" TEXT,
|
||||
"ipv6" TEXT,
|
||||
"network_mode" "NetworkMode" NOT NULL DEFAULT 'nat',
|
||||
"ssh_port" INTEGER,
|
||||
"root_password" TEXT,
|
||||
"snapshotted_specs" JSONB DEFAULT '{}',
|
||||
"port_limit" INTEGER,
|
||||
"snapshot_limit" INTEGER,
|
||||
"backup_limit" INTEGER,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "instances_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "port_mappings" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"protocol" "Protocol" NOT NULL DEFAULT 'tcp',
|
||||
"public_port" INTEGER NOT NULL,
|
||||
"private_port" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "port_mappings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "snapshots" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"incus_name" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"stateful" BOOLEAN NOT NULL DEFAULT false,
|
||||
"size" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "snapshots_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "backups" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"incus_name" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"size" INTEGER NOT NULL DEFAULT 0,
|
||||
"status" "BackupStatus" NOT NULL DEFAULT 'creating',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"expires_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "backups_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "backup_policies" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"interval_hours" INTEGER NOT NULL DEFAULT 24,
|
||||
"retention_count" INTEGER NOT NULL DEFAULT 3,
|
||||
"last_run_at" TIMESTAMP(3),
|
||||
"next_run_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "backup_policies_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "snapshot_policies" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"interval_hours" INTEGER NOT NULL DEFAULT 6,
|
||||
"retention_count" INTEGER NOT NULL DEFAULT 5,
|
||||
"last_run_at" TIMESTAMP(3),
|
||||
"next_run_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "snapshot_policies_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "notification_channels" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"type" "NotificationChannelType" NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"config" JSONB NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "notification_channels_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "notification_logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"channel_id" INTEGER NOT NULL,
|
||||
"event_type" TEXT NOT NULL,
|
||||
"message" TEXT NOT NULL,
|
||||
"status" "NotificationLogStatus" NOT NULL,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "notification_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "oauth_configs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"provider" "OAuthProvider" NOT NULL,
|
||||
"client_id" TEXT NOT NULL,
|
||||
"client_secret" TEXT NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "oauth_configs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_oauth_bindings" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"provider" "OAuthProvider" NOT NULL,
|
||||
"provider_user_id" TEXT NOT NULL,
|
||||
"provider_username" TEXT,
|
||||
"provider_email" TEXT,
|
||||
"provider_avatar" TEXT,
|
||||
"access_token" TEXT,
|
||||
"refresh_token" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "user_oauth_bindings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "help_articles" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"slug" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"category" TEXT NOT NULL DEFAULT 'general',
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"published" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_by" INTEGER,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "help_articles_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "images" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"os_type" TEXT NOT NULL DEFAULT 'Linux',
|
||||
"remote_alias" TEXT NOT NULL,
|
||||
"architecture" TEXT NOT NULL DEFAULT 'x86_64',
|
||||
"description" TEXT,
|
||||
"icon" TEXT,
|
||||
"active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "images_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "host_images" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"image_id" INTEGER NOT NULL,
|
||||
"fingerprint" TEXT,
|
||||
"alias" TEXT,
|
||||
"size" INTEGER NOT NULL DEFAULT 0,
|
||||
"status" "HostImageStatus" NOT NULL DEFAULT 'pending',
|
||||
"error_message" TEXT,
|
||||
"synced_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "host_images_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER,
|
||||
"module" TEXT NOT NULL,
|
||||
"action" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"result" TEXT NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "users_username_key" ON "users"("username");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_quotas_user_id_key" ON "user_quotas"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "invite_codes_code_key" ON "invite_codes"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "invite_codes_code_idx" ON "invite_codes"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ssh_keys_user_id_idx" ON "ssh_keys"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "node_groups_name_key" ON "node_groups"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "hosts_name_key" ON "hosts"("name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "hosts_node_group_id_idx" ON "hosts"("node_group_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instances_user_id_idx" ON "instances"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instances_host_id_idx" ON "instances"("host_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instances_status_idx" ON "instances"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "port_mappings_instance_id_idx" ON "port_mappings"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "snapshots_instance_id_idx" ON "snapshots"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backups_instance_id_idx" ON "backups"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backups_status_idx" ON "backups"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "backup_policies_instance_id_key" ON "backup_policies"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "snapshot_policies_instance_id_key" ON "snapshot_policies"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "notification_channels_user_id_idx" ON "notification_channels"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "oauth_configs_provider_key" ON "oauth_configs"("provider");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_oauth_bindings_user_id_idx" ON "user_oauth_bindings"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_oauth_bindings_provider_provider_user_id_idx" ON "user_oauth_bindings"("provider", "provider_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_oauth_bindings_user_id_provider_key" ON "user_oauth_bindings"("user_id", "provider");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_oauth_bindings_provider_provider_user_id_key" ON "user_oauth_bindings"("provider", "provider_user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "help_articles_slug_key" ON "help_articles"("slug");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "host_images_host_id_idx" ON "host_images"("host_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "host_images_image_id_idx" ON "host_images"("image_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "host_images_host_id_image_id_key" ON "host_images"("host_id", "image_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "logs_user_id_idx" ON "logs"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "logs_module_idx" ON "logs"("module");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "logs_created_at_idx" ON "logs"("created_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_quotas" ADD CONSTRAINT "user_quotas_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "invite_codes" ADD CONSTRAINT "invite_codes_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "invite_codes" ADD CONSTRAINT "invite_codes_used_by_fkey" FOREIGN KEY ("used_by") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ssh_keys" ADD CONSTRAINT "ssh_keys_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "hosts" ADD CONSTRAINT "hosts_node_group_id_fkey" FOREIGN KEY ("node_group_id") REFERENCES "node_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "packages" ADD CONSTRAINT "packages_node_group_id_fkey" FOREIGN KEY ("node_group_id") REFERENCES "node_groups"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instances" ADD CONSTRAINT "instances_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instances" ADD CONSTRAINT "instances_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instances" ADD CONSTRAINT "instances_package_id_fkey" FOREIGN KEY ("package_id") REFERENCES "packages"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "port_mappings" ADD CONSTRAINT "port_mappings_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "port_mappings" ADD CONSTRAINT "port_mappings_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "snapshots" ADD CONSTRAINT "snapshots_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "backups" ADD CONSTRAINT "backups_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "backup_policies" ADD CONSTRAINT "backup_policies_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "snapshot_policies" ADD CONSTRAINT "snapshot_policies_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "notification_channels" ADD CONSTRAINT "notification_channels_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "notification_logs" ADD CONSTRAINT "notification_logs_channel_id_fkey" FOREIGN KEY ("channel_id") REFERENCES "notification_channels"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_oauth_bindings" ADD CONSTRAINT "user_oauth_bindings_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "help_articles" ADD CONSTRAINT "help_articles_created_by_fkey" FOREIGN KEY ("created_by") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "host_images" ADD CONSTRAINT "host_images_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "host_images" ADD CONSTRAINT "host_images_image_id_fkey" FOREIGN KEY ("image_id") REFERENCES "images"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "logs" ADD CONSTRAINT "logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "two_factor_enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN "two_factor_secret" TEXT;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "two_factor_recovery_codes" TEXT;
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[install_token]` on the table `hosts` will be added. If there are existing duplicate values, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "enable_api" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN "install_token" TEXT,
|
||||
ADD COLUMN "ip_address" TEXT,
|
||||
ADD COLUMN "ipv6_gateway" TEXT,
|
||||
ADD COLUMN "ipv6_mode" INTEGER NOT NULL DEFAULT 1,
|
||||
ADD COLUMN "ipv6_subnet" TEXT,
|
||||
ADD COLUMN "is_installed" BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN "zfs_size" INTEGER NOT NULL DEFAULT 60;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "hosts_install_token_key" ON "hosts"("install_token");
|
||||
@@ -0,0 +1,16 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "system_configs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"key" TEXT NOT NULL,
|
||||
"value" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL DEFAULT 'string',
|
||||
"label" TEXT,
|
||||
"description" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "system_configs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "system_configs_key_key" ON "system_configs"("key");
|
||||
@@ -0,0 +1,52 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TrafficStatus" AS ENUM ('NORMAL', 'WARNING', 'LIMITED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "instances" ADD COLUMN "monthly_traffic_limit" BIGINT,
|
||||
ADD COLUMN "monthly_traffic_used" BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN "traffic_status" "TrafficStatus" NOT NULL DEFAULT 'NORMAL';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "user_quotas" ADD COLUMN "extra_traffic_quota" BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN "extra_traffic_used" BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN "monthly_traffic_limit" BIGINT,
|
||||
ADD COLUMN "monthly_traffic_used" BIGINT NOT NULL DEFAULT 0,
|
||||
ADD COLUMN "traffic_status" "TrafficStatus" NOT NULL DEFAULT 'NORMAL',
|
||||
ADD COLUMN "traffic_warning_sent_at" TIMESTAMP(3);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "traffic_snapshots" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"rx_raw" BIGINT NOT NULL,
|
||||
"tx_raw" BIGINT NOT NULL,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "traffic_snapshots_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "daily_traffic" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"date" DATE NOT NULL,
|
||||
"rx_total" BIGINT NOT NULL,
|
||||
"tx_total" BIGINT NOT NULL,
|
||||
|
||||
CONSTRAINT "daily_traffic_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "traffic_snapshots_instance_id_key" ON "traffic_snapshots"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "daily_traffic_instance_id_date_idx" ON "daily_traffic"("instance_id", "date");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "daily_traffic_instance_id_date_key" ON "daily_traffic"("instance_id", "date");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "traffic_snapshots" ADD CONSTRAINT "traffic_snapshots_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "daily_traffic" ADD CONSTRAINT "daily_traffic_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ADD COLUMN "monthly_traffic_limit" BIGINT;
|
||||
@@ -0,0 +1,30 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RestoreTaskStatus" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "instances" ADD COLUMN "restored_from" JSONB;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "restore_tasks" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"backup_id" INTEGER,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"status" "RestoreTaskStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"temp_instance_name" TEXT,
|
||||
"original_instance_name" TEXT NOT NULL,
|
||||
"original_incus_id" TEXT NOT NULL,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"started_at" TIMESTAMP(3),
|
||||
"finished_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "restore_tasks_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "restore_tasks_host_id_status_idx" ON "restore_tasks"("host_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "restore_tasks_instance_id_idx" ON "restore_tasks"("instance_id");
|
||||
@@ -0,0 +1,61 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "StorageType" AS ENUM ('S3', 'WEBDAV', 'FTP', 'SFTP');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "BackupUploadTaskStatus" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "storage_configs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" "StorageType" NOT NULL,
|
||||
"host" TEXT NOT NULL,
|
||||
"port" INTEGER,
|
||||
"username" TEXT,
|
||||
"password" TEXT,
|
||||
"base_path" TEXT,
|
||||
"extra" JSONB,
|
||||
"is_default" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "storage_configs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "backup_upload_tasks" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"backup_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"storage_config_id" INTEGER NOT NULL,
|
||||
"status" "BackupUploadTaskStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"remote_file_name" TEXT,
|
||||
"file_size" BIGINT,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"started_at" TIMESTAMP(3),
|
||||
"finished_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "backup_upload_tasks_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "storage_configs_user_id_idx" ON "storage_configs"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backup_upload_tasks_host_id_status_idx" ON "backup_upload_tasks"("host_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backup_upload_tasks_user_id_idx" ON "backup_upload_tasks"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backup_upload_tasks_instance_id_idx" ON "backup_upload_tasks"("instance_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "storage_configs" ADD CONSTRAINT "storage_configs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "backup_upload_tasks" ADD CONSTRAINT "backup_upload_tasks_storage_config_id_fkey" FOREIGN KEY ("storage_config_id") REFERENCES "storage_configs"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backup_upload_tasks_storage_config_id_status_idx" ON "backup_upload_tasks"("storage_config_id", "status");
|
||||
@@ -0,0 +1,38 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TransferStatus" AS ENUM ('pending', 'accepted', 'rejected', 'cancelled');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "instance_transfers" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"from_user_id" INTEGER NOT NULL,
|
||||
"to_user_id" INTEGER NOT NULL,
|
||||
"status" "TransferStatus" NOT NULL DEFAULT 'pending',
|
||||
"snapshot" JSONB NOT NULL,
|
||||
"remark" TEXT,
|
||||
"reject_reason" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"accepted_at" TIMESTAMP(3),
|
||||
"rejected_at" TIMESTAMP(3),
|
||||
"cancelled_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "instance_transfers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instance_transfers_from_user_id_status_idx" ON "instance_transfers"("from_user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instance_transfers_to_user_id_status_idx" ON "instance_transfers"("to_user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instance_transfers_instance_id_status_idx" ON "instance_transfers"("instance_id", "status");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instance_transfers" ADD CONSTRAINT "instance_transfers_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instance_transfers" ADD CONSTRAINT "instance_transfers_from_user_id_fkey" FOREIGN KEY ("from_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instance_transfers" ADD CONSTRAINT "instance_transfers_to_user_id_fkey" FOREIGN KEY ("to_user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "avatar_style" TEXT NOT NULL DEFAULT 'bigSmile';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "port_mappings" ADD COLUMN "remark" TEXT;
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
-- 创建好友关系状态枚举
|
||||
CREATE TYPE "FriendshipStatus" AS ENUM ('pending', 'accepted');
|
||||
|
||||
-- 创建好友关系表
|
||||
CREATE TABLE "friendships" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"friend_id" INTEGER NOT NULL,
|
||||
"status" "FriendshipStatus" NOT NULL DEFAULT 'pending',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"accepted_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "friendships_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 添加 user_id 列到 hosts 表(先允许 NULL)
|
||||
ALTER TABLE "hosts" ADD COLUMN "user_id" INTEGER;
|
||||
|
||||
-- 添加 user_id 列到 images 表(先允许 NULL)
|
||||
ALTER TABLE "images" ADD COLUMN "user_id" INTEGER;
|
||||
|
||||
-- 添加 user_id 列到 packages 表(先允许 NULL)
|
||||
ALTER TABLE "packages" ADD COLUMN "user_id" INTEGER;
|
||||
|
||||
-- 添加 user_id 列到 node_groups 表(先允许 NULL)
|
||||
ALTER TABLE "node_groups" ADD COLUMN "user_id" INTEGER;
|
||||
|
||||
-- 将现有数据的 user_id 设置为第一个管理员用户
|
||||
-- 如果没有管理员用户,则设置为第一个用户
|
||||
UPDATE "hosts" SET "user_id" = (
|
||||
SELECT "id" FROM "users" WHERE "role" = 'admin' ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
UPDATE "images" SET "user_id" = (
|
||||
SELECT "id" FROM "users" WHERE "role" = 'admin' ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
UPDATE "packages" SET "user_id" = (
|
||||
SELECT "id" FROM "users" WHERE "role" = 'admin' ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
UPDATE "node_groups" SET "user_id" = (
|
||||
SELECT "id" FROM "users" WHERE "role" = 'admin' ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
-- 如果仍有 NULL 值(没有管理员用户的情况),使用第一个用户
|
||||
UPDATE "hosts" SET "user_id" = (
|
||||
SELECT "id" FROM "users" ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
UPDATE "images" SET "user_id" = (
|
||||
SELECT "id" FROM "users" ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
UPDATE "packages" SET "user_id" = (
|
||||
SELECT "id" FROM "users" ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
UPDATE "node_groups" SET "user_id" = (
|
||||
SELECT "id" FROM "users" ORDER BY "id" LIMIT 1
|
||||
) WHERE "user_id" IS NULL;
|
||||
|
||||
-- 删除 hosts 表原有的唯一约束
|
||||
DROP INDEX IF EXISTS "hosts_name_key";
|
||||
|
||||
-- 删除 node_groups 表原有的唯一约束
|
||||
DROP INDEX IF EXISTS "node_groups_name_key";
|
||||
|
||||
-- 设置 NOT NULL 约束
|
||||
ALTER TABLE "hosts" ALTER COLUMN "user_id" SET NOT NULL;
|
||||
ALTER TABLE "images" ALTER COLUMN "user_id" SET NOT NULL;
|
||||
ALTER TABLE "packages" ALTER COLUMN "user_id" SET NOT NULL;
|
||||
ALTER TABLE "node_groups" ALTER COLUMN "user_id" SET NOT NULL;
|
||||
|
||||
-- 添加外键约束
|
||||
ALTER TABLE "friendships" ADD CONSTRAINT "friendships_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "friendships" ADD CONSTRAINT "friendships_friend_id_fkey"
|
||||
FOREIGN KEY ("friend_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "hosts" ADD CONSTRAINT "hosts_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "images" ADD CONSTRAINT "images_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "packages" ADD CONSTRAINT "packages_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
ALTER TABLE "node_groups" ADD CONSTRAINT "node_groups_user_id_fkey"
|
||||
FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- 添加唯一约束
|
||||
CREATE UNIQUE INDEX "friendships_user_id_friend_id_key" ON "friendships"("user_id", "friend_id");
|
||||
CREATE UNIQUE INDEX "hosts_user_id_name_key" ON "hosts"("user_id", "name");
|
||||
CREATE UNIQUE INDEX "images_user_id_name_key" ON "images"("user_id", "name");
|
||||
CREATE UNIQUE INDEX "packages_user_id_name_key" ON "packages"("user_id", "name");
|
||||
CREATE UNIQUE INDEX "node_groups_user_id_name_key" ON "node_groups"("user_id", "name");
|
||||
|
||||
-- 添加索引
|
||||
CREATE INDEX "friendships_friend_id_status_idx" ON "friendships"("friend_id", "status");
|
||||
CREATE INDEX "friendships_user_id_status_idx" ON "friendships"("user_id", "status");
|
||||
CREATE INDEX "hosts_user_id_idx" ON "hosts"("user_id");
|
||||
CREATE INDEX "images_user_id_idx" ON "images"("user_id");
|
||||
CREATE INDEX "packages_user_id_idx" ON "packages"("user_id");
|
||||
CREATE INDEX "node_groups_user_id_idx" ON "node_groups"("user_id");
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "FriendshipStatus" ADD VALUE 'rejected';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "friendships" ADD COLUMN "rejected_at" TIMESTAMP(3),
|
||||
ADD COLUMN "remark" TEXT;
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `backup_limit` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `backup_used` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `cpu_limit` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `cpu_used` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `disk_limit` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `disk_used` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `memory_limit` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `memory_used` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `port_limit` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `port_used` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `snapshot_limit` on the `user_quotas` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `snapshot_used` on the `user_quotas` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "user_quotas" DROP COLUMN "backup_limit",
|
||||
DROP COLUMN "backup_used",
|
||||
DROP COLUMN "cpu_limit",
|
||||
DROP COLUMN "cpu_used",
|
||||
DROP COLUMN "disk_limit",
|
||||
DROP COLUMN "disk_used",
|
||||
DROP COLUMN "memory_limit",
|
||||
DROP COLUMN "memory_used",
|
||||
DROP COLUMN "port_limit",
|
||||
DROP COLUMN "port_used",
|
||||
DROP COLUMN "snapshot_limit",
|
||||
DROP COLUMN "snapshot_used",
|
||||
ALTER COLUMN "instance_limit" SET DEFAULT 50;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "package_shares" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"package_id" INTEGER NOT NULL,
|
||||
"owner_id" INTEGER NOT NULL,
|
||||
"shared_to_id" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "package_shares_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "package_shares_shared_to_id_idx" ON "package_shares"("shared_to_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "package_shares_owner_id_idx" ON "package_shares"("owner_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "package_shares_package_id_idx" ON "package_shares"("package_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "package_shares_package_id_shared_to_id_key" ON "package_shares"("package_id", "shared_to_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "package_shares" ADD CONSTRAINT "package_shares_package_id_fkey" FOREIGN KEY ("package_id") REFERENCES "packages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "package_shares" ADD CONSTRAINT "package_shares_owner_id_fkey" FOREIGN KEY ("owner_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "package_shares" ADD CONSTRAINT "package_shares_shared_to_id_fkey" FOREIGN KEY ("shared_to_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `zfs_size` on the `hosts` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" DROP COLUMN "zfs_size",
|
||||
ADD COLUMN "install_token_expire" TIMESTAMP(3),
|
||||
ADD COLUMN "storage_driver" TEXT NOT NULL DEFAULT 'zfs',
|
||||
ADD COLUMN "storage_path" TEXT,
|
||||
ADD COLUMN "storage_size" INTEGER NOT NULL DEFAULT 60,
|
||||
ADD COLUMN "storage_type" TEXT NOT NULL DEFAULT 'loop',
|
||||
ADD COLUMN "sysctl_config" TEXT;
|
||||
@@ -0,0 +1,67 @@
|
||||
-- 配额系统重构迁移
|
||||
-- 用户配额从 CPU/内存/磁盘/端口/快照/备份 改为 宿主机数量/实例数量/好友数量
|
||||
-- 套餐新增 snapshotLimit 和 backupLimit 字段
|
||||
|
||||
-- =====================================================
|
||||
-- 1. 修改 user_quotas 表
|
||||
-- =====================================================
|
||||
|
||||
-- 添加新字段
|
||||
ALTER TABLE "user_quotas" ADD COLUMN IF NOT EXISTS "host_limit" INTEGER NOT NULL DEFAULT 5;
|
||||
ALTER TABLE "user_quotas" ADD COLUMN IF NOT EXISTS "host_used" INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE "user_quotas" ADD COLUMN IF NOT EXISTS "friend_limit" INTEGER NOT NULL DEFAULT 20;
|
||||
ALTER TABLE "user_quotas" ADD COLUMN IF NOT EXISTS "friend_used" INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
-- 更新 instance_limit 默认值为 50(可选,不影响现有数据)
|
||||
-- ALTER TABLE "user_quotas" ALTER COLUMN "instance_limit" SET DEFAULT 50;
|
||||
|
||||
-- 统计并更新现有用户的 host_used
|
||||
UPDATE "user_quotas" q SET "host_used" = (
|
||||
SELECT COUNT(*) FROM "hosts" h WHERE h."user_id" = q."user_id"
|
||||
);
|
||||
|
||||
-- 统计并更新现有用户的 instance_used
|
||||
UPDATE "user_quotas" q SET "instance_used" = (
|
||||
SELECT COUNT(*) FROM "instances" i WHERE i."user_id" = q."user_id" AND i."status" != 'deleted'
|
||||
);
|
||||
|
||||
-- 统计并更新现有用户的 friend_used(已接受的好友数)
|
||||
UPDATE "user_quotas" q SET "friend_used" = (
|
||||
SELECT COUNT(*) FROM "friendships" f
|
||||
WHERE (f."user_id" = q."user_id" OR f."friend_id" = q."user_id")
|
||||
AND f."status" = 'accepted'
|
||||
);
|
||||
|
||||
-- 删除旧字段(可选,建议先保留一段时间后再删除)
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "cpu_limit";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "cpu_used";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "memory_limit";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "memory_used";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "disk_limit";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "disk_used";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "port_limit";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "port_used";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "snapshot_limit";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "snapshot_used";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "backup_limit";
|
||||
-- ALTER TABLE "user_quotas" DROP COLUMN IF EXISTS "backup_used";
|
||||
|
||||
-- =====================================================
|
||||
-- 2. 修改 packages 表
|
||||
-- =====================================================
|
||||
|
||||
-- 添加新字段
|
||||
ALTER TABLE "packages" ADD COLUMN IF NOT EXISTS "snapshot_limit" INTEGER NOT NULL DEFAULT 5;
|
||||
ALTER TABLE "packages" ADD COLUMN IF NOT EXISTS "backup_limit" INTEGER NOT NULL DEFAULT 3;
|
||||
|
||||
-- =====================================================
|
||||
-- 3. 更新实例配额(从套餐继承)
|
||||
-- =====================================================
|
||||
|
||||
-- 对于有套餐的实例,如果 snapshot_limit 或 backup_limit 为 NULL,则从套餐继承
|
||||
UPDATE "instances" i SET
|
||||
"snapshot_limit" = COALESCE(i."snapshot_limit", p."snapshot_limit"),
|
||||
"backup_limit" = COALESCE(i."backup_limit", p."backup_limit")
|
||||
FROM "packages" p
|
||||
WHERE i."package_id" = p."id"
|
||||
AND (i."snapshot_limit" IS NULL OR i."backup_limit" IS NULL);
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 清理系统配置表中的旧配额字段
|
||||
-- 删除不再使用的 CPU/内存/磁盘/端口/快照/备份 配额配置
|
||||
|
||||
DELETE FROM "system_configs" WHERE "key" IN (
|
||||
'default_quota_cpu',
|
||||
'default_quota_memory',
|
||||
'default_quota_disk',
|
||||
'default_quota_port',
|
||||
'default_quota_snapshot',
|
||||
'default_quota_backup'
|
||||
);
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
迁移说明:
|
||||
1. 创建 package_hosts 表(套餐与宿主机的多对多关联)
|
||||
2. 迁移数据:将基于 node_group_id 的绑定转换为精确的宿主机绑定
|
||||
3. 删除 node_group_id 字段和 node_groups 表
|
||||
4. 对于没有绑定节点组的套餐,绑定套餐所有者的所有宿主机
|
||||
*/
|
||||
|
||||
-- 步骤1: 创建 package_hosts 表
|
||||
CREATE TABLE "package_hosts" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"package_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "package_hosts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 步骤2: 创建索引和外键
|
||||
CREATE INDEX "package_hosts_package_id_idx" ON "package_hosts"("package_id");
|
||||
CREATE INDEX "package_hosts_host_id_idx" ON "package_hosts"("host_id");
|
||||
CREATE UNIQUE INDEX "package_hosts_package_id_host_id_key" ON "package_hosts"("package_id", "host_id");
|
||||
|
||||
ALTER TABLE "package_hosts" ADD CONSTRAINT "package_hosts_package_id_fkey" FOREIGN KEY ("package_id") REFERENCES "packages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "package_hosts" ADD CONSTRAINT "package_hosts_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- 步骤3: 迁移数据
|
||||
-- 3.1: 对于有 node_group_id 的套餐,绑定该节点组下的所有宿主机
|
||||
INSERT INTO "package_hosts" ("package_id", "host_id", "created_at")
|
||||
SELECT DISTINCT p.id, h.id, CURRENT_TIMESTAMP
|
||||
FROM "packages" p
|
||||
INNER JOIN "hosts" h ON h.node_group_id = p.node_group_id
|
||||
WHERE p.node_group_id IS NOT NULL;
|
||||
|
||||
-- 3.2: 对于没有 node_group_id 的套餐,绑定套餐所有者的所有宿主机
|
||||
INSERT INTO "package_hosts" ("package_id", "host_id", "created_at")
|
||||
SELECT DISTINCT p.id, h.id, CURRENT_TIMESTAMP
|
||||
FROM "packages" p
|
||||
INNER JOIN "hosts" h ON h.user_id = p.user_id
|
||||
WHERE p.node_group_id IS NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM "package_hosts" ph
|
||||
WHERE ph.package_id = p.id AND ph.host_id = h.id
|
||||
);
|
||||
|
||||
-- 步骤4: 删除外键约束
|
||||
ALTER TABLE "hosts" DROP CONSTRAINT IF EXISTS "hosts_node_group_id_fkey";
|
||||
ALTER TABLE "node_groups" DROP CONSTRAINT IF EXISTS "node_groups_user_id_fkey";
|
||||
ALTER TABLE "packages" DROP CONSTRAINT IF EXISTS "packages_node_group_id_fkey";
|
||||
|
||||
-- 步骤5: 删除索引
|
||||
DROP INDEX IF EXISTS "hosts_node_group_id_idx";
|
||||
|
||||
-- 步骤6: 删除字段
|
||||
ALTER TABLE "hosts" DROP COLUMN IF EXISTS "node_group_id";
|
||||
ALTER TABLE "packages" DROP COLUMN IF EXISTS "node_group_id";
|
||||
|
||||
-- 步骤7: 删除 node_groups 表
|
||||
DROP TABLE IF EXISTS "node_groups";
|
||||
@@ -0,0 +1,76 @@
|
||||
-- 简化镜像管理:移除 Image 表,直接在 HostImage 中存储镜像信息
|
||||
|
||||
-- 1. 备份现有数据(如果需要恢复)
|
||||
-- 创建临时表保存现有的 host_images 数据
|
||||
CREATE TEMP TABLE temp_host_images AS
|
||||
SELECT
|
||||
hi.id,
|
||||
hi.host_id,
|
||||
i.remote_alias as image_id,
|
||||
i.name,
|
||||
i.os_type,
|
||||
i.remote_alias,
|
||||
COALESCE(i.icon, SPLIT_PART(i.remote_alias, '/', 1)) as icon,
|
||||
hi.fingerprint,
|
||||
hi.alias,
|
||||
hi.size,
|
||||
hi.status,
|
||||
hi.error_message,
|
||||
hi.synced_at,
|
||||
hi.created_at,
|
||||
hi.updated_at
|
||||
FROM host_images hi
|
||||
JOIN images i ON hi.image_id = i.id;
|
||||
|
||||
-- 2. 删除外键约束和旧表
|
||||
DROP TABLE IF EXISTS host_images;
|
||||
DROP TABLE IF EXISTS images;
|
||||
|
||||
-- 3. 创建新的 host_images 表
|
||||
CREATE TABLE host_images (
|
||||
id SERIAL PRIMARY KEY,
|
||||
host_id INTEGER NOT NULL,
|
||||
image_id VARCHAR(100) NOT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
os_type VARCHAR(50) NOT NULL DEFAULT 'Linux',
|
||||
remote_alias VARCHAR(200) NOT NULL,
|
||||
icon VARCHAR(50),
|
||||
fingerprint VARCHAR(100),
|
||||
alias VARCHAR(100),
|
||||
size INTEGER NOT NULL DEFAULT 0,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'pending',
|
||||
error_message TEXT,
|
||||
synced_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
CONSTRAINT fk_host_images_host FOREIGN KEY (host_id) REFERENCES hosts(id) ON DELETE CASCADE,
|
||||
CONSTRAINT uq_host_images_host_image UNIQUE (host_id, image_id)
|
||||
);
|
||||
|
||||
-- 4. 创建索引
|
||||
CREATE INDEX idx_host_images_host_id ON host_images(host_id);
|
||||
|
||||
-- 5. 恢复数据(将 remote_alias 转换为 image_id 格式)
|
||||
INSERT INTO host_images (
|
||||
host_id, image_id, name, os_type, remote_alias, icon,
|
||||
fingerprint, alias, size, status, error_message, synced_at, created_at, updated_at
|
||||
)
|
||||
SELECT
|
||||
host_id,
|
||||
LOWER(REPLACE(REPLACE(remote_alias, '/', '-'), '.', '-')) as image_id,
|
||||
name,
|
||||
os_type,
|
||||
remote_alias,
|
||||
icon,
|
||||
fingerprint,
|
||||
alias,
|
||||
size,
|
||||
status::VARCHAR,
|
||||
error_message,
|
||||
synced_at,
|
||||
created_at,
|
||||
updated_at
|
||||
FROM temp_host_images;
|
||||
|
||||
-- 6. 清理临时表
|
||||
DROP TABLE temp_host_images;
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The `status` column on the `host_images` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "host_images" DROP CONSTRAINT "fk_host_images_host";
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "host_images" ALTER COLUMN "image_id" SET DATA TYPE TEXT,
|
||||
ALTER COLUMN "name" SET DATA TYPE TEXT,
|
||||
ALTER COLUMN "os_type" SET DATA TYPE TEXT,
|
||||
ALTER COLUMN "remote_alias" SET DATA TYPE TEXT,
|
||||
ALTER COLUMN "icon" SET DATA TYPE TEXT,
|
||||
ALTER COLUMN "fingerprint" SET DATA TYPE TEXT,
|
||||
ALTER COLUMN "alias" SET DATA TYPE TEXT,
|
||||
DROP COLUMN "status",
|
||||
ADD COLUMN "status" "HostImageStatus" NOT NULL DEFAULT 'pending',
|
||||
ALTER COLUMN "synced_at" SET DATA TYPE TIMESTAMP(3),
|
||||
ALTER COLUMN "created_at" SET DATA TYPE TIMESTAMP(3),
|
||||
ALTER COLUMN "updated_at" DROP DEFAULT,
|
||||
ALTER COLUMN "updated_at" SET DATA TYPE TIMESTAMP(3);
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "instances" ADD COLUMN "boot_autostart" BOOLEAN,
|
||||
ADD COLUMN "boot_autostart_delay" INTEGER,
|
||||
ADD COLUMN "boot_autostart_priority" INTEGER,
|
||||
ADD COLUMN "boot_host_shutdown_timeout" INTEGER,
|
||||
ADD COLUMN "limits_cpu_priority" INTEGER,
|
||||
ADD COLUMN "limits_egress" TEXT,
|
||||
ADD COLUMN "limits_ingress" TEXT,
|
||||
ADD COLUMN "limits_processes" INTEGER,
|
||||
ADD COLUMN "limits_read" TEXT,
|
||||
ADD COLUMN "limits_read_iops" INTEGER,
|
||||
ADD COLUMN "limits_write" TEXT,
|
||||
ADD COLUMN "limits_write_iops" INTEGER;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ADD COLUMN "boot_autostart" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN "boot_autostart_delay" INTEGER NOT NULL DEFAULT 15,
|
||||
ADD COLUMN "boot_autostart_priority" INTEGER NOT NULL DEFAULT 20,
|
||||
ADD COLUMN "boot_host_shutdown_timeout" INTEGER NOT NULL DEFAULT 30,
|
||||
ADD COLUMN "limits_cpu_priority" INTEGER NOT NULL DEFAULT 10,
|
||||
ADD COLUMN "limits_egress" TEXT NOT NULL DEFAULT '300Mbit',
|
||||
ADD COLUMN "limits_ingress" TEXT NOT NULL DEFAULT '300Mbit',
|
||||
ADD COLUMN "limits_processes" INTEGER NOT NULL DEFAULT 500,
|
||||
ADD COLUMN "limits_read" TEXT NOT NULL DEFAULT '100MB',
|
||||
ADD COLUMN "limits_read_iops" INTEGER NOT NULL DEFAULT 500,
|
||||
ADD COLUMN "limits_write" TEXT NOT NULL DEFAULT '100MB',
|
||||
ADD COLUMN "limits_write_iops" INTEGER NOT NULL DEFAULT 500;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "host_images" ADD CONSTRAINT "host_images_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- RenameIndex
|
||||
ALTER INDEX "idx_host_images_host_id" RENAME TO "host_images_host_id_idx";
|
||||
|
||||
-- RenameIndex
|
||||
ALTER INDEX "uq_host_images_host_image" RENAME TO "host_images_host_id_image_id_key";
|
||||
@@ -0,0 +1 @@
|
||||
-- This is an empty migration.
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the `host_images` table. If the table is not empty, all the data it contains will be lost.
|
||||
|
||||
*/
|
||||
-- DropForeignKey
|
||||
ALTER TABLE "host_images" DROP CONSTRAINT "host_images_host_id_fkey";
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE "host_images";
|
||||
|
||||
-- DropEnum
|
||||
DROP TYPE "HostImageStatus";
|
||||
@@ -0,0 +1 @@
|
||||
-- This is an empty migration.
|
||||
@@ -0,0 +1,32 @@
|
||||
-- CreateEnum (with IF NOT EXISTS to handle partial migrations)
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE "IpType" AS ENUM ('inet4', 'inet6');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
|
||||
-- CreateTable (with IF NOT EXISTS)
|
||||
CREATE TABLE IF NOT EXISTS "ip_addresses" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"address" TEXT NOT NULL,
|
||||
"type" "IpType" NOT NULL DEFAULT 'inet6',
|
||||
"is_primary" BOOLEAN NOT NULL DEFAULT false,
|
||||
"device" TEXT NOT NULL DEFAULT 'eth0',
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ip_addresses_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex (with IF NOT EXISTS)
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "ip_addresses_address_key" ON "ip_addresses"("address");
|
||||
|
||||
-- CreateIndex (with IF NOT EXISTS)
|
||||
CREATE INDEX IF NOT EXISTS "ip_addresses_instance_id_idx" ON "ip_addresses"("instance_id");
|
||||
|
||||
-- AddForeignKey (check if not exists)
|
||||
DO $$ BEGIN
|
||||
ALTER TABLE "ip_addresses" ADD CONSTRAINT "ip_addresses_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
@@ -0,0 +1,56 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "refresh_tokens" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"role" "UserRole" NOT NULL,
|
||||
"ip" TEXT,
|
||||
"user_agent" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
"last_active_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "refresh_tokens_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "token_invalidations" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"session_id" TEXT,
|
||||
"invalidated_at" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "token_invalidations_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "refresh_tokens_token_key" ON "refresh_tokens"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "refresh_tokens_user_id_idx" ON "refresh_tokens"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "refresh_tokens_token_idx" ON "refresh_tokens"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "refresh_tokens_expires_at_idx" ON "refresh_tokens"("expires_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "token_invalidations_user_id_idx" ON "token_invalidations"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "token_invalidations_session_id_idx" ON "token_invalidations"("session_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "token_invalidations_invalidated_at_idx" ON "token_invalidations"("invalidated_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "token_invalidations_user_id_session_id_key" ON "token_invalidations"("user_id", "session_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "refresh_tokens" ADD CONSTRAINT "refresh_tokens_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "token_invalidations" ADD CONSTRAINT "token_invalidations_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum
|
||||
ALTER TYPE "FriendshipStatus" ADD VALUE 'removed';
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `cpu_total` on the `hosts` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `disk_total` on the `hosts` table. All the data in the column will be lost.
|
||||
- You are about to drop the column `memory_total` on the `hosts` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" DROP COLUMN "cpu_total",
|
||||
DROP COLUMN "disk_total",
|
||||
DROP COLUMN "memory_total";
|
||||
@@ -0,0 +1,7 @@
|
||||
-- Add progress and retryCount fields to RestoreTask
|
||||
ALTER TABLE "restore_tasks" ADD COLUMN "progress" TEXT;
|
||||
ALTER TABLE "restore_tasks" ADD COLUMN "retry_count" INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
-- Add progress and retryCount fields to BackupUploadTask
|
||||
ALTER TABLE "backup_upload_tasks" ADD COLUMN "progress" TEXT;
|
||||
ALTER TABLE "backup_upload_tasks" ADD COLUMN "retry_count" INTEGER NOT NULL DEFAULT 0;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add cert_download_count field to hosts table
|
||||
ALTER TABLE "hosts" ADD COLUMN "cert_download_count" INTEGER NOT NULL DEFAULT 0;
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "OperationType" AS ENUM ('change_password', 'disable_2fa', 'change_email', 'delete_account', 'delete_instance', 'reinstall_instance', 'transfer_instance', 'delete_snapshot', 'delete_backup');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "VerificationChannel" AS ENUM ('email', 'telegram', 'discord', 'webhook');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "cert_download_expire" TIMESTAMP(3);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "operation_verifications" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"operation_type" "OperationType" NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"channel" "VerificationChannel" NOT NULL,
|
||||
"resource_id" INTEGER,
|
||||
"resource_type" TEXT,
|
||||
"verified" BOOLEAN NOT NULL DEFAULT false,
|
||||
"verified_at" TIMESTAMP(3),
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "operation_verifications_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "distributed_locks" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"lock_key" TEXT NOT NULL,
|
||||
"owner_id" TEXT NOT NULL,
|
||||
"acquired_at" TIMESTAMP(3) NOT NULL,
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "distributed_locks_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "operation_verifications_user_id_operation_type_code_idx" ON "operation_verifications"("user_id", "operation_type", "code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "operation_verifications_expires_at_idx" ON "operation_verifications"("expires_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "distributed_locks_lock_key_key" ON "distributed_locks"("lock_key");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "distributed_locks_expires_at_idx" ON "distributed_locks"("expires_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "operation_verifications" ADD CONSTRAINT "operation_verifications_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
-- 修改 NetworkMode 枚举:添加新值并重命名
|
||||
-- 旧值: nat, ipv6, dual
|
||||
-- 新值: nat, nat_ipv6, ipv4, ipv4_ipv6
|
||||
-- 注意: PostgreSQL 不允许在同一事务中添加新枚举值后立即使用,所以使用替换枚举类型的方法
|
||||
|
||||
-- 1. 创建新的枚举类型
|
||||
CREATE TYPE "NetworkMode_new" AS ENUM ('nat', 'nat_ipv6', 'ipv4', 'ipv4_ipv6');
|
||||
|
||||
-- 2. 修改 packages 表:先删除默认值,转换类型,再恢复默认值
|
||||
ALTER TABLE "packages" ALTER COLUMN "network_mode" DROP DEFAULT;
|
||||
ALTER TABLE "packages" ALTER COLUMN "network_mode" TYPE TEXT;
|
||||
UPDATE "packages" SET "network_mode" = 'nat_ipv6' WHERE "network_mode" = 'ipv6';
|
||||
UPDATE "packages" SET "network_mode" = 'ipv4_ipv6' WHERE "network_mode" = 'dual';
|
||||
ALTER TABLE "packages" ALTER COLUMN "network_mode" TYPE "NetworkMode_new" USING "network_mode"::"NetworkMode_new";
|
||||
ALTER TABLE "packages" ALTER COLUMN "network_mode" SET DEFAULT 'nat'::"NetworkMode_new";
|
||||
|
||||
-- 3. 修改 instances 表:先删除默认值,转换类型,再恢复默认值
|
||||
ALTER TABLE "instances" ALTER COLUMN "network_mode" DROP DEFAULT;
|
||||
ALTER TABLE "instances" ALTER COLUMN "network_mode" TYPE TEXT;
|
||||
UPDATE "instances" SET "network_mode" = 'nat_ipv6' WHERE "network_mode" = 'ipv6';
|
||||
UPDATE "instances" SET "network_mode" = 'ipv4_ipv6' WHERE "network_mode" = 'dual';
|
||||
ALTER TABLE "instances" ALTER COLUMN "network_mode" TYPE "NetworkMode_new" USING "network_mode"::"NetworkMode_new";
|
||||
ALTER TABLE "instances" ALTER COLUMN "network_mode" SET DEFAULT 'nat'::"NetworkMode_new";
|
||||
|
||||
-- 4. 删除旧枚举类型,重命名新类型
|
||||
DROP TYPE "NetworkMode";
|
||||
ALTER TYPE "NetworkMode_new" RENAME TO "NetworkMode";
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "system_images" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"remote_alias" TEXT NOT NULL,
|
||||
"os_type" TEXT NOT NULL DEFAULT 'Linux',
|
||||
"architecture" TEXT NOT NULL DEFAULT 'x86_64',
|
||||
"icon" TEXT NOT NULL,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"hidden" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "system_images_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "system_images_remote_alias_key" ON "system_images"("remote_alias");
|
||||
|
||||
-- 插入默认镜像数据(remote_alias 包含完整的源前缀)
|
||||
INSERT INTO "system_images" ("name", "remote_alias", "os_type", "architecture", "icon", "sort_order", "updated_at") VALUES
|
||||
-- AlmaLinux
|
||||
('AlmaLinux 8', 'images:almalinux/8/cloud', 'Linux', 'x86_64', 'almalinux', 10, NOW()),
|
||||
('AlmaLinux 9', 'images:almalinux/9/cloud', 'Linux', 'x86_64', 'almalinux', 11, NOW()),
|
||||
('AlmaLinux 10', 'images:almalinux/10/cloud', 'Linux', 'x86_64', 'almalinux', 12, NOW()),
|
||||
-- Alpine
|
||||
('Alpine 3.19', 'images:alpine/3.19/cloud', 'Linux', 'x86_64', 'alpine', 20, NOW()),
|
||||
('Alpine 3.20', 'images:alpine/3.20/cloud', 'Linux', 'x86_64', 'alpine', 21, NOW()),
|
||||
('Alpine 3.21', 'images:alpine/3.21/cloud', 'Linux', 'x86_64', 'alpine', 22, NOW()),
|
||||
('Alpine Edge', 'images:alpine/edge/cloud', 'Linux', 'x86_64', 'alpine', 29, NOW()),
|
||||
-- CentOS
|
||||
('CentOS 9 Stream', 'images:centos/9-Stream/cloud', 'Linux', 'x86_64', 'centos', 30, NOW()),
|
||||
('CentOS 10 Stream', 'images:centos/10-Stream/cloud', 'Linux', 'x86_64', 'centos', 31, NOW()),
|
||||
-- Debian
|
||||
('Debian 11 (Bullseye)', 'images:debian/11/cloud', 'Linux', 'x86_64', 'debian', 40, NOW()),
|
||||
('Debian 12 (Bookworm)', 'images:debian/12/cloud', 'Linux', 'x86_64', 'debian', 41, NOW()),
|
||||
('Debian 13 (Trixie)', 'images:debian/13/cloud', 'Linux', 'x86_64', 'debian', 42, NOW()),
|
||||
-- Fedora
|
||||
('Fedora 41', 'images:fedora/41/cloud', 'Linux', 'x86_64', 'fedora', 50, NOW()),
|
||||
('Fedora 42', 'images:fedora/42/cloud', 'Linux', 'x86_64', 'fedora', 51, NOW()),
|
||||
-- Kali
|
||||
('Kali Linux', 'images:kali/cloud', 'Linux', 'x86_64', 'kali', 60, NOW()),
|
||||
-- openSUSE
|
||||
('openSUSE 15.6', 'images:opensuse/15.6/cloud', 'Linux', 'x86_64', 'opensuse', 70, NOW()),
|
||||
('openSUSE Tumbleweed', 'images:opensuse/tumbleweed/cloud', 'Linux', 'x86_64', 'opensuse', 79, NOW()),
|
||||
-- Oracle
|
||||
('Oracle Linux 8', 'images:oracle/8/cloud', 'Linux', 'x86_64', 'oracle', 80, NOW()),
|
||||
('Oracle Linux 9', 'images:oracle/9/cloud', 'Linux', 'x86_64', 'oracle', 81, NOW()),
|
||||
-- Rocky Linux
|
||||
('Rocky Linux 8', 'images:rockylinux/8/cloud', 'Linux', 'x86_64', 'rockylinux', 90, NOW()),
|
||||
('Rocky Linux 9', 'images:rockylinux/9/cloud', 'Linux', 'x86_64', 'rockylinux', 91, NOW()),
|
||||
('Rocky Linux 10', 'images:rockylinux/10/cloud', 'Linux', 'x86_64', 'rockylinux', 92, NOW()),
|
||||
-- Ubuntu
|
||||
('Ubuntu 22.04 (Jammy)', 'images:ubuntu/jammy/cloud', 'Linux', 'x86_64', 'ubuntu', 100, NOW()),
|
||||
('Ubuntu 24.04 (Noble)', 'images:ubuntu/noble/cloud', 'Linux', 'x86_64', 'ubuntu', 101, NOW()),
|
||||
('Ubuntu 25.04 (Plucky)', 'images:ubuntu/25.04/cloud', 'Linux', 'x86_64', 'ubuntu', 102, NOW()),
|
||||
('Ubuntu 25.10 (Questing)', 'images:ubuntu/25.10/cloud', 'Linux', 'x86_64', 'ubuntu', 103, NOW());
|
||||
@@ -0,0 +1,31 @@
|
||||
-- 自动快照/备份策略重构迁移
|
||||
-- 1. 将 interval_hours 改为 interval_minutes(支持更细粒度的频率设置)
|
||||
-- 2. 移除 retention_count(额度直接继承自套餐包)
|
||||
|
||||
-- =====================================================
|
||||
-- 1. 修改 snapshot_policies 表
|
||||
-- =====================================================
|
||||
|
||||
-- 添加 interval_minutes 字段,默认值为原 interval_hours * 60
|
||||
ALTER TABLE "snapshot_policies" ADD COLUMN "interval_minutes" INTEGER NOT NULL DEFAULT 360;
|
||||
|
||||
-- 迁移现有数据:将 interval_hours 转换为分钟
|
||||
UPDATE "snapshot_policies" SET "interval_minutes" = "interval_hours" * 60;
|
||||
|
||||
-- 删除旧字段
|
||||
ALTER TABLE "snapshot_policies" DROP COLUMN "interval_hours";
|
||||
ALTER TABLE "snapshot_policies" DROP COLUMN "retention_count";
|
||||
|
||||
-- =====================================================
|
||||
-- 2. 修改 backup_policies 表
|
||||
-- =====================================================
|
||||
|
||||
-- 添加 interval_minutes 字段,默认值为原 interval_hours * 60
|
||||
ALTER TABLE "backup_policies" ADD COLUMN "interval_minutes" INTEGER NOT NULL DEFAULT 1440;
|
||||
|
||||
-- 迁移现有数据:将 interval_hours 转换为分钟
|
||||
UPDATE "backup_policies" SET "interval_minutes" = "interval_hours" * 60;
|
||||
|
||||
-- 删除旧字段
|
||||
ALTER TABLE "backup_policies" DROP COLUMN "interval_hours";
|
||||
ALTER TABLE "backup_policies" DROP COLUMN "retention_count";
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[host_id,protocol,public_port]` on the table `port_mappings` will be added. If there are existing duplicate values, this will fail.
|
||||
|
||||
*/
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "port_mappings_host_id_protocol_public_port_key" ON "port_mappings"("host_id", "protocol", "public_port");
|
||||
@@ -0,0 +1,3 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "package_shares" ADD COLUMN "max_instances" INTEGER,
|
||||
ADD COLUMN "quota_multiplier" DECIMAL(3,1);
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ADD COLUMN "instance_type" "InstanceType" NOT NULL DEFAULT 'container';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "system_images" ADD COLUMN "instance_type" TEXT NOT NULL DEFAULT 'both';
|
||||
@@ -0,0 +1,27 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "ipv6_parent_interface" TEXT;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "ip_addresses" ADD COLUMN "is_custom" BOOLEAN NOT NULL DEFAULT false,
|
||||
ALTER COLUMN "device" SET DEFAULT 'eth1';
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ipv6_subnets" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"cidr" TEXT NOT NULL,
|
||||
"primary_ip" TEXT NOT NULL,
|
||||
"device" TEXT NOT NULL DEFAULT 'eth1',
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ipv6_subnets_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "ipv6_subnets_cidr_key" ON "ipv6_subnets"("cidr");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ipv6_subnets_instance_id_idx" ON "ipv6_subnets"("instance_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ipv6_subnets" ADD CONSTRAINT "ipv6_subnets_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The values [ipv4,ipv4_ipv6] on the enum `NetworkMode` will be removed. If these variants are still used in the database, this will fail.
|
||||
|
||||
*/
|
||||
-- CreateEnum
|
||||
CREATE TYPE "StoragePurpose" AS ENUM ('instance_data', 'instance_storage');
|
||||
|
||||
-- AlterEnum
|
||||
BEGIN;
|
||||
CREATE TYPE "NetworkMode_new" AS ENUM ('nat', 'nat_ipv6');
|
||||
ALTER TABLE "public"."instances" ALTER COLUMN "network_mode" DROP DEFAULT;
|
||||
ALTER TABLE "public"."packages" ALTER COLUMN "network_mode" DROP DEFAULT;
|
||||
ALTER TABLE "packages" ALTER COLUMN "network_mode" TYPE "NetworkMode_new" USING ("network_mode"::text::"NetworkMode_new");
|
||||
ALTER TABLE "instances" ALTER COLUMN "network_mode" TYPE "NetworkMode_new" USING ("network_mode"::text::"NetworkMode_new");
|
||||
ALTER TYPE "NetworkMode" RENAME TO "NetworkMode_old";
|
||||
ALTER TYPE "NetworkMode_new" RENAME TO "NetworkMode";
|
||||
DROP TYPE "public"."NetworkMode_old";
|
||||
ALTER TABLE "instances" ALTER COLUMN "network_mode" SET DEFAULT 'nat';
|
||||
ALTER TABLE "packages" ALTER COLUMN "network_mode" SET DEFAULT 'nat';
|
||||
COMMIT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "storage_pools" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"driver" TEXT NOT NULL,
|
||||
"purpose" "StoragePurpose" NOT NULL DEFAULT 'instance_data',
|
||||
"description" TEXT,
|
||||
"config" JSONB DEFAULT '{}',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "storage_pools_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "storage_pools_host_id_idx" ON "storage_pools"("host_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "storage_pools_host_id_name_key" ON "storage_pools"("host_id", "name");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "storage_pools" ADD CONSTRAINT "storage_pools_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,39 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ProxySiteStatus" AS ENUM ('pending', 'active', 'error');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "caddy_enabled" BOOLEAN NOT NULL DEFAULT false,
|
||||
ADD COLUMN "caddy_password" TEXT,
|
||||
ADD COLUMN "caddy_port" INTEGER NOT NULL DEFAULT 8444,
|
||||
ADD COLUMN "caddy_username" TEXT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "proxy_sites" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"domain" TEXT NOT NULL,
|
||||
"target_port" INTEGER NOT NULL,
|
||||
"status" "ProxySiteStatus" NOT NULL DEFAULT 'pending',
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "proxy_sites_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "proxy_sites_instance_id_idx" ON "proxy_sites"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "proxy_sites_host_id_idx" ON "proxy_sites"("host_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "proxy_sites_host_id_domain_key" ON "proxy_sites"("host_id", "domain");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "proxy_sites" ADD CONSTRAINT "proxy_sites_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "proxy_sites" ADD CONSTRAINT "proxy_sites_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,12 @@
|
||||
-- AlterTable: 为套餐添加站点数量限制
|
||||
ALTER TABLE "packages" ADD COLUMN IF NOT EXISTS "site_limit" INTEGER NOT NULL DEFAULT 10;
|
||||
|
||||
-- AlterTable: 为实例添加站点数量限制(可覆盖套餐设置)
|
||||
ALTER TABLE "instances" ADD COLUMN IF NOT EXISTS "site_limit" INTEGER;
|
||||
|
||||
-- 对于已有实例,从套餐继承站点限制
|
||||
UPDATE "instances" i SET
|
||||
"site_limit" = COALESCE(i."site_limit", p."site_limit")
|
||||
FROM "packages" p
|
||||
WHERE i."package_id" = p."id"
|
||||
AND i."site_limit" IS NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "proxy_sites" ADD COLUMN "https_enabled" BOOLEAN NOT NULL DEFAULT true;
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "user_quotas" ADD COLUMN "package_limit" INTEGER NOT NULL DEFAULT 0,
|
||||
ADD COLUMN "package_used" INTEGER NOT NULL DEFAULT 0,
|
||||
ALTER COLUMN "host_limit" SET DEFAULT 0,
|
||||
ALTER COLUMN "friend_limit" SET DEFAULT 0;
|
||||
@@ -0,0 +1,4 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ADD COLUMN "global_max_instances" INTEGER,
|
||||
ADD COLUMN "global_quota_multiplier" DECIMAL(3,1),
|
||||
ADD COLUMN "global_shared" BOOLEAN NOT NULL DEFAULT false;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ADD COLUMN "allow_instance_deletion" BOOLEAN NOT NULL DEFAULT true;
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ALTER COLUMN "allow_instance_deletion" SET DEFAULT false;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
-- AlterTable: Add pinned field to help_articles
|
||||
ALTER TABLE "help_articles" ADD COLUMN "pinned" BOOLEAN NOT NULL DEFAULT false;
|
||||
|
||||
-- AlterTable: Remove instance quota fields from user_quotas
|
||||
ALTER TABLE "user_quotas" DROP COLUMN "instance_limit";
|
||||
ALTER TABLE "user_quotas" DROP COLUMN "instance_used";
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "users" ADD COLUMN "ban_reason" TEXT;
|
||||
@@ -0,0 +1,22 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "inbox_messages" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"event_type" TEXT NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"is_read" BOOLEAN NOT NULL DEFAULT false,
|
||||
"data" JSONB,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "inbox_messages_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "inbox_messages_user_id_is_read_idx" ON "inbox_messages"("user_id", "is_read");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "inbox_messages_user_id_created_at_idx" ON "inbox_messages"("user_id", "created_at" DESC);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "inbox_messages" ADD CONSTRAINT "inbox_messages_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "packages" ADD COLUMN "release_channel_id" INTEGER;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "packages" ADD CONSTRAINT "packages_release_channel_id_fkey" FOREIGN KEY ("release_channel_id") REFERENCES "notification_channels"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,40 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InstanceTaskType" AS ENUM ('start', 'stop', 'restart', 'rebuild', 'clone');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "InstanceTaskStatus" AS ENUM ('PENDING', 'PROCESSING', 'COMPLETED', 'FAILED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "instance_tasks" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"task_type" "InstanceTaskType" NOT NULL,
|
||||
"status" "InstanceTaskStatus" NOT NULL DEFAULT 'PENDING',
|
||||
"progress" TEXT,
|
||||
"retry_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"image_alias" TEXT,
|
||||
"target_name" TEXT,
|
||||
"target_host_id" INTEGER,
|
||||
"snapshot_name" TEXT,
|
||||
"new_instance_id" INTEGER,
|
||||
"error" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"started_at" TIMESTAMP(3),
|
||||
"finished_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "instance_tasks_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instance_tasks_host_id_status_idx" ON "instance_tasks"("host_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instance_tasks_instance_id_idx" ON "instance_tasks"("instance_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instance_tasks_user_id_idx" ON "instance_tasks"("user_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "instance_tasks" ADD CONSTRAINT "instance_tasks_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "proxy_sites" ADD COLUMN "remark" TEXT;
|
||||
@@ -0,0 +1,24 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "login_records" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"ip" TEXT NOT NULL,
|
||||
"country" TEXT,
|
||||
"region" TEXT,
|
||||
"city" TEXT,
|
||||
"isp" TEXT,
|
||||
"timezone" TEXT,
|
||||
"user_agent" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "login_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "login_records_user_id_created_at_idx" ON "login_records"("user_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "login_records_user_id_ip_idx" ON "login_records"("user_id", "ip");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "login_records" ADD CONSTRAINT "login_records_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,11 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "instances" ADD COLUMN "last_synced_at" TIMESTAMP(3);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "backup_policies_enabled_next_run_at_idx" ON "backup_policies"("enabled", "next_run_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "instances_host_id_status_idx" ON "instances"("host_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "snapshot_policies_enabled_next_run_at_idx" ON "snapshot_policies"("enabled", "next_run_at");
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "instance_tasks" ADD COLUMN "ssh_key_id" INTEGER;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterEnum: Add 'processing' to TransferStatus
|
||||
ALTER TYPE "TransferStatus" ADD VALUE 'processing';
|
||||
|
||||
-- AlterTable: Add updated_at column to instance_transfers
|
||||
ALTER TABLE "instance_transfers" ADD COLUMN "updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP;
|
||||
@@ -0,0 +1,99 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "AnnouncementType" AS ENUM ('system_broadcast', 'host_broadcast', 'admin_message', 'host_message');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TicketStatus" AS ENUM ('open', 'in_progress', 'resolved', 'closed');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "TicketPriority" AS ENUM ('low', 'normal', 'high', 'urgent');
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "instance_transfers" ALTER COLUMN "updated_at" DROP DEFAULT;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "announcements" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"type" "AnnouncementType" NOT NULL,
|
||||
"sender_id" INTEGER NOT NULL,
|
||||
"title" TEXT NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"recipient_count" INTEGER NOT NULL,
|
||||
"host_id" INTEGER,
|
||||
"target_user_id" INTEGER,
|
||||
"instance_id" INTEGER,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "announcements_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "tickets" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"instance_id" INTEGER,
|
||||
"subject" TEXT NOT NULL,
|
||||
"category" TEXT NOT NULL DEFAULT 'general',
|
||||
"priority" "TicketPriority" NOT NULL DEFAULT 'normal',
|
||||
"status" "TicketStatus" NOT NULL DEFAULT 'open',
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"resolved_at" TIMESTAMP(3),
|
||||
"closed_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "tickets_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "ticket_messages" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"ticket_id" INTEGER NOT NULL,
|
||||
"sender_id" INTEGER NOT NULL,
|
||||
"content" TEXT NOT NULL,
|
||||
"is_from_owner" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "ticket_messages_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "announcements_sender_id_created_at_idx" ON "announcements"("sender_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "announcements_host_id_created_at_idx" ON "announcements"("host_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "announcements_type_created_at_idx" ON "announcements"("type", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "tickets_user_id_status_idx" ON "tickets"("user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "tickets_host_id_status_idx" ON "tickets"("host_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "tickets_status_created_at_idx" ON "tickets"("status", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "ticket_messages_ticket_id_created_at_idx" ON "ticket_messages"("ticket_id", "created_at");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "announcements" ADD CONSTRAINT "announcements_sender_id_fkey" FOREIGN KEY ("sender_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "announcements" ADD CONSTRAINT "announcements_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "tickets" ADD CONSTRAINT "tickets_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ticket_messages" ADD CONSTRAINT "ticket_messages_ticket_id_fkey" FOREIGN KEY ("ticket_id") REFERENCES "tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "ticket_messages" ADD CONSTRAINT "ticket_messages_sender_id_fkey" FOREIGN KEY ("sender_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1 @@
|
||||
-- This is an empty migration.
|
||||
@@ -0,0 +1,57 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "RedeemCodeType" AS ENUM ('c', 'r', 'd', 't');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "checkin_records" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"redeem_code" TEXT NOT NULL,
|
||||
"code_type" "RedeemCodeType" NOT NULL,
|
||||
"code_value" INTEGER NOT NULL,
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
"used_at" TIMESTAMP(3),
|
||||
"used_by" INTEGER,
|
||||
"used_for" INTEGER,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "checkin_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "checkin_stats" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"last_checkin_date" TIMESTAMP(3),
|
||||
"last_redeem_date" TIMESTAMP(3),
|
||||
"consecutive_others_use" INTEGER NOT NULL DEFAULT 0,
|
||||
"self_only_mode" BOOLEAN NOT NULL DEFAULT false,
|
||||
|
||||
CONSTRAINT "checkin_stats_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "checkin_records_redeem_code_key" ON "checkin_records"("redeem_code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "checkin_records_user_id_idx" ON "checkin_records"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "checkin_records_used_by_idx" ON "checkin_records"("used_by");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "checkin_records_expires_at_idx" ON "checkin_records"("expires_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "checkin_stats_user_id_key" ON "checkin_stats"("user_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "checkin_records" ADD CONSTRAINT "checkin_records_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "checkin_records" ADD CONSTRAINT "checkin_records_used_by_fkey" FOREIGN KEY ("used_by") REFERENCES "users"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "checkin_records" ADD CONSTRAINT "checkin_records_used_for_fkey" FOREIGN KEY ("used_for") REFERENCES "instances"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "checkin_stats" ADD CONSTRAINT "checkin_stats_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,64 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "redeem_codes" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"created_by_id" INTEGER NOT NULL,
|
||||
"code_type" "RedeemCodeType" NOT NULL,
|
||||
"code_value" INTEGER NOT NULL,
|
||||
"max_uses" INTEGER NOT NULL DEFAULT 1,
|
||||
"used_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"expires_at" TIMESTAMP(3),
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "redeem_codes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "redeem_code_usages" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"redeem_code_id" INTEGER NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"used_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "redeem_code_usages_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "redeem_codes_code_key" ON "redeem_codes"("code");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_codes_host_id_idx" ON "redeem_codes"("host_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_codes_created_by_id_idx" ON "redeem_codes"("created_by_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_codes_enabled_idx" ON "redeem_codes"("enabled");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_code_usages_redeem_code_id_idx" ON "redeem_code_usages"("redeem_code_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_code_usages_user_id_idx" ON "redeem_code_usages"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_code_usages_instance_id_idx" ON "redeem_code_usages"("instance_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "redeem_codes" ADD CONSTRAINT "redeem_codes_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "redeem_codes" ADD CONSTRAINT "redeem_codes_created_by_id_fkey" FOREIGN KEY ("created_by_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "redeem_code_usages" ADD CONSTRAINT "redeem_code_usages_redeem_code_id_fkey" FOREIGN KEY ("redeem_code_id") REFERENCES "redeem_codes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "redeem_code_usages" ADD CONSTRAINT "redeem_code_usages_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "redeem_code_usages" ADD CONSTRAINT "redeem_code_usages_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "redeem_codes" ADD COLUMN "batch_id" TEXT;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "redeem_codes_batch_id_idx" ON "redeem_codes"("batch_id");
|
||||
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- A unique constraint covering the columns `[user_id,batch_id]` on the table `redeem_code_usages` will be added. If there are existing duplicate values, this will fail.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "redeem_code_usages" ADD COLUMN "batch_id" TEXT;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "redeem_code_usages_user_id_batch_id_key" ON "redeem_code_usages"("user_id", "batch_id");
|
||||
@@ -0,0 +1,18 @@
|
||||
-- AlterEnum: Add 'suspended' to InstanceStatus
|
||||
ALTER TYPE "InstanceStatus" ADD VALUE 'suspended';
|
||||
|
||||
-- AlterTable: Add billing fields to packages
|
||||
ALTER TABLE "packages" ADD COLUMN "price_monthly" DECIMAL(10,2);
|
||||
ALTER TABLE "packages" ADD COLUMN "setup_fee" DECIMAL(10,2) NOT NULL DEFAULT 0;
|
||||
|
||||
-- AlterTable: Add billing and suspend fields to instances
|
||||
ALTER TABLE "instances" ADD COLUMN "expires_at" TIMESTAMP(3);
|
||||
ALTER TABLE "instances" ADD COLUMN "suspended_at" TIMESTAMP(3);
|
||||
ALTER TABLE "instances" ADD COLUMN "suspended_by" INTEGER;
|
||||
ALTER TABLE "instances" ADD COLUMN "suspend_reason" TEXT;
|
||||
ALTER TABLE "instances" ADD COLUMN "billing_price" DECIMAL(10,2);
|
||||
ALTER TABLE "instances" ADD COLUMN "auto_renew" BOOLEAN NOT NULL DEFAULT false;
|
||||
ALTER TABLE "instances" ADD COLUMN "expiry_notified_at" TIMESTAMP(3);
|
||||
|
||||
-- CreateIndex: For billing scheduler to find expiring instances
|
||||
CREATE INDEX "instances_expires_at_idx" ON "instances"("expires_at");
|
||||
@@ -0,0 +1,26 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "custom_init_commands" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"command" TEXT NOT NULL,
|
||||
"distros" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "custom_init_commands_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AlterTable: Add custom_init_command_ids to instance_tasks
|
||||
ALTER TABLE "instance_tasks" ADD COLUMN "custom_init_command_ids" TEXT;
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "custom_init_commands_user_id_idx" ON "custom_init_commands"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "custom_init_commands_user_id_enabled_idx" ON "custom_init_commands"("user_id", "enabled");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "custom_init_commands" ADD CONSTRAINT "custom_init_commands_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable: 添加 IPv6 重新分配冷却时间字段(每实例每天限一次)
|
||||
ALTER TABLE "instances" ADD COLUMN "last_ipv6_reassign_at" TIMESTAMP(3);
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "transfer_enabled" BOOLEAN NOT NULL DEFAULT true;
|
||||
@@ -0,0 +1,209 @@
|
||||
-- CreateEnum: Balance Log Types
|
||||
CREATE TYPE "BalanceLogType" AS ENUM ('recharge', 'consume', 'refund', 'admin_adjust', 'gift');
|
||||
|
||||
-- CreateEnum: Payment Provider Types
|
||||
CREATE TYPE "PaymentProviderType" AS ENUM ('yipay', 'stripe', 'alipay_direct', 'wechat_direct', 'manual');
|
||||
|
||||
-- CreateEnum: Payment Provider Status
|
||||
CREATE TYPE "PaymentProviderStatus" AS ENUM ('active', 'disabled', 'testing');
|
||||
|
||||
-- CreateEnum: Recharge Status
|
||||
CREATE TYPE "RechargeStatus" AS ENUM ('pending', 'paid', 'completed', 'failed', 'cancelled', 'refunded');
|
||||
|
||||
-- CreateEnum: Billing Record Types
|
||||
CREATE TYPE "BillingRecordType" AS ENUM ('newPurchase', 'renew', 'upgrade', 'downgrade', 'refund');
|
||||
|
||||
-- AlterTable: Add balance field to users
|
||||
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "balance" DECIMAL(10,2) NOT NULL DEFAULT 0;
|
||||
|
||||
-- AlterTable: Add billing fields to instances
|
||||
ALTER TABLE "instances" ADD COLUMN IF NOT EXISTS "package_plan_id" INTEGER;
|
||||
ALTER TABLE "instances" ADD COLUMN IF NOT EXISTS "billing_cycle" INTEGER;
|
||||
ALTER TABLE "instances" ADD COLUMN IF NOT EXISTS "auto_renew_attempts" INTEGER NOT NULL DEFAULT 0;
|
||||
ALTER TABLE "instances" ADD COLUMN IF NOT EXISTS "last_auto_renew_attempt_at" TIMESTAMP(3);
|
||||
ALTER TABLE "instances" ADD COLUMN IF NOT EXISTS "version" INTEGER NOT NULL DEFAULT 0;
|
||||
|
||||
-- CreateTable: PaymentProvider
|
||||
CREATE TABLE IF NOT EXISTS "payment_providers" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" "PaymentProviderType" NOT NULL,
|
||||
"status" "PaymentProviderStatus" NOT NULL DEFAULT 'disabled',
|
||||
"config" JSONB NOT NULL DEFAULT '{}',
|
||||
"methods" JSONB NOT NULL DEFAULT '[]',
|
||||
"fee_rate" DECIMAL(5,4) NOT NULL DEFAULT 0,
|
||||
"fee_fixed" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
"min_amount" DECIMAL(10,2) NOT NULL DEFAULT 1,
|
||||
"max_amount" DECIMAL(10,2),
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "payment_providers_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: BalanceLog
|
||||
CREATE TABLE IF NOT EXISTS "balance_logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"type" "BalanceLogType" NOT NULL,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"balance_before" DECIMAL(10,2) NOT NULL,
|
||||
"balance_after" DECIMAL(10,2) NOT NULL,
|
||||
"order_id" TEXT,
|
||||
"instance_id" INTEGER,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "balance_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: RechargeRecord
|
||||
CREATE TABLE IF NOT EXISTS "recharge_records" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"provider_id" INTEGER NOT NULL,
|
||||
"order_no" TEXT NOT NULL,
|
||||
"trade_no" TEXT,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"actual_amount" DECIMAL(10,2),
|
||||
"fee" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
"payment_method" TEXT,
|
||||
"status" "RechargeStatus" NOT NULL DEFAULT 'pending',
|
||||
"callback_data" JSONB,
|
||||
"callback_at" TIMESTAMP(3),
|
||||
"fail_reason" TEXT,
|
||||
"ip" TEXT,
|
||||
"user_agent" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"expired_at" TIMESTAMP(3),
|
||||
"completed_at" TIMESTAMP(3),
|
||||
|
||||
CONSTRAINT "recharge_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: PackagePlan
|
||||
CREATE TABLE IF NOT EXISTS "package_plans" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"package_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"cpu" INTEGER NOT NULL,
|
||||
"memory" INTEGER NOT NULL,
|
||||
"disk" INTEGER NOT NULL,
|
||||
"port_limit" INTEGER NOT NULL,
|
||||
"snapshot_limit" INTEGER NOT NULL,
|
||||
"backup_limit" INTEGER NOT NULL,
|
||||
"site_limit" INTEGER NOT NULL,
|
||||
"traffic_limit" BIGINT NOT NULL,
|
||||
"traffic_limit_speed" TEXT NOT NULL DEFAULT '1Mbit',
|
||||
"price" DECIMAL(10,2) NOT NULL,
|
||||
"billing_cycle" INTEGER NOT NULL DEFAULT 1,
|
||||
"setup_fee" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"stock_limit" INTEGER,
|
||||
"stock_used" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "package_plans_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: InstanceBillingRecord
|
||||
CREATE TABLE IF NOT EXISTS "instance_billing_records" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"type" "BillingRecordType" NOT NULL,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"months" INTEGER NOT NULL DEFAULT 1,
|
||||
"period_start" TIMESTAMP(3) NOT NULL,
|
||||
"period_end" TIMESTAMP(3) NOT NULL,
|
||||
"balance_log_id" INTEGER,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "instance_billing_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "recharge_records_order_no_key" ON "recharge_records"("order_no");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "balance_logs_user_id_created_at_idx" ON "balance_logs"("user_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "balance_logs_type_idx" ON "balance_logs"("type");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "recharge_records_user_id_status_idx" ON "recharge_records"("user_id", "status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "recharge_records_status_created_at_idx" ON "recharge_records"("status", "created_at");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "recharge_records_order_no_idx" ON "recharge_records"("order_no");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "recharge_records_trade_no_idx" ON "recharge_records"("trade_no");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "package_plans_package_id_name_key" ON "package_plans"("package_id", "name");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "package_plans_package_id_is_active_idx" ON "package_plans"("package_id", "is_active");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "instance_billing_records_instance_id_created_at_idx" ON "instance_billing_records"("instance_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX IF NOT EXISTS "instance_billing_records_user_id_created_at_idx" ON "instance_billing_records"("user_id", "created_at" DESC);
|
||||
|
||||
-- AddForeignKey (only if not exists - handle gracefully)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'balance_logs_user_id_fkey') THEN
|
||||
ALTER TABLE "balance_logs" ADD CONSTRAINT "balance_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'recharge_records_user_id_fkey') THEN
|
||||
ALTER TABLE "recharge_records" ADD CONSTRAINT "recharge_records_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'recharge_records_provider_id_fkey') THEN
|
||||
ALTER TABLE "recharge_records" ADD CONSTRAINT "recharge_records_provider_id_fkey" FOREIGN KEY ("provider_id") REFERENCES "payment_providers"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'package_plans_package_id_fkey') THEN
|
||||
ALTER TABLE "package_plans" ADD CONSTRAINT "package_plans_package_id_fkey" FOREIGN KEY ("package_id") REFERENCES "packages"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'instances_package_plan_id_fkey') THEN
|
||||
ALTER TABLE "instances" ADD CONSTRAINT "instances_package_plan_id_fkey" FOREIGN KEY ("package_plan_id") REFERENCES "package_plans"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'instance_billing_records_instance_id_fkey') THEN
|
||||
ALTER TABLE "instance_billing_records" ADD CONSTRAINT "instance_billing_records_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- Remove old billing fields from packages (if exist)
|
||||
ALTER TABLE "packages" DROP COLUMN IF EXISTS "price_monthly";
|
||||
ALTER TABLE "packages" DROP COLUMN IF EXISTS "setup_fee";
|
||||
@@ -0,0 +1,9 @@
|
||||
-- Replace stock_limit/stock_used with sla_guarantee in package_plans table
|
||||
-- This migration converts the inventory system to an SLA guarantee display
|
||||
|
||||
-- Step 1: Add the new sla_guarantee column
|
||||
ALTER TABLE "package_plans" ADD COLUMN IF NOT EXISTS "sla_guarantee" DECIMAL(5,2);
|
||||
|
||||
-- Step 2: Drop the old stock columns (no data migration needed as we're changing the concept entirely)
|
||||
ALTER TABLE "package_plans" DROP COLUMN IF EXISTS "stock_limit";
|
||||
ALTER TABLE "package_plans" DROP COLUMN IF EXISTS "stock_used";
|
||||
@@ -0,0 +1,152 @@
|
||||
-- CreateEnum: AFF Log Types (if not exists)
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'AffLogType') THEN
|
||||
CREATE TYPE "AffLogType" AS ENUM ('new_purchase', 'renew', 'convert');
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- CreateEnum: AFF Withdrawal Status (if not exists)
|
||||
DO $$ BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'AffWithdrawalStatus') THEN
|
||||
CREATE TYPE "AffWithdrawalStatus" AS ENUM ('pending', 'approved', 'rejected');
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AlterTable: Add AFF fields to users
|
||||
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "aff_balance" DECIMAL(10,2) NOT NULL DEFAULT 0;
|
||||
ALTER TABLE "users" ADD COLUMN IF NOT EXISTS "aff_activated_at" TIMESTAMP(3);
|
||||
|
||||
-- CreateTable: AffCode (优惠码)
|
||||
CREATE TABLE IF NOT EXISTS "aff_codes" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"package_plan_id" INTEGER NOT NULL,
|
||||
"discount_rate" DECIMAL(5,4) NOT NULL DEFAULT 0.05,
|
||||
"commission_rate" DECIMAL(5,4) NOT NULL DEFAULT 0.05,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"used_count" INTEGER NOT NULL DEFAULT 0,
|
||||
"total_earnings" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "aff_codes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: AffBinding (实例与优惠码绑定)
|
||||
CREATE TABLE IF NOT EXISTS "aff_bindings" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"aff_code_id" INTEGER NOT NULL,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "aff_bindings_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: AffLog (AFF余额变动日志)
|
||||
CREATE TABLE IF NOT EXISTS "aff_logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"aff_code_id" INTEGER,
|
||||
"instance_id" INTEGER,
|
||||
"type" "AffLogType" NOT NULL,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"original_amount" DECIMAL(10,2),
|
||||
"balance_before" DECIMAL(10,2) NOT NULL,
|
||||
"balance_after" DECIMAL(10,2) NOT NULL,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "aff_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable: AffWithdrawal (AFF余额转化申请)
|
||||
CREATE TABLE IF NOT EXISTS "aff_withdrawals" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"amount" DECIMAL(10,2) NOT NULL,
|
||||
"status" "AffWithdrawalStatus" NOT NULL DEFAULT 'pending',
|
||||
"reject_reason" TEXT,
|
||||
"reviewed_by" INTEGER,
|
||||
"reviewed_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "aff_withdrawals_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex: Unique code
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "aff_codes_code_key" ON "aff_codes"("code");
|
||||
|
||||
-- CreateIndex: User can only have one code per plan
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "aff_codes_user_id_package_plan_id_key" ON "aff_codes"("user_id", "package_plan_id");
|
||||
|
||||
-- CreateIndex: AffCode indexes
|
||||
CREATE INDEX IF NOT EXISTS "aff_codes_user_id_idx" ON "aff_codes"("user_id");
|
||||
CREATE INDEX IF NOT EXISTS "aff_codes_package_plan_id_idx" ON "aff_codes"("package_plan_id");
|
||||
|
||||
-- CreateIndex: AffBinding - one instance can only bind one code
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "aff_bindings_instance_id_key" ON "aff_bindings"("instance_id");
|
||||
CREATE INDEX IF NOT EXISTS "aff_bindings_aff_code_id_idx" ON "aff_bindings"("aff_code_id");
|
||||
|
||||
-- CreateIndex: AffLog indexes
|
||||
CREATE INDEX IF NOT EXISTS "aff_logs_user_id_created_at_idx" ON "aff_logs"("user_id", "created_at" DESC);
|
||||
CREATE INDEX IF NOT EXISTS "aff_logs_aff_code_id_idx" ON "aff_logs"("aff_code_id");
|
||||
|
||||
-- CreateIndex: AffWithdrawal indexes
|
||||
CREATE INDEX IF NOT EXISTS "aff_withdrawals_user_id_status_idx" ON "aff_withdrawals"("user_id", "status");
|
||||
CREATE INDEX IF NOT EXISTS "aff_withdrawals_status_created_at_idx" ON "aff_withdrawals"("status", "created_at");
|
||||
|
||||
-- AddForeignKey: AffCode -> User
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_codes_user_id_fkey') THEN
|
||||
ALTER TABLE "aff_codes" ADD CONSTRAINT "aff_codes_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AddForeignKey: AffCode -> PackagePlan
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_codes_package_plan_id_fkey') THEN
|
||||
ALTER TABLE "aff_codes" ADD CONSTRAINT "aff_codes_package_plan_id_fkey" FOREIGN KEY ("package_plan_id") REFERENCES "package_plans"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AddForeignKey: AffBinding -> Instance
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_bindings_instance_id_fkey') THEN
|
||||
ALTER TABLE "aff_bindings" ADD CONSTRAINT "aff_bindings_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AddForeignKey: AffBinding -> AffCode
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_bindings_aff_code_id_fkey') THEN
|
||||
ALTER TABLE "aff_bindings" ADD CONSTRAINT "aff_bindings_aff_code_id_fkey" FOREIGN KEY ("aff_code_id") REFERENCES "aff_codes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AddForeignKey: AffLog -> User
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_logs_user_id_fkey') THEN
|
||||
ALTER TABLE "aff_logs" ADD CONSTRAINT "aff_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AddForeignKey: AffLog -> AffCode (SetNull on delete)
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_logs_aff_code_id_fkey') THEN
|
||||
ALTER TABLE "aff_logs" ADD CONSTRAINT "aff_logs_aff_code_id_fkey" FOREIGN KEY ("aff_code_id") REFERENCES "aff_codes"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- AddForeignKey: AffWithdrawal -> User
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'aff_withdrawals_user_id_fkey') THEN
|
||||
ALTER TABLE "aff_withdrawals" ADD CONSTRAINT "aff_withdrawals_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,18 @@
|
||||
-- CreateTable: 支付回调防重放记录
|
||||
CREATE TABLE IF NOT EXISTS "payment_callbacks" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"provider_id" INTEGER NOT NULL,
|
||||
"order_no" TEXT NOT NULL,
|
||||
"trade_no" TEXT,
|
||||
"callback_ip" TEXT,
|
||||
"processed" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "payment_callbacks_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex: 联合唯一索引防重放
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "payment_callbacks_provider_id_order_no_trade_no_key" ON "payment_callbacks"("provider_id", "order_no", "trade_no");
|
||||
|
||||
-- CreateIndex: 用于定期清理
|
||||
CREATE INDEX IF NOT EXISTS "payment_callbacks_created_at_idx" ON "payment_callbacks"("created_at");
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterTable: Make host_id optional in tickets table
|
||||
-- This allows users to create tickets without selecting an instance
|
||||
-- (e.g., for billing/recharge issues before having any instances)
|
||||
|
||||
ALTER TABLE "tickets" ALTER COLUMN "host_id" DROP NOT NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "instances" ADD COLUMN "last_plan_change_at" TIMESTAMP(3);
|
||||
@@ -0,0 +1,18 @@
|
||||
-- AlterTable: Make package_plan_id nullable to support global AFF codes
|
||||
-- Global AFF codes have package_plan_id = NULL and can be used for all paid plans
|
||||
|
||||
-- Step 1: Drop the existing foreign key constraint
|
||||
ALTER TABLE "aff_codes" DROP CONSTRAINT IF EXISTS "aff_codes_package_plan_id_fkey";
|
||||
|
||||
-- Step 2: Alter column to be nullable
|
||||
ALTER TABLE "aff_codes" ALTER COLUMN "package_plan_id" DROP NOT NULL;
|
||||
|
||||
-- Step 3: Re-add foreign key with ON DELETE CASCADE (allows NULL values)
|
||||
ALTER TABLE "aff_codes" ADD CONSTRAINT "aff_codes_package_plan_id_fkey"
|
||||
FOREIGN KEY ("package_plan_id") REFERENCES "package_plans"("id")
|
||||
ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- Note: The unique constraint on (user_id, package_plan_id) remains
|
||||
-- In PostgreSQL, NULL values are not considered equal for unique constraints,
|
||||
-- so multiple rows with (same_user_id, NULL) would be allowed by the database.
|
||||
-- The application layer uses transactions to prevent duplicate global codes per user.
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "traffic_reset_day" INTEGER NOT NULL DEFAULT 1;
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "PointsLogType" AS ENUM ('convert', 'lottery_win', 'lottery_spend', 'admin_adjust');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "LotteryPrizeType" AS ENUM ('nothing', 'points', 'balance', 'instance');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "LotteryRecordStatus" AS ENUM ('pending', 'delivered', 'claimed', 'expired');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_points" (
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"points" INTEGER NOT NULL DEFAULT 0,
|
||||
"total_earned" INTEGER NOT NULL DEFAULT 0,
|
||||
"total_spent" INTEGER NOT NULL DEFAULT 0,
|
||||
"converted_consume" DECIMAL(10,2) NOT NULL DEFAULT 0,
|
||||
"last_converted_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "user_points_pkey" PRIMARY KEY ("user_id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "points_logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"type" "PointsLogType" NOT NULL,
|
||||
"amount" INTEGER NOT NULL,
|
||||
"points_before" INTEGER NOT NULL,
|
||||
"points_after" INTEGER NOT NULL,
|
||||
"related_id" INTEGER,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "points_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "lotteries" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"cost_points" INTEGER NOT NULL,
|
||||
"is_active" BOOLEAN NOT NULL DEFAULT true,
|
||||
"start_at" TIMESTAMP(3),
|
||||
"end_at" TIMESTAMP(3),
|
||||
"total_draws" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
"created_by" INTEGER NOT NULL,
|
||||
|
||||
CONSTRAINT "lotteries_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "lottery_prizes" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"lottery_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"type" "LotteryPrizeType" NOT NULL,
|
||||
"value" INTEGER NOT NULL DEFAULT 0,
|
||||
"probability" DECIMAL(5,2) NOT NULL,
|
||||
"total_quantity" INTEGER,
|
||||
"remain_quantity" INTEGER,
|
||||
"display_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"instance_desc" TEXT,
|
||||
|
||||
CONSTRAINT "lottery_prizes_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "lottery_records" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"lottery_id" INTEGER NOT NULL,
|
||||
"prize_id" INTEGER NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"prize_type" "LotteryPrizeType" NOT NULL,
|
||||
"prize_value" INTEGER NOT NULL,
|
||||
"prize_name" TEXT NOT NULL,
|
||||
"status" "LotteryRecordStatus" NOT NULL DEFAULT 'delivered',
|
||||
"points_spent" INTEGER NOT NULL,
|
||||
"delivered_at" TIMESTAMP(3),
|
||||
"delivered_by" INTEGER,
|
||||
"ticket_id" INTEGER,
|
||||
"notification_sent" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "lottery_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "lottery_notification_configs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"lottery_id" INTEGER NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"type" TEXT NOT NULL,
|
||||
"config" JSONB NOT NULL,
|
||||
"notify_balance" BOOLEAN NOT NULL DEFAULT true,
|
||||
"notify_instance" BOOLEAN NOT NULL DEFAULT true,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "lottery_notification_configs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "points_logs_user_id_created_at_idx" ON "points_logs"("user_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "lottery_prizes_lottery_id_idx" ON "lottery_prizes"("lottery_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "lottery_records_user_id_created_at_idx" ON "lottery_records"("user_id", "created_at" DESC);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "lottery_records_lottery_id_idx" ON "lottery_records"("lottery_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "lottery_records_status_idx" ON "lottery_records"("status");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "lottery_notification_configs_lottery_id_key" ON "lottery_notification_configs"("lottery_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_points" ADD CONSTRAINT "user_points_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "points_logs" ADD CONSTRAINT "points_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "lottery_prizes" ADD CONSTRAINT "lottery_prizes_lottery_id_fkey" FOREIGN KEY ("lottery_id") REFERENCES "lotteries"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "lottery_records" ADD CONSTRAINT "lottery_records_lottery_id_fkey" FOREIGN KEY ("lottery_id") REFERENCES "lotteries"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "lottery_records" ADD CONSTRAINT "lottery_records_prize_id_fkey" FOREIGN KEY ("prize_id") REFERENCES "lottery_prizes"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "lottery_records" ADD CONSTRAINT "lottery_records_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "lottery_notification_configs" ADD CONSTRAINT "lottery_notification_configs_lottery_id_fkey" FOREIGN KEY ("lottery_id") REFERENCES "lotteries"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add 'checkin' value to PointsLogType enum
|
||||
ALTER TYPE "PointsLogType" ADD VALUE 'checkin';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum: Add 'recreate' to InstanceTaskType
|
||||
ALTER TYPE "InstanceTaskType" ADD VALUE 'recreate';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterEnum: Add 'recreate_instance' to OperationType
|
||||
ALTER TYPE "OperationType" ADD VALUE 'recreate_instance';
|
||||
@@ -0,0 +1,6 @@
|
||||
-- Add transfer fee field to instance_transfers table
|
||||
ALTER TABLE "instance_transfers" ADD COLUMN "fee" DECIMAL(10,2);
|
||||
|
||||
-- Add transfer_fee and transfer_refund to BalanceLogType enum
|
||||
ALTER TYPE "BalanceLogType" ADD VALUE IF NOT EXISTS 'transfer_fee';
|
||||
ALTER TYPE "BalanceLogType" ADD VALUE IF NOT EXISTS 'transfer_refund';
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
-- Add transfer_fee to BillingRecordType enum
|
||||
-- This is safe: adding a new enum value does not affect existing records
|
||||
ALTER TYPE "BillingRecordType" ADD VALUE IF NOT EXISTS 'transfer_fee';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "announcement" TEXT;
|
||||
@@ -0,0 +1,47 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "ResourcePoolAction" AS ENUM ('checkin', 'redeem', 'admin_grant', 'system_grant', 'lottery', 'apply');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_resource_pools" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"cpu" INTEGER NOT NULL DEFAULT 0,
|
||||
"memory" INTEGER NOT NULL DEFAULT 0,
|
||||
"disk" INTEGER NOT NULL DEFAULT 0,
|
||||
"traffic" BIGINT NOT NULL DEFAULT 0,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "user_resource_pools_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "resource_pool_logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"action" "ResourcePoolAction" NOT NULL,
|
||||
"resource_type" "RedeemCodeType" NOT NULL,
|
||||
"amount" INTEGER NOT NULL,
|
||||
"instance_id" INTEGER,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "resource_pool_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "user_resource_pools_user_id_key" ON "user_resource_pools"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "resource_pool_logs_user_id_idx" ON "resource_pool_logs"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "resource_pool_logs_user_id_action_idx" ON "resource_pool_logs"("user_id", "action");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_resource_pools" ADD CONSTRAINT "user_resource_pools_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "resource_pool_logs" ADD CONSTRAINT "resource_pool_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "resource_pool_logs" ADD CONSTRAINT "resource_pool_logs_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
-- AlterEnum: Add resource prize types to LotteryPrizeType
|
||||
ALTER TYPE "LotteryPrizeType" ADD VALUE 'cpu';
|
||||
ALTER TYPE "LotteryPrizeType" ADD VALUE 'memory';
|
||||
ALTER TYPE "LotteryPrizeType" ADD VALUE 'disk';
|
||||
ALTER TYPE "LotteryPrizeType" ADD VALUE 'traffic';
|
||||
|
||||
-- AlterEnum: Add system_redeem to ResourcePoolAction
|
||||
ALTER TYPE "ResourcePoolAction" ADD VALUE 'system_redeem';
|
||||
@@ -0,0 +1,29 @@
|
||||
-- CreateTable
|
||||
CREATE TABLE "user_destroy_records" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"host_id" INTEGER NOT NULL,
|
||||
"instance_id" INTEGER NOT NULL,
|
||||
"instance_name" TEXT NOT NULL,
|
||||
"destroyed_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"refund_amount" DECIMAL(10,2) NOT NULL,
|
||||
"fee_amount" DECIMAL(10,2) NOT NULL,
|
||||
"is_first_time" BOOLEAN NOT NULL,
|
||||
|
||||
CONSTRAINT "user_destroy_records_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_destroy_records_user_id_idx" ON "user_destroy_records"("user_id");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "user_destroy_records_user_id_host_id_idx" ON "user_destroy_records"("user_id", "host_id");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_destroy_records" ADD CONSTRAINT "user_destroy_records_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_destroy_records" ADD CONSTRAINT "user_destroy_records_host_id_fkey" FOREIGN KEY ("host_id") REFERENCES "hosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "user_destroy_records" ADD CONSTRAINT "user_destroy_records_instance_id_fkey" FOREIGN KEY ("instance_id") REFERENCES "instances"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,53 @@
|
||||
-- 托管余额系统
|
||||
-- 允许满足条件的用户自行托管节点并获得收益
|
||||
|
||||
-- 枚举类型
|
||||
CREATE TYPE "HostingBalanceType" AS ENUM ('income', 'unfreeze', 'withdraw');
|
||||
CREATE TYPE "WithdrawalStatus" AS ENUM ('pending', 'approved', 'rejected', 'completed');
|
||||
CREATE TYPE "WithdrawalTarget" AS ENUM ('balance', 'usdt');
|
||||
|
||||
-- 在 users 表添加托管余额字段
|
||||
ALTER TABLE "users" ADD COLUMN "hosting_balance" DECIMAL(10, 2) NOT NULL DEFAULT 0;
|
||||
|
||||
-- 托管余额变动日志表
|
||||
CREATE TABLE "hosting_balance_logs" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"type" "HostingBalanceType" NOT NULL,
|
||||
"amount" DECIMAL(10, 2) NOT NULL,
|
||||
"frozen" BOOLEAN NOT NULL DEFAULT true,
|
||||
"unfreeze_at" TIMESTAMP(3),
|
||||
"related_id" INTEGER,
|
||||
"remark" TEXT,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "hosting_balance_logs_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 托管余额提现记录表
|
||||
CREATE TABLE "hosting_withdrawals" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"amount" DECIMAL(10, 2) NOT NULL,
|
||||
"fee_rate" DECIMAL(5, 4) NOT NULL,
|
||||
"fee_amount" DECIMAL(10, 2) NOT NULL,
|
||||
"actual_amount" DECIMAL(10, 2) NOT NULL,
|
||||
"target" "WithdrawalTarget" NOT NULL,
|
||||
"usdt_address" TEXT,
|
||||
"status" "WithdrawalStatus" NOT NULL DEFAULT 'pending',
|
||||
"reject_reason" TEXT,
|
||||
"processed_at" TIMESTAMP(3),
|
||||
"processed_by" INTEGER,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
CONSTRAINT "hosting_withdrawals_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX "hosting_balance_logs_user_id_frozen_idx" ON "hosting_balance_logs"("user_id", "frozen");
|
||||
CREATE INDEX "hosting_balance_logs_unfreeze_at_idx" ON "hosting_balance_logs"("unfreeze_at");
|
||||
CREATE INDEX "hosting_withdrawals_user_id_status_idx" ON "hosting_withdrawals"("user_id", "status");
|
||||
|
||||
-- 外键约束
|
||||
ALTER TABLE "hosting_balance_logs" ADD CONSTRAINT "hosting_balance_logs_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "hosting_withdrawals" ADD CONSTRAINT "hosting_withdrawals_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add hosting_deduction to BalanceLogType enum
|
||||
ALTER TYPE "BalanceLogType" ADD VALUE IF NOT EXISTS 'hosting_deduction';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- Add deduction to HostingBalanceType enum
|
||||
ALTER TYPE "HostingBalanceType" ADD VALUE IF NOT EXISTS 'deduction';
|
||||
@@ -0,0 +1,13 @@
|
||||
-- Add HostingActionType enum and action_type column to hosting_balance_logs
|
||||
|
||||
-- Create HostingActionType enum
|
||||
CREATE TYPE "HostingActionType" AS ENUM ('purchase', 'renew', 'upgrade', 'destroy', 'unfreeze', 'withdraw');
|
||||
|
||||
-- Add action_type column (nullable for existing records)
|
||||
ALTER TABLE "hosting_balance_logs" ADD COLUMN "action_type" "HostingActionType";
|
||||
|
||||
-- Backfill action_type for existing records based on type
|
||||
UPDATE "hosting_balance_logs" SET "action_type" = 'purchase' WHERE "type" = 'income' AND "action_type" IS NULL;
|
||||
UPDATE "hosting_balance_logs" SET "action_type" = 'unfreeze' WHERE "type" = 'unfreeze' AND "action_type" IS NULL;
|
||||
UPDATE "hosting_balance_logs" SET "action_type" = 'destroy' WHERE "type" = 'deduction' AND "action_type" IS NULL;
|
||||
UPDATE "hosting_balance_logs" SET "action_type" = 'withdraw' WHERE "type" = 'withdraw' AND "action_type" IS NULL;
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "enable_resource_pool" BOOLEAN NOT NULL DEFAULT true;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
-- 为 BalanceLogType 枚举添加 hosting_withdraw 值
|
||||
-- 用于记录托管余额提现到面板余额的操作
|
||||
|
||||
ALTER TYPE "BalanceLogType" ADD VALUE IF NOT EXISTS 'hosting_withdraw';
|
||||
@@ -0,0 +1,4 @@
|
||||
-- 为 HostingActionType 枚举添加 admin_adjust 值
|
||||
-- 用于标识管理员调整托管余额的操作
|
||||
|
||||
ALTER TYPE "HostingActionType" ADD VALUE IF NOT EXISTS 'admin_adjust';
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "hosts" ADD COLUMN "probe_url" TEXT;
|
||||
@@ -0,0 +1,112 @@
|
||||
-- 域名邮箱模块
|
||||
-- 包含邮箱源、方案、订阅、域名、账户管理
|
||||
|
||||
-- 创建枚举类型
|
||||
CREATE TYPE "MailDomainStatus" AS ENUM ('pending', 'verified', 'suspended');
|
||||
CREATE TYPE "MailSubscriptionStatus" AS ENUM ('active', 'expired', 'suspended');
|
||||
CREATE TYPE "MailBillingCycle" AS ENUM ('monthly', 'yearly');
|
||||
|
||||
-- 邮箱源表(数据中心)
|
||||
CREATE TABLE "mail_sources" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"code" TEXT NOT NULL,
|
||||
"api_url" TEXT NOT NULL,
|
||||
"api_key" TEXT NOT NULL,
|
||||
"smartermail_url" TEXT NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mail_sources_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 套餐方案表
|
||||
CREATE TABLE "mail_plans" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"source_id" INTEGER NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"description" TEXT,
|
||||
"domain_limit" INTEGER NOT NULL,
|
||||
"disk_limit_gb" INTEGER NOT NULL,
|
||||
"billing_cycle" "MailBillingCycle" NOT NULL,
|
||||
"price" DECIMAL(10,2) NOT NULL,
|
||||
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
"sort_order" INTEGER NOT NULL DEFAULT 0,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mail_plans_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 用户订阅表
|
||||
CREATE TABLE "mail_subscriptions" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"user_id" INTEGER NOT NULL,
|
||||
"source_id" INTEGER NOT NULL,
|
||||
"plan_id" INTEGER NOT NULL,
|
||||
"status" "MailSubscriptionStatus" NOT NULL DEFAULT 'active',
|
||||
"domain_limit" INTEGER NOT NULL,
|
||||
"disk_limit_gb" INTEGER NOT NULL,
|
||||
"expires_at" TIMESTAMP(3) NOT NULL,
|
||||
"auto_renew" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mail_subscriptions_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 域名表
|
||||
CREATE TABLE "mail_domains" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"subscription_id" INTEGER NOT NULL,
|
||||
"source_id" INTEGER NOT NULL,
|
||||
"domain" TEXT NOT NULL,
|
||||
"status" "MailDomainStatus" NOT NULL DEFAULT 'pending',
|
||||
"admin_username" TEXT,
|
||||
"admin_password" TEXT,
|
||||
"disk_used_mb" INTEGER NOT NULL DEFAULT 0,
|
||||
"verified_at" TIMESTAMP(3),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mail_domains_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 邮箱账户表
|
||||
CREATE TABLE "mail_accounts" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"domain_id" INTEGER NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"username" TEXT NOT NULL,
|
||||
"display_name" TEXT,
|
||||
"disk_limit_mb" INTEGER NOT NULL DEFAULT 2048,
|
||||
"disk_used_mb" INTEGER NOT NULL DEFAULT 0,
|
||||
"is_admin" BOOLEAN NOT NULL DEFAULT false,
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "mail_accounts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- 创建唯一索引
|
||||
CREATE UNIQUE INDEX "mail_sources_code_key" ON "mail_sources"("code");
|
||||
CREATE UNIQUE INDEX "mail_domains_domain_source_id_key" ON "mail_domains"("domain", "source_id");
|
||||
CREATE UNIQUE INDEX "mail_accounts_domain_id_username_key" ON "mail_accounts"("domain_id", "username");
|
||||
|
||||
-- 创建普通索引
|
||||
CREATE INDEX "mail_plans_source_id_enabled_idx" ON "mail_plans"("source_id", "enabled");
|
||||
CREATE INDEX "mail_subscriptions_user_id_status_idx" ON "mail_subscriptions"("user_id", "status");
|
||||
CREATE INDEX "mail_subscriptions_expires_at_idx" ON "mail_subscriptions"("expires_at");
|
||||
CREATE INDEX "mail_domains_subscription_id_idx" ON "mail_domains"("subscription_id");
|
||||
CREATE INDEX "mail_accounts_domain_id_idx" ON "mail_accounts"("domain_id");
|
||||
|
||||
-- 添加外键约束
|
||||
ALTER TABLE "mail_plans" ADD CONSTRAINT "mail_plans_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "mail_sources"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "mail_subscriptions" ADD CONSTRAINT "mail_subscriptions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "mail_subscriptions" ADD CONSTRAINT "mail_subscriptions_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "mail_sources"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "mail_subscriptions" ADD CONSTRAINT "mail_subscriptions_plan_id_fkey" FOREIGN KEY ("plan_id") REFERENCES "mail_plans"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "mail_domains" ADD CONSTRAINT "mail_domains_subscription_id_fkey" FOREIGN KEY ("subscription_id") REFERENCES "mail_subscriptions"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "mail_domains" ADD CONSTRAINT "mail_domains_source_id_fkey" FOREIGN KEY ("source_id") REFERENCES "mail_sources"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
ALTER TABLE "mail_accounts" ADD CONSTRAINT "mail_accounts_domain_id_fkey" FOREIGN KEY ("domain_id") REFERENCES "mail_domains"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user