Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion DOCKER_COMPOSE_SETUP.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,12 @@ This setup uses `serversideup/php:8.4-fpm-nginx` as the base image and is design
docker compose exec loops php artisan passport:keys
```

8. **Create admin user:**
8. **Ensure boot-time environment:**
```bash
docker compose exec loops php artisan app:ensure-boottime
```

9. **Create admin user:**
```bash
docker compose exec loops php artisan create-admin-account
```
Expand Down
2 changes: 2 additions & 0 deletions app/Console/Commands/ComposerPostInstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Console\Commands;

use App\Services\BootstrapService;
use App\Services\SettingsFileService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Cache;
Expand Down Expand Up @@ -33,6 +34,7 @@ public function handle()

Cache::forget('version_check_result');
app(SettingsFileService::class)->flush();
BootstrapService::ensureBoottimeEnvironment();

$this->info('Post-install tasks completed successfully.');
} catch (\Exception $e) {
Expand Down
40 changes: 40 additions & 0 deletions app/Console/Commands/EnsureBoottimeEnvCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace App\Console\Commands;

use App\Services\BootstrapService;
use Illuminate\Console\Command;

class EnsureBoottimeEnvCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:ensure-boottime';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Ensure boot-time environment checks pass (directories, permissions, etc.)';

/**
* Execute the console command.
*/
public function handle(): int
{
try {
BootstrapService::ensureBoottimeEnvironment();
$this->info('All boot-time environment checks passed.');

return Command::SUCCESS;
} catch (\RuntimeException $e) {
$this->error($e->getMessage());

return Command::FAILURE;
}
}
}
89 changes: 89 additions & 0 deletions app/Services/BootstrapService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

namespace App\Services;

use Illuminate\Support\Facades\File;
use RuntimeException;

class BootstrapService
{
public static function ensureBoottimeEnvironment(): void
{
self::checkOAuthKeyPermissions();
self::ensureAvatarTempDirectory();
}

protected static function ensureAvatarTempDirectory(): void
{
$path = storage_path('app/avatar-temp');

if (! File::isDirectory($path)) {
File::makeDirectory($path, 0755, true);

return;
}

$perms = fileperms($path) & 0777;

if ($perms === 0755) {
return;
}

if (@chmod($path, 0755)) {
return;
}

throw new RuntimeException(
"Avatar temp directory \"{$path}\" has incorrect permissions (".self::formatPerms($perms).'). '.
"Expected 0755. Please run: chmod 755 {$path}"
);
}

protected static function checkOAuthKeyPermissions(): void
{
$privateKeyPath = storage_path('oauth-private.key');
$publicKeyPath = storage_path('oauth-public.key');

self::checkOAuthFile($privateKeyPath);
self::checkOAuthFile($publicKeyPath);
}

protected static function checkOAuthFile(string $filePath): void
{
if (app()->environment('production') && ! file_exists($filePath)) {
throw new RuntimeException(
"OAuth key file {$filePath} is missing. Please generate OAuth keys."
);
}

$permissions = self::getPermissions($filePath);

$isSafe = ($permissions === '600' || $permissions === '660');

if ($isSafe) {
return;
}

$fixed = @chmod($filePath, 0660);

if ($fixed) {
return;
}

throw new RuntimeException(
"File {$filePath} has bad permissions ({$permissions}). "."Should be 600 or 660. Run this command: chmod 660 {$filePath}"
);
}

protected static function getPermissions(string $filePath): string
{
$permissionNumber = fileperms($filePath) & 0777;

return decoct($permissionNumber);
}

protected static function formatPerms(int $perms): string
{
return sprintf('%04o', $perms);
}
}