Unifi-Voucher-Tool/database.sql
Claude 1a2f86ed3d
Merge origin/main: Review-Branch mit v2.4.0-Features zusammenführen
Konfliktauflösung kombiniert beide Seiten:
- Auth: Secure-Cookie-Flag + DB-Session-Handler (main)
- UniFiController: createVouchers (n-Parameter, 1 API-Call) + QoS-Optionen (main)
- index.php: IP-Rate-Limit/PRG/Sticky-Forms + CAPTCHA/SMS/QoS/Tageslimit (main)
- users.php: 2FA-Reset (main) auf POST+PRG umgestellt wie übrige Aktionen
- forgot_password: Session-Throttle (main) + IP-Throttle kombiniert
- Eigene Migrationen wegen Nummernkollision auf 0005/0006 umbenannt

https://claude.ai/code/session_01KKVpVPJjrTKGoRgpJcySD4
2026-06-09 20:03:32 +00:00

178 lines
6.9 KiB
SQL

-- UniFi Voucher Management System - Datenbankstruktur
CREATE TABLE IF NOT EXISTS `settings` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`setting_key` VARCHAR(100) UNIQUE NOT NULL,
`setting_value` TEXT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sites` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`site_id` VARCHAR(100) NOT NULL,
`unifi_controller_url` VARCHAR(255) NOT NULL,
`unifi_username` VARCHAR(100) NOT NULL,
`unifi_password` VARCHAR(255) NOT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`public_access` TINYINT(1) DEFAULT 0,
`ssl_verify` TINYINT(1) NOT NULL DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `users` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`email` VARCHAR(255) UNIQUE NOT NULL,
`name` VARCHAR(255),
`password_hash` VARCHAR(255),
`is_admin` TINYINT(1) DEFAULT 0,
`is_active` TINYINT(1) DEFAULT 1,
`microsoft_id` VARCHAR(255) UNIQUE,
`totp_secret` VARCHAR(64) NULL,
`totp_enabled` TINYINT(1) NOT NULL DEFAULT 0,
`totp_backup_codes` TEXT NULL,
`last_login` TIMESTAMP NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_email` (`email`),
INDEX `idx_microsoft` (`microsoft_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `user_site_access` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT NOT NULL,
`site_id` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON DELETE CASCADE,
UNIQUE KEY `unique_user_site` (`user_id`, `site_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `voucher_templates` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`max_uses` INT NOT NULL DEFAULT 1,
`expire_minutes` INT NOT NULL DEFAULT 480,
`description` VARCHAR(500),
`qos_rate_max_down` INT NULL,
`qos_rate_max_up` INT NULL,
`qos_usage_quota` INT NULL,
`is_active` TINYINT(1) DEFAULT 1,
`created_by` INT,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`created_by`) REFERENCES `users`(`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `api_keys` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`key_prefix` VARCHAR(16) NOT NULL,
`key_hash` VARCHAR(255) NOT NULL,
`scope` VARCHAR(16) NOT NULL DEFAULT 'write',
`rate_limit` INT NOT NULL DEFAULT 0,
`created_by` INT,
`last_used_at` TIMESTAMP NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`created_by`) REFERENCES `users`(`id`) ON DELETE SET NULL,
INDEX `idx_prefix` (`key_prefix`),
INDEX `idx_active` (`is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `api_key_hits` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`api_key_id` INT NOT NULL,
`hit_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_key_time` (`api_key_id`, `hit_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `vouchers` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`site_id` INT NOT NULL,
`user_id` INT,
`voucher_code` VARCHAR(50) NOT NULL,
`voucher_name` VARCHAR(255) NOT NULL,
`max_uses` INT NOT NULL,
`expire_minutes` INT NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`unifi_voucher_id` VARCHAR(100),
`status` ENUM('valid', 'used', 'expired') DEFAULT 'valid',
`used_count` INT DEFAULT 0,
`expires_at` TIMESTAMP NULL,
`synced_from_unifi` TINYINT(1) DEFAULT 0,
`last_sync` TIMESTAMP NULL,
FOREIGN KEY (`site_id`) REFERENCES `sites`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL,
INDEX `idx_site` (`site_id`),
INDEX `idx_created` (`created_at`),
INDEX `idx_unifi_id` (`unifi_voucher_id`),
INDEX `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `sessions` (
`id` VARCHAR(128) PRIMARY KEY,
`user_id` INT NULL,
`data` TEXT,
`expires_at` TIMESTAMP NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
INDEX `idx_expires` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `login_attempts` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`ip_address` VARCHAR(45) NOT NULL,
`email` VARCHAR(255) NOT NULL,
`attempted_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_ip` (`ip_address`),
INDEX `idx_email` (`email`),
INDEX `idx_attempted` (`attempted_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `audit_log` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT,
`action` VARCHAR(100) NOT NULL,
`entity_type` VARCHAR(50),
`entity_id` VARCHAR(100),
`details` TEXT,
`ip_address` VARCHAR(45),
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE SET NULL,
INDEX `idx_user` (`user_id`),
INDEX `idx_action` (`action`),
INDEX `idx_created` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- IP-basiertes Request-Throttling (anonyme Voucher-Erstellung, Passwort-Resets)
CREATE TABLE IF NOT EXISTS `request_throttle` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`ip_address` VARCHAR(45) NOT NULL,
`action` VARCHAR(50) NOT NULL,
`weight` INT NOT NULL DEFAULT 1,
`requested_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX `idx_throttle` (`action`, `ip_address`, `requested_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE IF NOT EXISTS `password_reset_tokens` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`user_id` INT NOT NULL,
`token` VARCHAR(128) NOT NULL,
`expires_at` TIMESTAMP NOT NULL,
`used` TINYINT(1) DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`user_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
UNIQUE KEY `unique_token` (`token`),
INDEX `idx_expires` (`expires_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Migrations für bestehende Installationen:
-- ALTER TABLE vouchers ADD COLUMN IF NOT EXISTS `status` ENUM('valid', 'used', 'expired') DEFAULT 'valid';
-- ALTER TABLE vouchers ADD COLUMN IF NOT EXISTS `used_count` INT DEFAULT 0;
-- ALTER TABLE vouchers ADD COLUMN IF NOT EXISTS `expires_at` TIMESTAMP NULL;
-- ALTER TABLE vouchers ADD COLUMN IF NOT EXISTS `synced_from_unifi` TINYINT(1) DEFAULT 0;
-- ALTER TABLE vouchers ADD COLUMN IF NOT EXISTS `last_sync` TIMESTAMP NULL;