Files
2026-06-20 14:26:29 +08:00

536 lines
18 KiB
SQL

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