feat: add installer, ranks/groups enhancements, and founder protections

This commit is contained in:
2026-01-23 19:26:35 +01:00
parent 24c16ed0dd
commit d4fb86633b
43 changed files with 6176 additions and 4039 deletions

View File

@@ -0,0 +1,140 @@
<?php
namespace App\Http\Controllers;
use App\Models\Role;
use App\Models\User;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use Illuminate\View\View;
class InstallerController extends Controller
{
public function show(Request $request): View|RedirectResponse
{
if ($this->envExists()) {
return redirect('/');
}
return view('installer', [
'appUrl' => $request->getSchemeAndHttpHost(),
]);
}
public function store(Request $request): View|RedirectResponse
{
if ($this->envExists()) {
return redirect('/');
}
$data = $request->validate([
'app_url' => ['required', 'url'],
'db_host' => ['required', 'string', 'max:255'],
'db_port' => ['nullable', 'integer'],
'db_database' => ['required', 'string', 'max:255'],
'db_username' => ['required', 'string', 'max:255'],
'db_password' => ['nullable', 'string'],
'admin_name' => ['required', 'string', 'max:255'],
'admin_email' => ['required', 'email', 'max:255'],
'admin_password' => ['required', 'string', 'min:8'],
]);
$appKey = 'base64:' . base64_encode(random_bytes(32));
$envLines = [
'APP_NAME="speedBB"',
'APP_ENV=production',
'APP_DEBUG=false',
'APP_URL=' . $data['app_url'],
'APP_KEY=' . $appKey,
'',
'DB_CONNECTION=mysql',
'DB_HOST=' . $data['db_host'],
'DB_PORT=' . ($data['db_port'] ?: 3306),
'DB_DATABASE=' . $data['db_database'],
'DB_USERNAME=' . $data['db_username'],
'DB_PASSWORD=' . ($data['db_password'] ?? ''),
'',
'MAIL_MAILER=sendmail',
'MAIL_SENDMAIL_PATH="/usr/sbin/sendmail -bs -i"',
'MAIL_FROM_ADDRESS="hello@example.com"',
'MAIL_FROM_NAME="speedBB"',
];
$this->writeEnv(implode("\n", $envLines) . "\n");
config([
'app.key' => $appKey,
'app.url' => $data['app_url'],
'database.default' => 'mysql',
'database.connections.mysql.host' => $data['db_host'],
'database.connections.mysql.port' => (int) ($data['db_port'] ?: 3306),
'database.connections.mysql.database' => $data['db_database'],
'database.connections.mysql.username' => $data['db_username'],
'database.connections.mysql.password' => $data['db_password'] ?? '',
'mail.default' => 'sendmail',
'mail.mailers.sendmail.path' => '/usr/sbin/sendmail -bs -i',
]);
DB::purge('mysql');
try {
DB::connection('mysql')->getPdo();
} catch (\Throwable $e) {
$this->removeEnv();
return view('installer', [
'appUrl' => $data['app_url'],
'error' => 'Database connection failed: ' . $e->getMessage(),
'old' => $data,
]);
}
$migrateExit = Artisan::call('migrate', ['--force' => true]);
if ($migrateExit !== 0) {
$this->removeEnv();
return view('installer', [
'appUrl' => $data['app_url'],
'error' => 'Migration failed. Please check your database credentials.',
'old' => $data,
]);
}
$adminRole = Role::firstOrCreate(['name' => 'ROLE_ADMIN']);
$founderRole = Role::firstOrCreate(['name' => 'ROLE_FOUNDER']);
$user = User::create([
'name' => $data['admin_name'],
'name_canonical' => Str::lower(trim($data['admin_name'])),
'email' => $data['admin_email'],
'password' => Hash::make($data['admin_password']),
'email_verified_at' => now(),
]);
$user->roles()->sync([$adminRole->id, $founderRole->id]);
return view('installer-success');
}
private function envExists(): bool
{
return file_exists(base_path('.env'));
}
private function writeEnv(string $contents): void
{
$path = base_path('.env');
file_put_contents($path, $contents);
}
private function removeEnv(): void
{
$path = base_path('.env');
if (file_exists($path)) {
unlink($path);
}
}
}