import os import stat # ============================================================================== # DEPLOYMENT SCRIPT FOR BLACK-CLOVER (v6 - CSRF Protection Removed) # ============================================================================== # This script generates the complete file and folder structure for the # Black-Clover project. CSRF functionality has been removed to resolve # persistent validation errors as requested. # # WARNING: This version is less secure against Cross-Site Request Forgery # and is only suitable for strictly local, trusted network environments. # # To run: # 1. Save this file as 'deploy.py'. # 2. Run from the command line: python deploy.py # ============================================================================== PROJECT_NAME = "black-clover" def create_file(path, content): """Creates a file with the given content, ensuring the directory exists.""" os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, "w", encoding="utf-8") as f: f.write(content.strip()) print(f"Created file: {path}") # ============================================================================== # --- FILE CONTENTS --- # ============================================================================== # --- Config (Outside Web Root) --- config_php_content = r''' 0, 'path' => '/', 'domain' => $_SERVER['HTTP_HOST'], 'secure' => isset($_SERVER['HTTPS']), 'httponly' => true, 'samesite' => 'Strict']; session_set_cookie_params($p); session_start(); } start_secure_session(); function manage_session_security() { if (is_logged_in() && isset($_SESSION['last_activity']) && (time() - $_SESSION['last_activity']) > SESSION_TIMEOUT_SECONDS) { session_unset(); session_destroy(); start_secure_session(); if (is_api_request()) { header('Content-Type: application/json'); http_response_code(401); echo json_encode(['error' => 'Session expired due to inactivity.']); exit; } return; } if (is_logged_in()) { $_SESSION['last_activity'] = time(); } } manage_session_security(); function get_db_connection() { static $pdo = null; if ($pdo === null) { try { $db_dir = dirname(DB_PATH); if (!is_dir($db_dir)) mkdir($db_dir, 0750, true); $pdo = new PDO('sqlite:' . DB_PATH); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); $pdo->exec('PRAGMA journal_mode = WAL;'); } catch (PDOException $e) { die("Database connection failed: " . $e->getMessage()); } } return $pdo; } function is_setup_needed() { return !file_exists(DB_PATH) || filesize(DB_PATH) === 0; } ''' header_php_content = r''' 'Login', 'setup.php' => 'Initial Setup', 'dashboard.php' => 'Account Dashboard', 'user.php' => 'User Management', 'catagory-platform.php' => 'Manage Categories & Platforms', 'change-password.php' => 'Change Password', 'qrcode.php' => 'Scan QR Code', 'recovery.php' => 'Account Recovery' ]; $page_title = ($page_titles[$current_page] ?? 'Dashboard') . ' - Black-Clover'; $username = $_SESSION['username'] ?? '{Username}'; $nav_links = [ 'Scan QR' => ['link' => 'qrcode.php', 'icon' => 'fa-solid fa-qrcode'], 'Catagory-Platform' => ['link' => 'catagory-platform.php', 'icon' => 'fa-solid fa-tags'], 'Change Password' => ['link' => 'change-password.php', 'icon' => 'fa-solid fa-key'], 'Recover' => ['link' => 'recovery.php', 'icon' => 'fa-solid fa-shield-halved'], 'Add User' => ['link' => 'user.php', 'icon' => 'fa-solid fa-user-plus'] ]; ?> <?= htmlspecialchars($page_title) ?>
''' footer_php_content = r''' ''' # --- PHP API Handlers --- cp_handler_php_content = r'''query("SELECT * FROM categories ORDER BY name")->fetchAll(); $platforms = $pdo->query("SELECT * FROM platforms ORDER BY name")->fetchAll(); echo json_encode(['categories' => $categories, 'platforms' => $platforms]); break; case 'POST': $input = json_decode(file_get_contents('php://input'), true); $type = $input['type'] ?? ''; $item = $input['item'] ?? null; $name = isset($item['name']) ? trim($item['name']) : ''; $id = $item['id'] ?? null; if (!($table = get_table_name($type)) || empty($name)) { http_response_code(400); echo json_encode(['error' => 'Invalid input.']); exit; } if ($id) { $stmt = $pdo->prepare("UPDATE {$table} SET name = ? WHERE id = ?"); $stmt->execute([$name, $id]); } else { $stmt = $pdo->prepare("INSERT INTO {$table} (name) VALUES (?)"); $stmt->execute([$name]); } echo json_encode(['success' => true]); break; case 'DELETE': $input = json_decode(file_get_contents('php://input'), true); $type = $input['type'] ?? ''; $id = $input['id'] ?? null; if (!($table = get_table_name($type)) || !$id) { http_response_code(400); echo json_encode(['error' => 'Invalid input.']); exit; } $stmt = $pdo->prepare("DELETE FROM {$table} WHERE id = ?"); $stmt->execute([$id]); echo json_encode(['success' => $stmt->rowCount() > 0]); break; default: http_response_code(405); break; } } catch (PDOException $e) { if ($e->getCode() == 23000) { http_response_code(409); // 409 Conflict echo json_encode(['error' => 'This name already exists. Please choose a different name.']); } else { error_log("CP Handler PDOException: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'A database error occurred.']); } } catch (Exception $e) { error_log("CP Handler Exception: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'An internal server error occurred.']); } ''' setup_handler_php_content = r''' 'Setup has already been completed.']); exit; } if ($_SERVER['REQUEST_METHOD'] !== 'POST') { http_response_code(405); exit; } $input = json_decode(file_get_contents('php://input'), true); $username = $input['username'] ?? ''; $password = $input['password'] ?? ''; if (empty($username) || empty($password) || strlen($password) < 8) { http_response_code(400); echo json_encode(['error' => 'Username and a password of at least 8 characters are required.']); exit; } try { $pdo = get_db_connection(); $pdo->exec("CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, kdf_salt BLOB NOT NULL, user_type TEXT NOT NULL DEFAULT 'admin', recovery_encrypted_mek BLOB, recovery_kdf_salt BLOB)"); $pdo->exec("CREATE TABLE IF NOT EXISTS accounts (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, encrypted_blob TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users (id) ON DELETE CASCADE)"); $pdo->exec("CREATE TABLE IF NOT EXISTS categories (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL)"); $pdo->exec("CREATE TABLE IF NOT EXISTS platforms (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT UNIQUE NOT NULL)"); $password_hash = password_hash($password, PASSWORD_ARGON2ID); $kdf_salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES); $stmt = $pdo->prepare("INSERT INTO users (username, password_hash, kdf_salt, user_type) VALUES (?, ?, ?, 'admin')"); $stmt->execute([$username, $password_hash, $kdf_salt]); $_SESSION['user_id'] = $pdo->lastInsertId(); $_SESSION['username'] = $username; $_SESSION['user_type'] = 'admin'; $_SESSION['last_activity'] = time(); $mek = sodium_crypto_pwhash(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES, $password, $kdf_salt, KDF_OPS_LIMIT, KDF_MEM_LIMIT); $_SESSION['mek'] = $mek; sodium_memzero($password); sodium_memzero($mek); echo json_encode(['success' => true]); } catch (Exception $e) { error_log("Setup Error: " . $e->getMessage()); // Log the actual error if (file_exists(DB_PATH)) unlink(DB_PATH); http_response_code(500); echo json_encode(['error' => 'An error occurred during setup.']); } ''' login_handler_php_content = r''' 'Username and password are required.']); exit; } try { $pdo = get_db_connection(); $stmt = $pdo->prepare("SELECT * FROM users WHERE username = ?"); $stmt->execute([$username]); $user = $stmt->fetch(); if ($user && password_verify($password, $user['password_hash'])) { session_regenerate_id(true); $_SESSION['user_id'] = $user['id']; $_SESSION['username'] = $user['username']; $_SESSION['user_type'] = $user['user_type']; $_SESSION['last_activity'] = time(); $mek = sodium_crypto_pwhash(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES, $password, $user['kdf_salt'], KDF_OPS_LIMIT, KDF_MEM_LIMIT); $_SESSION['mek'] = $mek; sodium_memzero($password); sodium_memzero($mek); echo json_encode(['success' => true]); } else { usleep(500000); http_response_code(401); echo json_encode(['error' => 'Wrong username or password.']); } } catch (Exception $e) { error_log("Login Error: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'An internal server error occurred.']); } ''' logout_handler_php_content = r''' 'Authentication required.']); exit; } $pdo = get_db_connection(); $user_id = $_SESSION['user_id']; $mek = $_SESSION['mek']; function encrypt_data($plaintext, $mek) { $nonce = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES); $ciphertext = sodium_crypto_aead_aes256gcm_encrypt(json_encode($plaintext), '', $nonce, $mek); return json_encode(['nonce' => base64_encode($nonce), 'ciphertext' => base64_encode($ciphertext)]); } function decrypt_data($encrypted_blob, $mek) { $data = json_decode($encrypted_blob, true); if (!isset($data['nonce'], $data['ciphertext'])) return null; $nonce = base64_decode($data['nonce']); $ciphertext = base64_decode($data['ciphertext']); $decrypted = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, '', $nonce, $mek); return $decrypted ? json_decode($decrypted, true) : null; } switch ($_SERVER['REQUEST_METHOD']) { case 'GET': $stmt = $pdo->prepare("SELECT * FROM accounts WHERE user_id = ?"); $stmt->execute([$user_id]); $encrypted_accounts = $stmt->fetchAll(); $decrypted_accounts = []; foreach ($encrypted_accounts as $enc_acc) { if ($decrypted_data = decrypt_data($enc_acc['encrypted_blob'], $mek)) { $decrypted_data['id'] = $enc_acc['id']; $decrypted_accounts[] = $decrypted_data; } } $categories = $pdo->query("SELECT * FROM categories ORDER BY name")->fetchAll(); $platforms = $pdo->query("SELECT * FROM platforms ORDER BY name")->fetchAll(); echo json_encode(['accounts' => $decrypted_accounts, 'categories' => $categories, 'platforms' => $platforms]); break; case 'POST': $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? null; unset($input['id']); $encrypted_blob = encrypt_data($input, $mek); if ($id) { $stmt = $pdo->prepare("UPDATE accounts SET encrypted_blob = ? WHERE id = ? AND user_id = ?"); $stmt->execute([$encrypted_blob, $id, $user_id]); } else { $stmt = $pdo->prepare("INSERT INTO accounts (user_id, encrypted_blob) VALUES (?, ?)"); $stmt->execute([$user_id, $encrypted_blob]); $id = $pdo->lastInsertId(); } echo json_encode(['success' => true, 'id' => $id]); break; case 'DELETE': $input = json_decode(file_get_contents('php://input'), true); if (!($id = $input['id'] ?? null)) { http_response_code(400); exit; } $stmt = $pdo->prepare("DELETE FROM accounts WHERE id = ? AND user_id = ?"); $stmt->execute([$id, $user_id]); echo json_encode(['success' => $stmt->rowCount() > 0]); break; default: http_response_code(405); break; } sodium_memzero($mek); ''' user_handler_php_content = r'''query("SELECT id, username, user_type FROM users")->fetchAll()); break; case 'POST': $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? null; $username = trim($input['username'] ?? ''); $password = $input['password'] ?? ''; $user_type = $input['user_type'] ?? 'user'; if (empty($username) || !in_array($user_type, ['user', 'admin'])) { http_response_code(400); exit; } if ($id) { if (!empty($password)) { $stmt = $pdo->prepare("UPDATE users SET username = ?, password_hash = ?, user_type = ? WHERE id = ?"); $stmt->execute([$username, password_hash($password, PASSWORD_ARGON2ID), $user_type, $id]); } else { $stmt = $pdo->prepare("UPDATE users SET username = ?, user_type = ? WHERE id = ?"); $stmt->execute([$username, $user_type, $id]); } } else { if (empty($password) || strlen($password) < 8) { http_response_code(400); exit; } $kdf_salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES); $stmt = $pdo->prepare("INSERT INTO users (username, password_hash, kdf_salt, user_type) VALUES (?, ?, ?, ?)"); $stmt->execute([$username, password_hash($password, PASSWORD_ARGON2ID), $kdf_salt, $user_type]); } echo json_encode(['success' => true]); break; case 'DELETE': $input = json_decode(file_get_contents('php://input'), true); if (!($id = $input['id'] ?? null) || $id == $_SESSION['user_id']) { http_response_code(400); exit; } $stmt = $pdo->prepare("DELETE FROM users WHERE id = ?"); $stmt->execute([$id]); echo json_encode(['success' => $stmt->rowCount() > 0]); break; default: http_response_code(405); break; } } catch (Exception $e) { error_log("User Handler Error: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'An internal server error occurred.']); } ''' data_handler_php_content = r''' 'Authentication required.']); exit; } $pdo = get_db_connection(); $user_id = $_SESSION['user_id']; $mek = $_SESSION['mek']; // ... (encryption/decryption functions remain the same) ... function encrypt_data($plaintext, $mek) { $nonce = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES); $ciphertext = sodium_crypto_aead_aes256gcm_encrypt(json_encode($plaintext), '', $nonce, $mek); return json_encode(['nonce' => base64_encode($nonce), 'ciphertext' => base64_encode($ciphertext)]); } function decrypt_data($encrypted_blob, $mek) { try { $data = json_decode($encrypted_blob, true, 512, JSON_THROW_ON_ERROR); if (!isset($data['nonce'], $data['ciphertext'])) return null; $nonce = base64_decode($data['nonce'], true); $ciphertext = base64_decode($data['ciphertext'], true); if ($nonce === false || $ciphertext === false) return null; $decrypted = sodium_crypto_aead_aes256gcm_decrypt($ciphertext, '', $nonce, $mek); return $decrypted ? json_decode($decrypted, true, 512, JSON_THROW_ON_ERROR) : null; } catch (Exception $e) { error_log("Decryption failed for user_id {$user_id}: " . $e->getMessage()); return null; // Return null on any failure } } try { switch ($_SERVER['REQUEST_METHOD']) { case 'GET': $stmt = $pdo->prepare("SELECT * FROM accounts WHERE user_id = ?"); $stmt->execute([$user_id]); $encrypted_accounts = $stmt->fetchAll(); $decrypted_accounts = []; foreach ($encrypted_accounts as $enc_acc) { if ($decrypted_data = decrypt_data($enc_acc['encrypted_blob'], $mek)) { $decrypted_data['id'] = $enc_acc['id']; $decrypted_accounts[] = $decrypted_data; } } $categories = $pdo->query("SELECT * FROM categories ORDER BY name")->fetchAll(); $platforms = $pdo->query("SELECT * FROM platforms ORDER BY name")->fetchAll(); echo json_encode(['accounts' => $decrypted_accounts, 'categories' => $categories, 'platforms' => $platforms]); break; case 'POST': $input = json_decode(file_get_contents('php://input'), true); $id = $input['id'] ?? null; unset($input['id']); $encrypted_blob = encrypt_data($input, $mek); if ($id) { $stmt = $pdo->prepare("UPDATE accounts SET encrypted_blob = ? WHERE id = ? AND user_id = ?"); $stmt->execute([$encrypted_blob, $id, $user_id]); } else { $stmt = $pdo->prepare("INSERT INTO accounts (user_id, encrypted_blob) VALUES (?, ?)"); $stmt->execute([$user_id, $encrypted_blob]); $id = $pdo->lastInsertId(); } echo json_encode(['success' => true, 'id' => $id]); break; case 'DELETE': $input = json_decode(file_get_contents('php://input'), true); if (!($id = $input['id'] ?? null)) { http_response_code(400); exit; } $stmt = $pdo->prepare("DELETE FROM accounts WHERE id = ? AND user_id = ?"); $stmt->execute([$id, $user_id]); echo json_encode(['success' => $stmt->rowCount() > 0]); break; default: http_response_code(405); break; } } catch(Exception $e) { error_log("Data handler error: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'An internal server error occurred.']); } finally { if (isset($mek)) { sodium_memzero($mek); } } ''' password_change_handler_php_content = r''' 'All fields required; new password must be 8+ characters.']); exit; } try { $pdo = get_db_connection(); $stmt = $pdo->prepare("SELECT password_hash, kdf_salt FROM users WHERE id = ?"); $stmt->execute([$_SESSION['user_id']]); $user = $stmt->fetch(); if (!$user || !password_verify($oldPassword, $user['password_hash'])) { http_response_code(401); echo json_encode(['error' => 'Incorrect old password.']); exit; } $stmt = $pdo->prepare("UPDATE users SET password_hash = ? WHERE id = ?"); $stmt->execute([password_hash($newPassword, PASSWORD_ARGON2ID), $_SESSION['user_id']]); $_SESSION['mek'] = sodium_crypto_pwhash(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES, $newPassword, $user['kdf_salt'], KDF_OPS_LIMIT, KDF_MEM_LIMIT); sodium_memzero($oldPassword); sodium_memzero($newPassword); echo json_encode(['success' => true]); } catch (Exception $e) { error_log("Password Change Error: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'An internal server error occurred.']); } ''' recovery_handler_php_content = r'''prepare("SELECT password_hash FROM users WHERE id = ?"); $stmt->execute([$user_id]); $user = $stmt->fetch(); if (!$user || !password_verify($currentPassword, $user['password_hash'])) { http_response_code(401); exit; } $recovery_code_formatted = implode('-', str_split(strtoupper(bin2hex(random_bytes(6))), 4)); $recovery_kdf_salt = random_bytes(SODIUM_CRYPTO_PWHASH_SALTBYTES); $recovery_encryption_key = sodium_crypto_pwhash(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES, $recovery_code_formatted, $recovery_kdf_salt, KDF_OPS_LIMIT, KDF_MEM_LIMIT); $nonce = random_bytes(SODIUM_CRYPTO_AEAD_AES256GCM_NPUBBYTES); $encrypted_mek = sodium_crypto_aead_aes256gcm_encrypt($_SESSION['mek'], '', $nonce, $recovery_encryption_key); $recovery_blob = json_encode(['nonce' => base64_encode($nonce), 'ciphertext' => base64_encode($encrypted_mek)]); $updateStmt = $pdo->prepare("UPDATE users SET recovery_encrypted_mek = ?, recovery_kdf_salt = ? WHERE id = ?"); $updateStmt->execute([$recovery_blob, $recovery_kdf_salt, $user_id]); echo json_encode(['success' => true, 'recoveryCode' => $recovery_code_formatted]); exit; } if ($action === 'verify_code') { $username = $input['username'] ?? ''; $recoveryCode = $input['recoveryCode'] ?? ''; if (empty($username) || empty($recoveryCode)) { http_response_code(400); exit; } $stmt = $pdo->prepare("SELECT id, recovery_encrypted_mek, recovery_kdf_salt FROM users WHERE username = ?"); $stmt->execute([$username]); $user = $stmt->fetch(); if (!$user || empty($user['recovery_encrypted_mek'])) { http_response_code(401); exit; } $key = sodium_crypto_pwhash(SODIUM_CRYPTO_AEAD_AES256GCM_KEYBYTES, $recoveryCode, $user['recovery_kdf_salt'], KDF_OPS_LIMIT, KDF_MEM_LIMIT); $blob = json_decode($user['recovery_encrypted_mek'], true); if (sodium_crypto_aead_aes256gcm_decrypt(base64_decode($blob['ciphertext']), '', base64_decode($blob['nonce']), $key) === false) { http_response_code(401); exit; } $reset_token = bin2hex(random_bytes(32)); $_SESSION['reset_token'] = $reset_token; $_SESSION['reset_user_id'] = $user['id']; $_SESSION['reset_expires'] = time() + 300; echo json_encode(['success' => true, 'token' => $reset_token]); exit; } if ($action === 'reset_password') { $token = $input['token'] ?? ''; $newPassword = $input['newPassword'] ?? ''; if (empty($token) || empty($_SESSION['reset_token']) || !hash_equals($_SESSION['reset_token'], $token) || time() > $_SESSION['reset_expires']) { http_response_code(401); exit; } if (strlen($newPassword) < 8) { http_response_code(400); exit; } $stmt = $pdo->prepare("UPDATE users SET password_hash = ?, recovery_encrypted_mek = NULL, recovery_kdf_salt = NULL WHERE id = ?"); $stmt->execute([password_hash($newPassword, PASSWORD_ARGON2ID), $_SESSION['reset_user_id']]); unset($_SESSION['reset_token'], $_SESSION['reset_user_id'], $_SESSION['reset_expires']); echo json_encode(['success' => true]); exit; } http_response_code(400); } catch (Exception $e) { error_log("Recovery Handler Error: " . $e->getMessage()); http_response_code(500); echo json_encode(['error' => 'An internal server error occurred.']); } ''' # --- PHP Page Files --- index_php_content = r'''

Black-Clover

Initial Setup

{{ message.text }}
''' login_php_content = r'''

Black-Clover

Admin Login

{{ errorMessage }}
{{ successMessage }}
''' dashboard_php_content = r'''

Account Credentials

Total Accounts: {{ accounts.length }} | Search Results: {{ fullFilteredAccounts.length }}
CategoryPlatformMailDescriptionUsernamePassword2FBackupRec. MailRec. PhoneTOTPActions
{{ a.category }}{{ a.platform }}{{ a.mail }}{{ a.description }}{{ a.username }}
**********{{ a.password }}
{{ a.two_factor_status.toUpperCase() }} {{ a.backup_code }}{{ a.rec_mail }}{{ a.recovery_phone }}
{{ a.remainingTime }}
{{ a.currentOtp }}
N/A

Scan TOTP QR Code

{{ isEditMode ? 'Edit Account' : 'Add Account' }}

Are you sure?

{{ confirmMessage }}

''' user_php_content = r'''

User Management

UsernameUser TypeActions
{{ user.username }} {{ user.user_type }}

{{ modalTitle }}

Are you sure?

{{ confirmMessage }}

''' catagory_platform_php_content = r'''

Manage Categories & Platforms

Categories

NameActions
{{ c.name }}

Platforms

NameActions
{{ p.name }}

{{ formTitle }}

Are you sure?

{{ confirmMessage }}

''' change_password_php_content = r'''

Change Your Password

{{ message.text }}
''' qrcode_php_content = r'''

Scan QR Code

''' # --- Root index.php for redirection --- root_index_php_content = r'''

Account Recovery

OR
{{ message.text }}

Recovery code accepted. Set a new password.

{{ message.text }}

Verify Identity

Enter current password to generate a new recovery code.

{{ passwordError }}

New Recovery Code

Store this in a safe place.

If you lose this and your password, data is lost forever.

{{ generatedCode }}
''' # ============================================================================== # --- MAIN EXECUTION --- # ============================================================================== def main(): """Main function to create the project structure.""" print("Starting Black-Clover project generation...") os.makedirs(PROJECT_NAME, exist_ok=True) create_file(os.path.join(PROJECT_NAME, "config/config.php"), config_php_content) public_dirs = ["public/api", "public/includes"] for d in public_dirs: os.makedirs(os.path.join(PROJECT_NAME, d), exist_ok=True) create_file(os.path.join(PROJECT_NAME, "public/index.php"), index_php_content) create_file(os.path.join(PROJECT_NAME, "public/setup.php"), setup_php_content) create_file(os.path.join(PROJECT_NAME, "public/login.php"), login_php_content) create_file(os.path.join(PROJECT_NAME, "public/dashboard.php"), dashboard_php_content) create_file(os.path.join(PROJECT_NAME, "public/user.php"), user_php_content) create_file(os.path.join(PROJECT_NAME, "public/catagory-platform.php"), catagory_platform_php_content) create_file(os.path.join(PROJECT_NAME, "public/change-password.php"), change_password_php_content) create_file(os.path.join(PROJECT_NAME, "public/qrcode.php"), qrcode_php_content) create_file(os.path.join(PROJECT_NAME, "public/recovery.php"), recovery_php_content) create_file(os.path.join(PROJECT_NAME, "public/includes/bootstrap.php"), bootstrap_php_content) create_file(os.path.join(PROJECT_NAME, "public/includes/header.php"), header_php_content) create_file(os.path.join(PROJECT_NAME, "public/includes/footer.php"), footer_php_content) create_file(os.path.join(PROJECT_NAME, "index.php"), root_index_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/setup_handler.php"), setup_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/login_handler.php"), login_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/logout_handler.php"), logout_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/data_handler.php"), data_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/user_handler.php"), user_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/cp_handler.php"), cp_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/password_change_handler.php"), password_change_handler_php_content) create_file(os.path.join(PROJECT_NAME, "public/api/recovery_handler.php"), recovery_handler_php_content) db_dir = os.path.join(PROJECT_NAME, "database") os.makedirs(db_dir, exist_ok=True) try: os.chmod(db_dir, stat.S_IRWXU | stat.S_IRWXG | stat.S_IROTH | stat.S_IXOTH) print(f"Set permissions for directory: {db_dir}") except Exception as e: print(f"Warning: Could not set permissions for {db_dir}. Please ensure your web server can write to this directory. Error: {e}") print("\nProject generation complete!") print(f"Project created in: ./{PROJECT_NAME}/") print("\nNext Steps:") print(f"1. Delete any old '{PROJECT_NAME}' directory and its database.") print(f"2. Point your local web server to the new '{PROJECT_NAME}/public' directory.") print("3. Ensure local SSL (HTTPS) is enabled for your site.") print("4. Open your site in a browser to begin the fresh setup.") if __name__ == "__main__": main()