$name, 'password_hash' => password_hash($password, PASSWORD_DEFAULT), 'role' => $role, 'groups' => $groups, ]; file_put_contents(USERS_FILE, json_encode($users, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"); $success[] = "User " . htmlspecialchars($username) . " saved."; } } elseif ($action === 'change_password') { $username = trim($_POST['username'] ?? ''); $password = $_POST['password'] ?? ''; $confirm = $_POST['confirm'] ?? ''; if (!isset($users[$username])) $errors[] = 'User not found.'; if (strlen($password) < 8) $errors[] = 'Password must be at least 8 characters.'; if ($password !== $confirm) $errors[] = 'Passwords do not match.'; if (!$errors) { $users[$username]['password_hash'] = password_hash($password, PASSWORD_DEFAULT); file_put_contents(USERS_FILE, json_encode($users, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"); $success[] = "Password updated for " . htmlspecialchars($username) . "."; } } elseif ($action === 'delete_user') { $username = trim($_POST['username'] ?? ''); $users_left = array_filter($users, fn($u) => $u['role'] === 'admin'); if (count($users_left) <= 1 && isset($users[$username]) && $users[$username]['role'] === 'admin') { $errors[] = 'Cannot delete the last admin account.'; } elseif (!isset($users[$username])) { $errors[] = 'User not found.'; } else { unset($users[$username]); file_put_contents(USERS_FILE, json_encode($users, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"); $success[] = "User " . htmlspecialchars($username) . " deleted."; } } elseif ($action === 'lock') { file_put_contents(LOCK_FILE, date('Y-m-d H:i:s')); $locked = true; } } } // ── Load current users for display ─────────────────────────────────────────── $users = json_decode(@file_get_contents(USERS_FILE), true) ?: []; $has_real_admin = false; foreach ($users as $u) { if ($u['role'] === 'admin' && !str_starts_with($u['password_hash'], '$2y$12$placeholder')) { $has_real_admin = true; break; } } $key_set = file_exists(LOCK_FILE . '.key'); // ── HTML ────────────────────────────────────────────────────────────────────── ?> GitGram Setup
GitGram — Account Setup
Account Setup

🔒 Setup is locked

Account setup has been completed and locked.

For security, delete this file from your server:

rm setup.php

To make further changes, delete data/setup.lock and reload this page.

⚠ No admin account has a real password yet. Set one before using GitGram.
🔑 First run: Choose a setup key below. You will need it every time you use this page. It is not a git login — it just protects this setup tool.
Current Users

No users yet.

$u): ?>
UsernameDisplay NameRoleGroups
Add / Update User
Change Password
Delete User
Lock Setup

Lowercase, 2–32 chars. Used for git push login.

Comma-separated. Used in repos.json access rules (@devs, etc.)

Updating an existing username will overwrite it.
🔒 Locking prevents anyone from accessing this setup page until you delete data/setup.lock. Do this when you are done configuring accounts.
⚠ You have not set a real admin password yet. Lock anyway?

After locking, delete setup.php from your server for maximum security. The data/setup.lock.key file also contains your setup key in plaintext — delete it too once you no longer need this tool.

GitGram Setup