Bug fixes: - UniFiController: add CURLOPT_TIMEOUT (10s) and CURLOPT_CONNECTTIMEOUT (5s) to login() and apiRequest() — prevents page freeze when controller unreachable - UniFiController: fix login response validation for UniFi OS API which returns a user object instead of meta.rc=ok - admin/sites.php: add JS loading state on form submit to give visual feedback - login.php: handle new 'rate_limited' return value from Auth::login() New features: - Database: in-memory settings cache eliminates redundant DB queries per request - Auth: login rate limiting (10 attempts per 10 min per IP/email) via login_attempts table - admin/vouchers.php: CSV export with UTF-8 BOM for Excel compatibility - admin/vouchers.php: client-side pagination (50 per page) - index.php: QR code display after voucher creation (qrcodejs CDN) - Mailer: sendTestEmail() method - admin/settings.php: SMTP test button with AJAX handler - database.sql: add login_attempts and audit_log tables Readme: condensed from ~420 to ~220 lines, removed duplicated sections, M365 Azure Portal walkthrough, contribution guidelines, update/migration section https://claude.ai/code/session_01UsuvFAmmeagtQa14QA4iaq
118 lines
No EOL
4.7 KiB
SQL
118 lines
No EOL
4.7 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,
|
|
`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,
|
|
`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 `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;
|
|
|
|
-- Migration für bestehende Tabellen (falls bereits vorhanden):
|
|
-- ALTER TABLE vouchers ADD COLUMN `status` ENUM('valid', 'used', 'expired') DEFAULT 'valid';
|
|
-- ALTER TABLE vouchers ADD COLUMN `used_count` INT DEFAULT 0;
|
|
-- ALTER TABLE vouchers ADD COLUMN `expires_at` TIMESTAMP NULL;
|
|
-- ALTER TABLE vouchers ADD COLUMN `synced_from_unifi` TINYINT(1) DEFAULT 0;
|
|
-- ALTER TABLE vouchers ADD COLUMN `last_sync` TIMESTAMP NULL;
|
|
-- ALTER TABLE vouchers ADD INDEX `idx_unifi_id` (`unifi_voucher_id`);
|
|
-- ALTER TABLE vouchers ADD INDEX `idx_status` (`status`);
|
|
|
|
CREATE TABLE IF NOT EXISTS `sessions` (
|
|
`id` VARCHAR(128) PRIMARY KEY,
|
|
`user_id` INT NOT 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;
|
|
|
|
-- Migration für bestehende Installationen:
|
|
-- Neue Tabellen werden automatisch erstellt (CREATE TABLE IF NOT EXISTS) |