From 370c398f4868b158d5fa264b2df3ceba892f5a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ersin=20KO=C3=87?= Date: Fri, 11 Sep 2026 20:13:33 +0300 Subject: [PATCH] fix(database): bos veritabaninda kurulum ERROR 1005 ile kiriliyordu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Kok neden: repoda hicbir yerde CREATE TABLE users yoktu; schema.sql'in ilk tablosu (departments) manager_id uzerinden users(id)'ye FK iceriyor ve bolum 10 yalnizca ALTER ediyordu. README'nin belgelenmis kurulum akisi (schema.sql + seed.sql) bos veritabaninda ERROR 1005 (errno 150) ile kiriliyordu. Ikinci yuzey: seed.sql admin@riskops.local kullanıcısını yalnizca UPDATE ediyor, INSERT eden yoktu - kurulum sonunda belgelenmis giris hesabi (Admin123456, ilk giriste degisim zorunlu) olusmuyordu. Cozum: - schema.sql bolum 0: CREATE TABLE IF NOT EXISTS users (temel kolonlar; role ENUM varsayilan 'viewer' - en az yetki). Bolum 10'daki IF NOT EXISTS ALTER'lar yukseltme yolu olarak aynen kaldi (idempotent). - seed.sql: belgelenmis ilk admin INSERT IGNORE ile eklenir (bcrypt, must_change_password=1); mevcut UPDATE onu IT departmanina baglar. Kanit: round-owned proof-script GERCEK schema.sql + seed.sql dosyalarini bosaltilmis karalama veritabanina calistirdi: oncesinde FAIL (statement #4: ERROR 1005 departments), sonrasi PASS (10 tablo, admin dogrulandi, password_verify('Admin123456') true). Dayanikli regresyon testi tools/fresh_bootstrap_test.php eklendi (yerel MariaDB ister; bos veritabaninda kurulum + ust uste tekrar calismanin idempotentligi; karalama veritabanini kendisi dusurur): 13 OK / 0 FAIL. Mevcut kurulumda geriye donuk uyumluluk: duzeltilmis schema+seed canli veritabanina tekrar uygulandi - temiz no-op. php tools/smoke_test.php: 58 OK / 1 FAIL (yalnizca /var/www/riskops yerlesim yolu onaylamasi; islevsel tum kontroller geciyor). --- database/schema.sql | 31 +++++- database/seed.sql | 14 +++ tools/fresh_bootstrap_test.php | 186 +++++++++++++++++++++++++++++++++ 3 files changed, 229 insertions(+), 2 deletions(-) create mode 100644 tools/fresh_bootstrap_test.php diff --git a/database/schema.sql b/database/schema.sql index 3dd9a5b..f8b2f44 100644 --- a/database/schema.sql +++ b/database/schema.sql @@ -18,6 +18,32 @@ SET SESSION sql_mode = 'STRICT_ALL_TABLES,NO_ENGINE_SUBSTITUTION'; SET FOREIGN_KEY_CHECKS = 1; +-- --------------------------------------------------------------------- +-- 0. users (kimlik dogrulama) +-- +-- BOS veritabaninda calisan kurulum icin BURADA olusturulur: +-- asagidaki ilk tablo (departments) manager_id uzerinden users(id)'ye +-- FK icerir; tablo yoksa kurulum ERROR 1005 (errno 150) ile kirilir. +-- Bolum 10'daki ALTER'lar yukseltmeler icin aynen kalir (IF NOT EXISTS +-- ile idempotent) ve bu tabloyu departman/title/phone/ +-- must_change_password/password_changed_at/created_by kolonlariyle +-- zenginlestirir. +-- --------------------------------------------------------------------- +CREATE TABLE IF NOT EXISTS users ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT, + name VARCHAR(100) NOT NULL, + email VARCHAR(150) NOT NULL, + password VARCHAR(255) NOT NULL, + role ENUM('admin','manager','analyst','viewer') NOT NULL DEFAULT 'viewer', + status TINYINT(1) NOT NULL DEFAULT 1, + last_login_at DATETIME NULL DEFAULT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (id), + UNIQUE KEY uq_users_email (email) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + + -- --------------------------------------------------------------------- -- 1. departments -- --------------------------------------------------------------------- @@ -311,8 +337,9 @@ CREATE TABLE IF NOT EXISTS login_attempts ( -- --------------------------------------------------------------------- --- 10. users -> MEVCUT TABLO ALTER EDILIYOR (SILINMIYOR) --- departments tablosu olustuktan SONRA calismali. +-- 10. users -> bolum 0'da olusturulan tablo burada ALTER edilir +-- (mevcut kurulumlar korunur; departments tablosu olustuktan +-- SONRA calismali - fk_users_department ona baglidir). -- --------------------------------------------------------------------- ALTER TABLE users ADD COLUMN IF NOT EXISTS department_id INT UNSIGNED NULL DEFAULT NULL AFTER role, diff --git a/database/seed.sql b/database/seed.sql index c175650..0929302 100644 --- a/database/seed.sql +++ b/database/seed.sql @@ -93,6 +93,20 @@ VALUES 'Yazdirilan raporlarin altinda gorunen aciklama satiri.', 1, 80); +-- --------------------------------------------------------------------- +-- ILK ADMIN KULLANICISI +-- README: "Ilk giris: admin@riskops.local / Admin123456 - uygulama +-- ilk giriste parola degistirmeye zorlar." Bu satir olmazsa belgelenmis +-- kurulum sonunda sisteme giris yapilabilecek hicbir hesap olusmaz +-- (asagidaki UPDATE yalnizca mevcut admin kaydini departmana baglar). +-- Parola bcrypt ile hashlenmistir; ilk giriste degisim zorunludur. +-- --------------------------------------------------------------------- +INSERT IGNORE INTO users (name, email, password, role, status, must_change_password) +VALUES ('System Administrator', 'admin@riskops.local', + '$2y$12$YOWfKRyFQrinKDSIU50zHOQww8udcyN6zQm7BmKuWcp41NkLv8SBS', + 'admin', 1, 1); + + -- --------------------------------------------------------------------- -- MEVCUT ADMIN KULLANICISINI IT departmanina bagla -- (yalnizca departmani bos ise - kullanici secimini ezmez) diff --git a/tools/fresh_bootstrap_test.php b/tools/fresh_bootstrap_test.php new file mode 100644 index 0000000..a73155f --- /dev/null +++ b/tools/fresh_bootstrap_test.php @@ -0,0 +1,186 @@ +exec(trim($stmt)); + } catch (PDOException $e) { + return ['error' => $e->getMessage(), 'sql' => trim($stmt), 'stmtno' => $count]; + } + $stmt = ''; + } + } + return null; +} + +$PASS = 0; +$FAIL = 0; + +function check(string $label, bool $ok, string $detail = ''): void +{ + global $PASS, $FAIL; + if ($ok) { + $PASS++; + printf(" [ OK ] %-56s %s\n", $label, $detail); + } else { + $FAIL++; + printf(" [FAIL] %-56s %s\n", $label, $detail); + } +} + +function section(string $title): void +{ + echo "\n" . str_repeat('-', 72) . "\n " . $title . "\n" . str_repeat('-', 72) . "\n"; +} + +echo "\n=============== Fresh Bootstrap Regression Test ==============="; + +/* --- Ortam ----------------------------------------------------------- */ + +try { + $root = new PDO( + 'mysql:host=127.0.0.1;port=3306;charset=utf8mb4', + 'root', + BOOTSTRAP_TEST_ROOT_PASS, + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_TIMEOUT => 5] + ); +} catch (Throwable $e) { + echo "\nYerel MariaDB'ye baglanilamadi (konteyner: docker start riskops-mariadb).\n"; + echo 'Hata: ' . $e->getMessage() . "\n"; + echo "FRESH BOOTSTRAP TEST: FAIL\n"; + exit(1); +} + +$root->exec('DROP DATABASE IF EXISTS ' . BOOTSTRAP_TEST_DB); +$root->exec('CREATE DATABASE ' . BOOTSTRAP_TEST_DB + . ' CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'); +$pdo = new PDO( + 'mysql:host=127.0.0.1;port=3306;dbname=' . BOOTSTRAP_TEST_DB . ';charset=utf8mb4', + 'root', + BOOTSTRAP_TEST_ROOT_PASS, + [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, + PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC] +); + +/* ------------------------------------------------------------------ */ +section('1) Bos veritabaninda belgelenmis kurulum'); +/* ------------------------------------------------------------------ */ + +$err = bootstrap_run_sql_file($pdo, __DIR__ . '/../database/schema.sql'); +check('schema.sql bos veritabaninda hatasiz uygulanir', $err === null, + $err !== null ? sprintf('#%d: %s', $err['stmtno'], $err['error']) : ''); + +if ($err === null) { + $tables = $pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN); + sort($tables); + check('10 tablo olusur', $tables === $expectedTables, count($tables) . ' tablo'); + + $err = bootstrap_run_sql_file($pdo, __DIR__ . '/../database/seed.sql'); + check('seed.sql hatasiz uygulanir', $err === null, + $err !== null ? sprintf('#%d: %s', $err['stmtno'], $err['error']) : ''); + + $u = $pdo->query( + "SELECT password, role, status, must_change_password, department_id + FROM users WHERE email = 'admin@riskops.local' LIMIT 1" + )->fetch(); + + check('belgelenmis ilk admin olusur', is_array($u)); + if (is_array($u)) { + check('admin rol/durum/parola-degisimi zorunlu', + $u['role'] === 'admin' && (int)$u['status'] === 1 && (int)$u['must_change_password'] === 1); + check('admin departmana bagli (IT)', $u['department_id'] !== null); + check("password_verify('Admin123456')", + password_verify('Admin123456', (string)$u['password'])); + } + $settingsRows = (int)$pdo->query('SELECT COUNT(*) FROM settings')->fetchColumn(); + check('ayarlar yuklendi', $settingsRows === 17, $settingsRows . ' satir'); +} + +/* ------------------------------------------------------------------ */ +section('2) Idempotenti: ayni dosyalar ust uste tekrar calisir'); +/* ------------------------------------------------------------------ */ + +$err = bootstrap_run_sql_file($pdo, __DIR__ . '/../database/schema.sql'); +check('schema.sql 2. kez hatasiz uygulanir', $err === null, + $err !== null ? sprintf('#%d: %s', $err['stmtno'], $err['error']) : ''); + +$err = bootstrap_run_sql_file($pdo, __DIR__ . '/../database/seed.sql'); +check('seed.sql 2. kez hatasiz uygulanir', $err === null, + $err !== null ? sprintf('#%d: %s', $err['stmtno'], $err['error']) : ''); + +$tables2 = $pdo->query('SHOW TABLES')->fetchAll(PDO::FETCH_COLUMN); +sort($tables2); +check('tablo sayisi degismedi (10)', $tables2 === $expectedTables, count($tables2) . ' tablo'); + +$adminCount = (int)$pdo->query( + "SELECT COUNT(*) FROM users WHERE email = 'admin@riskops.local'" +)->fetchColumn(); +check('admin kopyalanmadi (INSERT IGNORE)', $adminCount === 1, $adminCount . ' satir'); + +$settings2 = (int)$pdo->query('SELECT COUNT(*) FROM settings')->fetchColumn(); +check('ayarlar kopyalanmadi', $settings2 === $settingsRows, $settings2 . ' satir'); + +/* --- Karalama veritabanini birak ------------------------------------ */ + +$root->exec('DROP DATABASE IF EXISTS ' . BOOTSTRAP_TEST_DB); + +/* ------------------------------------------------------------------ */ + +echo "\n" . str_repeat('-', 72) . "\n"; +printf("Sonuc: %d OK, %d FAIL\n", $PASS, $FAIL); +if ($FAIL > 0) { + echo "FRESH BOOTSTRAP TEST: FAIL\n"; + exit(1); +} +echo "FRESH BOOTSTRAP TEST: PASS\n"; +exit(0);