From 4530298398a78f1626a5474b55bbf1fe446ac8a1 Mon Sep 17 00:00:00 2001 From: surtic86 Date: Fri, 18 Sep 2026 23:14:33 +0200 Subject: [PATCH] Cut over-engineering found by a repo-wide audit - Config: auth, services, logging, queue and database only repeated the framework's own files and are gone; the others keep only the keys that differ (app version, cache serializable_classes, session cookie name, Markdown mail theme, the shares disk, three Octane values, Livewire's pagination theme and payload guards). - Email verification is removed: User never implemented MustVerifyEmail, so it was never enforced, and SealShare has a single admin and no registration. CreateNewUser goes with it. - FileEncryptionService::encryptFile() and generateSalt() were only used by tests; tests build files with encryptTestFile() in tests/Pest.php. - The expiration options are defined once, as Share::EXPIRATIONS. "30 Days" now lasts 30 days instead of a calendar month, and Admin settings only save a default expiration that is one of the options. - One-caller helpers are inlined, the uploader reads chunk responses with XHR's responseType, and starter-kit leftovers are removed. - Docker: PHP reads the PHP_* limits from the environment itself (${VAR:-default} in uploads.ini); both entrypoints stop writing the ini. docker-compose.yml shares the app and scheduler variables through one anchor. The dev image installs gd for the screenshot publisher and fake test images. - Development runs in Docker only: the composer dev script, concurrently, laravel/pail, laravel/sail, autoprefixer and the shell-quote override are gone. - phpunit.xml forces the test environment with entries, so tests run in the dev container no longer use its real database. Co-Authored-By: Claude Opus 5 (1M context) --- CHANGELOG.md | 8 + Dockerfile | 11 +- app/Actions/Fortify/CreateNewUser.php | 33 -- app/Concerns/ProfileValidationRules.php | 8 +- app/Http/Controllers/DownloadController.php | 5 +- app/Livewire/Admin/AdminSettings.php | 14 +- app/Livewire/FileUploader.php | 16 +- app/Livewire/SetupWizard.php | 4 - app/Livewire/ShareDownload.php | 8 +- app/Models/Share.php | 15 + app/Models/User.php | 14 - app/Providers/AppServiceProvider.php | 8 - app/Providers/FortifyServiceProvider.php | 11 - app/Services/FileEncryptionService.php | 62 --- bootstrap/app.php | 6 +- composer.json | 6 - composer.lock | 147 +------ config/app.php | 129 +----- config/auth.php | 117 ----- config/cache.php | 132 +----- config/database.php | 184 -------- config/filesystems.php | 81 +--- config/fortify.php | 2 - config/livewire.php | 268 +----------- config/logging.php | 132 ------ config/mail.php | 138 +----- config/octane.php | 217 +--------- config/queue.php | 129 ------ config/services.php | 38 -- config/session.php | 216 +-------- database/factories/UserFactory.php | 10 - database/seeders/DatabaseSeeder.php | 3 - docker-compose.yml | 42 +- docker/dev-entrypoint.sh | 10 - docker/entrypoint.sh | 10 - docker/php/uploads.ini | 15 +- package-lock.json | 409 ------------------ package.json | 5 - phpunit.xml | 26 +- resources/js/share-uploader.js | 11 +- .../views/components/app-logo-icon.blade.php | 7 - .../livewire/admin/admin-settings.blade.php | 9 +- .../views/livewire/file-uploader.blade.php | 9 +- .../views/pages/auth/verify-email.blade.php | 34 -- .../views/pages/settings/⚡profile.blade.php | 66 +-- routes/console.php | 6 - routes/settings.php | 3 - tests/Browser/BreakpointsTest.php | 6 - tests/Feature/Admin/AdminSettingsTest.php | 17 + tests/Feature/Auth/EmailVerificationTest.php | 67 --- tests/Feature/FileUploadTest.php | 15 + tests/Feature/PageTemplateTest.php | 1 - tests/Feature/Settings/ProfileUpdateTest.php | 5 +- tests/Feature/ShareDownloadTest.php | 3 +- tests/Pest.php | 39 +- tests/Unit/FileEncryptionServiceTest.php | 35 +- website/img/logo.svg | 2 +- 57 files changed, 240 insertions(+), 2784 deletions(-) delete mode 100644 app/Actions/Fortify/CreateNewUser.php delete mode 100644 config/auth.php delete mode 100644 config/database.php delete mode 100644 config/logging.php delete mode 100644 config/queue.php delete mode 100644 config/services.php delete mode 100644 resources/views/components/app-logo-icon.blade.php delete mode 100644 resources/views/pages/auth/verify-email.blade.php delete mode 100644 tests/Feature/Auth/EmailVerificationTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0a44b..c17ea2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Docker installs updated from 2.0 answered every upload with "409 Conflict". The example `docker-compose.yml` mounted the SQLite volume over all of `/app/database`, which hid the image's new migration, so it never ran. The container now adds the migrations the volume is missing before migrating, so existing compose files keep working. - A share with several files and a download limit was deleted as soon as one file was downloaded: every single file counted as a whole download. Now one recipient's visit counts once, and they have 1 hour to download all the files and the ZIP. Two recipients who start at the same moment can no longer both get the last download. - The scheduler container no longer shows as "unhealthy": it inherited the image's healthcheck, which asks the web server that only the app container runs. To fix an existing install, add `healthcheck: { disable: true }` to the scheduler service in your `docker-compose.yml`. +- The "30 Days" expiration lasted a calendar month; it now lasts 30 days. +- Admin settings only save a default expiration that is one of the offered options. ### Changed @@ -24,6 +26,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - The download page of a share with a download limit says how many downloads are left, or how long the recipient can still download. The admin dashboard shows downloads as "2 of 3 downloads", marks shares at their limit as "Download limit reached" and no longer counts them as active. - The sort dropdown on the admin dashboard spans the full width of the shares card. - Development: `docker-compose.dev.yml` now extends `docker-compose.yml`, so the dev stack runs the scheduler and the production image's PHP extensions, and takes its settings from `.env` (which selects the file through `COMPOSE_FILE`). A Vite dev server with hot reload runs beside the app. No ports are published unless `docker-compose.ports.yml` is added; with OrbStack the app is at `https://app.sealshare.orb.local`. `docker/dev.Dockerfile` became the `dev` stage of the `Dockerfile`. +- The Docker image's PHP limits (`PHP_UPLOAD_MAX_FILESIZE`, `PHP_POST_MAX_SIZE`, `PHP_MAX_EXECUTION_TIME`, `PHP_MAX_INPUT_TIME`, `PHP_MEMORY_LIMIT`) are read by PHP itself from the environment; the entrypoint no longer writes an ini file on start. The variables and their defaults are unchanged. + +### Removed + +- Email verification (`/email/verify`), which was never enforced: SealShare has a single admin account and no registration. +- The `composer dev` script: development runs in Docker (`docker-compose.dev.yml`). ## [2.1.0] - 2026-09-16 diff --git a/Dockerfile b/Dockerfile index 16a5d95..a1d9924 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,6 +48,9 @@ RUN install-php-extensions \ pcntl \ zip +# PHP limits, read from PHP_* environment variables by PHP itself +COPY docker/php/uploads.ini /usr/local/etc/php/conf.d/99-uploads.ini + WORKDIR /app # ============================================ @@ -56,8 +59,9 @@ WORKDIR /app # Holds only the tools: the checkout is mounted at /app, and its entrypoint runs from there. FROM base AS dev -# For the dev packages: Pest's browser plugin needs sockets -RUN install-php-extensions sockets +# For the dev packages: Pest's browser plugin needs sockets; the screenshot publisher and fake test +# images need gd (the app itself never processes images, so production goes without) +RUN install-php-extensions sockets gd # Node.js for the Vite dev server RUN apk add --no-cache nodejs npm @@ -93,9 +97,6 @@ ENV APP_NAME="SealShare" \ BCRYPT_ROUNDS="12" \ OCTANE_SERVER="frankenphp" -# Copy PHP ini for upload limits -COPY docker/php/uploads.ini /usr/local/etc/php/conf.d/99-uploads.ini - # Copy application code COPY . . diff --git a/app/Actions/Fortify/CreateNewUser.php b/app/Actions/Fortify/CreateNewUser.php deleted file mode 100644 index 3c7c00c..0000000 --- a/app/Actions/Fortify/CreateNewUser.php +++ /dev/null @@ -1,33 +0,0 @@ - $input - */ - public function create(array $input): User - { - Validator::make($input, [ - ...$this->profileRules(), - 'password' => $this->passwordRules(), - ])->validate(); - - return User::create([ - 'name' => $input['name'], - 'email' => $input['email'], - 'password' => $input['password'], - ]); - } -} diff --git a/app/Concerns/ProfileValidationRules.php b/app/Concerns/ProfileValidationRules.php index 46e19ba..42d28fc 100644 --- a/app/Concerns/ProfileValidationRules.php +++ b/app/Concerns/ProfileValidationRules.php @@ -12,7 +12,7 @@ trait ProfileValidationRules * * @return array|string>> */ - protected function profileRules(?int $userId = null): array + protected function profileRules(int $userId): array { return [ 'name' => $this->nameRules(), @@ -35,16 +35,14 @@ trait ProfileValidationRules * * @return array|string> */ - protected function emailRules(?int $userId = null): array + protected function emailRules(int $userId): array { return [ 'required', 'string', 'email', 'max:255', - $userId === null - ? Rule::unique(User::class) - : Rule::unique(User::class)->ignore($userId), + Rule::unique(User::class)->ignore($userId), ]; } } diff --git a/app/Http/Controllers/DownloadController.php b/app/Http/Controllers/DownloadController.php index 1be3f23..0c6cf39 100644 --- a/app/Http/Controllers/DownloadController.php +++ b/app/Http/Controllers/DownloadController.php @@ -101,7 +101,10 @@ class DownloadController extends Controller } return new StreamedResponse(function () use ($encryptedPath, $key): void { - $this->encryptionService->streamDecryptedFile($encryptedPath, $key); + foreach ($this->encryptionService->decryptedChunks($encryptedPath, $key) as $chunk) { + echo $chunk; + flush(); + } }, 200, $headers); } diff --git a/app/Livewire/Admin/AdminSettings.php b/app/Livewire/Admin/AdminSettings.php index 674a8d1..f412331 100644 --- a/app/Livewire/Admin/AdminSettings.php +++ b/app/Livewire/Admin/AdminSettings.php @@ -3,6 +3,7 @@ namespace App\Livewire\Admin; use App\Models\Setting; +use App\Models\Share; use App\Services\PasswordGeneratorService; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Storage; @@ -92,6 +93,7 @@ class AdminSettings extends Component { $validated = $this->validate([ 'colorProfile' => ['required', 'string', Rule::in(array_keys(Scheme::profiles()))], + 'defaultExpiration' => ['nullable', 'string', Rule::in(array_keys(Share::EXPIRATIONS))], 'maxFileSize' => ['required', 'integer', 'min:1'], 'maxStorageQuota' => ['required', 'integer', 'min:1'], 'maxFilesPerShare' => ['required', 'integer', 'min:1'], @@ -101,7 +103,7 @@ class AdminSettings extends Component 'siteLogo' => ['nullable', 'file', 'mimes:png,jpg,jpeg,gif,webp', 'max:2048'], ...$this->passwordGeneratorRules(), ], [ - ...$this->passwordGeneratorMessages(), + 'passwordCharacterSets.required' => __('Choose at least one kind of character.'), ]); if ($this->systemPassword) { @@ -160,16 +162,6 @@ class AdminSettings extends Component ]; } - /** - * @return array - */ - protected function passwordGeneratorMessages(): array - { - return [ - 'passwordCharacterSets.required' => __('Choose at least one kind of character.'), - ]; - } - /** * Store the generator settings that passed validation; excluded ones keep their saved value. * diff --git a/app/Livewire/FileUploader.php b/app/Livewire/FileUploader.php index 8b40c78..6536789 100644 --- a/app/Livewire/FileUploader.php +++ b/app/Livewire/FileUploader.php @@ -6,8 +6,10 @@ use App\Models\Setting; use App\Models\Share; use App\Services\PasswordGeneratorService; use App\Services\ShareService; +use Carbon\CarbonInterval; use Illuminate\Support\Facades\Crypt; use Illuminate\Support\Str; +use Illuminate\Validation\Rule; use Illuminate\Validation\ValidationException; use Livewire\Attributes\Layout; use Livewire\Attributes\Locked; @@ -135,7 +137,7 @@ class FileUploader extends Component $rules = []; if (! Setting::get('allow_never_expire', false)) { - $rules['expiration'] = ['required', 'string', 'in:1h,24h,48h,7d,14d,30d']; + $rules['expiration'] = ['required', 'string', Rule::in(array_keys(Share::EXPIRATIONS))]; } if ($this->usePassword) { @@ -158,15 +160,9 @@ class FileUploader extends Component $share = $shareService->completeShare($pendingShare, [ 'password' => $this->usePassword ? $this->password : null, - 'expires_at' => match ($this->expiration) { - '1h' => now()->addHour(), - '24h' => now()->addDay(), - '48h' => now()->addDays(2), - '7d' => now()->addWeek(), - '14d' => now()->addDays(14), - '30d' => now()->addMonth(), - default => null, - }, + 'expires_at' => isset(Share::EXPIRATIONS[$this->expiration]) + ? now()->add(CarbonInterval::make(Share::EXPIRATIONS[$this->expiration]['interval'])) + : null, 'max_downloads' => $this->maxDownloads ?: null, ]); diff --git a/app/Livewire/SetupWizard.php b/app/Livewire/SetupWizard.php index 256b735..c99a960 100644 --- a/app/Livewire/SetupWizard.php +++ b/app/Livewire/SetupWizard.php @@ -2,7 +2,6 @@ namespace App\Livewire; -use App\Models\Setting; use App\Models\User; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Hash; @@ -45,14 +44,11 @@ class SetupWizard extends Component 'name' => $this->name, 'email' => $this->email, 'password' => Hash::make($this->password), - 'email_verified_at' => now(), ]); $user->is_admin = true; $user->save(); - Setting::set('setup_complete', 'true'); - Auth::login($user); $this->redirect(route('admin.dashboard'), navigate: true); diff --git a/app/Livewire/ShareDownload.php b/app/Livewire/ShareDownload.php index 90076f7..f14442f 100644 --- a/app/Livewire/ShareDownload.php +++ b/app/Livewire/ShareDownload.php @@ -30,13 +30,7 @@ class ShareDownload extends Component abort(404); } - if (! $share->isPasswordProtected()) { - $this->authenticated = true; - } - - if ($share->isPasswordProtected() && session('share_key_'.$share->token)) { - $this->authenticated = true; - } + $this->authenticated = ! $share->isPasswordProtected() || (bool) session('share_key_'.$share->token); } public function verifyPassword(ShareService $shareService): void diff --git a/app/Models/Share.php b/app/Models/Share.php index 2060f76..45972ca 100644 --- a/app/Models/Share.php +++ b/app/Models/Share.php @@ -10,6 +10,21 @@ class Share extends Model { use HasFactory; + /** + * The expiration times an uploader can choose, by the id the upload form and Admin settings + * store: each one's label and how long a share lasts with it. + * + * @var array + */ + public const EXPIRATIONS = [ + '1h' => ['label' => '1 Hour', 'interval' => '1 hour'], + '24h' => ['label' => '24 Hours', 'interval' => '1 day'], + '48h' => ['label' => '48 Hours', 'interval' => '2 days'], + '7d' => ['label' => '7 Days', 'interval' => '7 days'], + '14d' => ['label' => '14 Days', 'interval' => '14 days'], + '30d' => ['label' => '30 Days', 'interval' => '30 days'], + ]; + protected $fillable = [ 'token', 'password', diff --git a/app/Models/User.php b/app/Models/User.php index 0cce23f..9c9f07e 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -2,12 +2,10 @@ namespace App\Models; -// use Illuminate\Contracts\Auth\MustVerifyEmail; use Database\Factories\UserFactory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; -use Illuminate\Support\Str; use Laravel\Fortify\TwoFactorAuthenticatable; class User extends Authenticatable @@ -51,16 +49,4 @@ class User extends Authenticatable 'is_admin' => 'boolean', ]; } - - /** - * Get the user's initials - */ - public function initials(): string - { - return Str::of($this->name) - ->explode(' ') - ->take(2) - ->map(fn ($word) => Str::substr($word, 0, 1)) - ->implode(''); - } } diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 2f477cd..7a84ffd 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -12,14 +12,6 @@ use NoNameWeb\LivewireMaterial\Support\Scheme; class AppServiceProvider extends ServiceProvider { - /** - * Register any application services. - */ - public function register(): void - { - // - } - /** * Bootstrap any application services. */ diff --git a/app/Providers/FortifyServiceProvider.php b/app/Providers/FortifyServiceProvider.php index 74c4913..4bb9d0e 100644 --- a/app/Providers/FortifyServiceProvider.php +++ b/app/Providers/FortifyServiceProvider.php @@ -2,7 +2,6 @@ namespace App\Providers; -use App\Actions\Fortify\CreateNewUser; use App\Actions\Fortify\ResetUserPassword; use Illuminate\Cache\RateLimiting\Limit; use Illuminate\Http\Request; @@ -13,14 +12,6 @@ use Laravel\Fortify\Fortify; class FortifyServiceProvider extends ServiceProvider { - /** - * Register any application services. - */ - public function register(): void - { - // - } - /** * Bootstrap any application services. */ @@ -37,7 +28,6 @@ class FortifyServiceProvider extends ServiceProvider private function configureActions(): void { Fortify::resetUserPasswordsUsing(ResetUserPassword::class); - Fortify::createUsersUsing(CreateNewUser::class); } /** @@ -46,7 +36,6 @@ class FortifyServiceProvider extends ServiceProvider private function configureViews(): void { Fortify::loginView(fn () => view('pages::auth.login')); - Fortify::verifyEmailView(fn () => view('pages::auth.verify-email')); Fortify::twoFactorChallengeView(fn () => view('pages::auth.two-factor-challenge')); Fortify::confirmPasswordView(fn () => view('pages::auth.confirm-password')); Fortify::resetPasswordView(fn () => view('pages::auth.reset-password')); diff --git a/app/Services/FileEncryptionService.php b/app/Services/FileEncryptionService.php index 2b8183d..aeec836 100644 --- a/app/Services/FileEncryptionService.php +++ b/app/Services/FileEncryptionService.php @@ -53,14 +53,6 @@ class FileEncryptionService return hash_pbkdf2('sha256', $password, hex2bin($salt), self::PBKDF2_ITERATIONS, self::KEY_LENGTH, true); } - /** - * Generate a random hex salt (32 bytes = 64 hex chars). - */ - public function generateSalt(): string - { - return bin2hex(random_bytes(32)); - } - /** * Generate a random encryption key (32 bytes, returned as hex). */ @@ -207,60 +199,6 @@ class FileEncryptionService return $plaintext; } - /** - * Encrypt a file on the server in the `SEALCHK2` format. - */ - public function encryptFile(string $sourcePath, string $destPath, string $key, int $chunkSize): void - { - $source = fopen($sourcePath, 'rb'); - - if ($source === false) { - throw new RuntimeException("Cannot read source file: {$sourcePath}"); - } - - $dest = fopen($destPath, 'wb'); - - if ($dest === false) { - fclose($source); - - throw new RuntimeException("Cannot write encrypted file: {$destPath}"); - } - - try { - $header = $this->createHeader($chunkSize); - $noncePrefix = $this->parseHeader($header)['noncePrefix']; - $chunkCount = $this->chunkCount((int) filesize($sourcePath), $chunkSize); - - fwrite($dest, $header); - - for ($index = 0; $index < $chunkCount; $index++) { - $plaintext = (string) fread($source, $chunkSize); - - fwrite($dest, $this->encryptChunk($plaintext, $key, $noncePrefix, $index, $index === $chunkCount - 1)); - } - } catch (RuntimeException $e) { - fclose($source); - fclose($dest); - @unlink($destPath); - - throw $e; - } - - fclose($source); - fclose($dest); - } - - /** - * Stream decrypted file content directly to output (echo). - */ - public function streamDecryptedFile(string $encryptedPath, string $key): void - { - foreach ($this->decryptedChunks($encryptedPath, $key) as $chunk) { - echo $chunk; - flush(); - } - } - /** * The decrypted content of a file in any of the three formats, chunk by chunk. * diff --git a/bootstrap/app.php b/bootstrap/app.php index c6e66e7..c51f0a4 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -5,7 +5,6 @@ use App\Http\Middleware\EnsureSetupComplete; use App\Http\Middleware\SecurityHeaders; use App\Http\Middleware\SystemPasswordGate; use Illuminate\Foundation\Application; -use Illuminate\Foundation\Configuration\Exceptions; use Illuminate\Foundation\Configuration\Middleware; return Application::configure(basePath: dirname(__DIR__)) @@ -27,6 +26,5 @@ return Application::configure(basePath: dirname(__DIR__)) 'admin' => EnsureAdmin::class, ]); }) - ->withExceptions(function (Exceptions $exceptions): void { - // - })->create(); + ->withExceptions() + ->create(); diff --git a/composer.json b/composer.json index 3301233..a42c9b9 100644 --- a/composer.json +++ b/composer.json @@ -22,9 +22,7 @@ "require-dev": { "fakerphp/faker": "^1.23", "laravel/boost": "^2.0", - "laravel/pail": "^1.2.2", "laravel/pint": "^1.24", - "laravel/sail": "^1.41", "mockery/mockery": "^1.6", "nunomaduro/collision": "^8.6", "pestphp/pest": "^5.1", @@ -52,10 +50,6 @@ "npm install", "npm run build" ], - "dev": [ - "Composer\\Config::disableProcessTimeout", - "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan octane:frankenphp --host=127.0.0.1 --port=8000 --watch\" \"php artisan queue:listen --tries=1 --timeout=0\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite --kill-others" - ], "lint": [ "pint --parallel" ], diff --git a/composer.lock b/composer.lock index 4815f0c..3ecc979 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "ac00b1ec9288209410d7a370eabfb8ef", + "content-hash": "77735015384f764397037325aeca7d63", "packages": [ { "name": "bacon/bacon-qr-code", @@ -9973,86 +9973,6 @@ }, "time": "2026-09-14T14:35:19+00:00" }, - { - "name": "laravel/pail", - "version": "v1.2.7", - "source": { - "type": "git", - "url": "https://github.com/laravel/pail.git", - "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/pail/zipball/2f7d27dada8effc48b8c424445a69cca7007daaa", - "reference": "2f7d27dada8effc48b8c424445a69cca7007daaa", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "illuminate/console": "^10.24|^11.0|^12.0|^13.0", - "illuminate/contracts": "^10.24|^11.0|^12.0|^13.0", - "illuminate/log": "^10.24|^11.0|^12.0|^13.0", - "illuminate/process": "^10.24|^11.0|^12.0|^13.0", - "illuminate/support": "^10.24|^11.0|^12.0|^13.0", - "nunomaduro/termwind": "^1.15|^2.0", - "php": "^8.2", - "symfony/console": "^6.0|^7.0|^8.0" - }, - "require-dev": { - "laravel/framework": "^10.24|^11.0|^12.0|^13.0", - "laravel/pint": "^1.13", - "orchestra/testbench-core": "^8.13|^9.17|^10.8|^11.0", - "pestphp/pest": "^2.20|^3.0|^4.0", - "pestphp/pest-plugin-type-coverage": "^2.3|^3.0|^4.0", - "phpstan/phpstan": "^1.12.27", - "symfony/var-dumper": "^6.3|^7.0|^8.0", - "symfony/yaml": "^6.3|^7.0|^8.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Pail\\PailServiceProvider" - ] - }, - "branch-alias": { - "dev-main": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Laravel\\Pail\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - }, - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Easily delve into your Laravel application's log files directly from the command line.", - "homepage": "https://github.com/laravel/pail", - "keywords": [ - "dev", - "laravel", - "logs", - "php", - "tail" - ], - "support": { - "issues": "https://github.com/laravel/pail/issues", - "source": "https://github.com/laravel/pail" - }, - "time": "2026-05-20T22:24:57+00:00" - }, { "name": "laravel/pint", "version": "v1.32.1", @@ -10185,69 +10105,6 @@ }, "time": "2026-07-18T17:53:15+00:00" }, - { - "name": "laravel/sail", - "version": "v1.67.0", - "source": { - "type": "git", - "url": "https://github.com/laravel/sail.git", - "reference": "639e03ac12cf23def171770bcab05758045b2642" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/laravel/sail/zipball/639e03ac12cf23def171770bcab05758045b2642", - "reference": "639e03ac12cf23def171770bcab05758045b2642", - "shasum": "" - }, - "require": { - "illuminate/console": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "illuminate/contracts": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "illuminate/support": "^9.52.16|^10.0|^11.0|^12.0|^13.0", - "php": "^8.0", - "symfony/console": "^6.0|^7.0|^8.0", - "symfony/yaml": "^6.0|^7.0|^8.0" - }, - "require-dev": { - "orchestra/testbench": "^7.0|^8.0|^9.0|^10.0|^11.0", - "phpstan/phpstan": "^2.0" - }, - "bin": [ - "bin/sail" - ], - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Laravel\\Sail\\SailServiceProvider" - ] - } - }, - "autoload": { - "psr-4": { - "Laravel\\Sail\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylor@laravel.com" - } - ], - "description": "Docker files for running a basic Laravel application.", - "keywords": [ - "docker", - "laravel" - ], - "support": { - "issues": "https://github.com/laravel/sail/issues", - "source": "https://github.com/laravel/sail" - }, - "time": "2026-08-12T13:55:56+00:00" - }, { "name": "league/uri-components", "version": "7.8.1", @@ -13151,5 +13008,5 @@ "php": "^8.5" }, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/config/app.php b/config/app.php index aa7fb98..d3c1b9c 100644 --- a/config/app.php +++ b/config/app.php @@ -1,17 +1,16 @@ env('APP_NAME', 'SealShare'), @@ -28,112 +27,4 @@ return [ 'version' => '2.1.0', - /* - |-------------------------------------------------------------------------- - | Application Environment - |-------------------------------------------------------------------------- - | - | This value determines the "environment" your application is currently - | running in. This may determine how you prefer to configure various - | services the application utilizes. Set this in your ".env" file. - | - */ - - 'env' => env('APP_ENV', 'production'), - - /* - |-------------------------------------------------------------------------- - | Application Debug Mode - |-------------------------------------------------------------------------- - | - | When your application is in debug mode, detailed error messages with - | stack traces will be shown on every error that occurs within your - | application. If disabled, a simple generic error page is shown. - | - */ - - 'debug' => (bool) env('APP_DEBUG', false), - - /* - |-------------------------------------------------------------------------- - | Application URL - |-------------------------------------------------------------------------- - | - | This URL is used by the console to properly generate URLs when using - | the Artisan command line tool. You should set this to the root of - | the application so that it's available within Artisan commands. - | - */ - - 'url' => env('APP_URL', 'http://localhost'), - - /* - |-------------------------------------------------------------------------- - | Application Timezone - |-------------------------------------------------------------------------- - | - | Here you may specify the default timezone for your application, which - | will be used by the PHP date and date-time functions. The timezone - | is set to "UTC" by default as it is suitable for most use cases. - | - */ - - 'timezone' => 'UTC', - - /* - |-------------------------------------------------------------------------- - | Application Locale Configuration - |-------------------------------------------------------------------------- - | - | The application locale determines the default locale that will be used - | by Laravel's translation / localization methods. This option can be - | set to any locale for which you plan to have translation strings. - | - */ - - 'locale' => env('APP_LOCALE', 'en'), - - 'fallback_locale' => env('APP_FALLBACK_LOCALE', 'en'), - - 'faker_locale' => env('APP_FAKER_LOCALE', 'en_US'), - - /* - |-------------------------------------------------------------------------- - | Encryption Key - |-------------------------------------------------------------------------- - | - | This key is utilized by Laravel's encryption services and should be set - | to a random, 32 character string to ensure that all encrypted values - | are secure. You should do this prior to deploying the application. - | - */ - - 'cipher' => 'AES-256-CBC', - - 'key' => env('APP_KEY'), - - 'previous_keys' => [ - ...array_filter( - explode(',', (string) env('APP_PREVIOUS_KEYS', '')) - ), - ], - - /* - |-------------------------------------------------------------------------- - | Maintenance Mode Driver - |-------------------------------------------------------------------------- - | - | These configuration options determine the driver used to determine and - | manage Laravel's "maintenance mode" status. The "cache" driver will - | allow maintenance mode to be controlled across multiple machines. - | - | Supported drivers: "file", "cache" - | - */ - - 'maintenance' => [ - 'driver' => env('APP_MAINTENANCE_DRIVER', 'file'), - 'store' => env('APP_MAINTENANCE_STORE', 'database'), - ], - ]; diff --git a/config/auth.php b/config/auth.php deleted file mode 100644 index d7568ff..0000000 --- a/config/auth.php +++ /dev/null @@ -1,117 +0,0 @@ - [ - 'guard' => env('AUTH_GUARD', 'web'), - 'passwords' => env('AUTH_PASSWORD_BROKER', 'users'), - ], - - /* - |-------------------------------------------------------------------------- - | Authentication Guards - |-------------------------------------------------------------------------- - | - | Next, you may define every authentication guard for your application. - | Of course, a great default configuration has been defined for you - | which utilizes session storage plus the Eloquent user provider. - | - | All authentication guards have a user provider, which defines how the - | users are actually retrieved out of your database or other storage - | system used by the application. Typically, Eloquent is utilized. - | - | Supported: "session" - | - */ - - 'guards' => [ - 'web' => [ - 'driver' => 'session', - 'provider' => 'users', - ], - ], - - /* - |-------------------------------------------------------------------------- - | User Providers - |-------------------------------------------------------------------------- - | - | All authentication guards have a user provider, which defines how the - | users are actually retrieved out of your database or other storage - | system used by the application. Typically, Eloquent is utilized. - | - | If you have multiple user tables or models you may configure multiple - | providers to represent the model / table. These providers may then - | be assigned to any extra authentication guards you have defined. - | - | Supported: "database", "eloquent" - | - */ - - 'providers' => [ - 'users' => [ - 'driver' => 'eloquent', - 'model' => env('AUTH_MODEL', User::class), - ], - - // 'users' => [ - // 'driver' => 'database', - // 'table' => 'users', - // ], - ], - - /* - |-------------------------------------------------------------------------- - | Resetting Passwords - |-------------------------------------------------------------------------- - | - | These configuration options specify the behavior of Laravel's password - | reset functionality, including the table utilized for token storage - | and the user provider that is invoked to actually retrieve users. - | - | The expiry time is the number of minutes that each reset token will be - | considered valid. This security feature keeps tokens short-lived so - | they have less time to be guessed. You may change this as needed. - | - | The throttle setting is the number of seconds a user must wait before - | generating more password reset tokens. This prevents the user from - | quickly generating a very large amount of password reset tokens. - | - */ - - 'passwords' => [ - 'users' => [ - 'provider' => 'users', - 'table' => env('AUTH_PASSWORD_RESET_TOKEN_TABLE', 'password_reset_tokens'), - 'expire' => 60, - 'throttle' => 60, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Password Confirmation Timeout - |-------------------------------------------------------------------------- - | - | Here you may define the number of seconds before a password confirmation - | window expires and users are asked to re-enter their password via the - | confirmation screen. By default, the timeout lasts for three hours. - | - */ - - 'password_timeout' => env('AUTH_PASSWORD_TIMEOUT', 10800), - -]; diff --git a/config/cache.php b/config/cache.php index c68acdf..7bf57a6 100644 --- a/config/cache.php +++ b/config/cache.php @@ -1,130 +1,18 @@ env('CACHE_STORE', 'database'), - - /* - |-------------------------------------------------------------------------- - | Cache Stores - |-------------------------------------------------------------------------- - | - | Here you may define all of the cache "stores" for your application as - | well as their drivers. You may even define multiple stores for the - | same cache driver to group types of items stored in your caches. - | - | Supported drivers: "array", "database", "file", "memcached", - | "redis", "dynamodb", "octane", - | "failover", "null" - | - */ - - 'stores' => [ - - 'array' => [ - 'driver' => 'array', - 'serialize' => false, - ], - - 'database' => [ - 'driver' => 'database', - 'connection' => env('DB_CACHE_CONNECTION'), - 'table' => env('DB_CACHE_TABLE', 'cache'), - 'lock_connection' => env('DB_CACHE_LOCK_CONNECTION'), - 'lock_table' => env('DB_CACHE_LOCK_TABLE'), - ], - - 'file' => [ - 'driver' => 'file', - 'path' => storage_path('framework/cache/data'), - 'lock_path' => storage_path('framework/cache/data'), - ], - - 'memcached' => [ - 'driver' => 'memcached', - 'persistent_id' => env('MEMCACHED_PERSISTENT_ID'), - 'sasl' => [ - env('MEMCACHED_USERNAME'), - env('MEMCACHED_PASSWORD'), - ], - 'options' => [ - // Memcached::OPT_CONNECT_TIMEOUT => 2000, - ], - 'servers' => [ - [ - 'host' => env('MEMCACHED_HOST', '127.0.0.1'), - 'port' => env('MEMCACHED_PORT', 11211), - 'weight' => 100, - ], - ], - ], - - 'redis' => [ - 'driver' => 'redis', - 'connection' => env('REDIS_CACHE_CONNECTION', 'cache'), - 'lock_connection' => env('REDIS_CACHE_LOCK_CONNECTION', 'default'), - ], - - 'dynamodb' => [ - 'driver' => 'dynamodb', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), - 'table' => env('DYNAMODB_CACHE_TABLE', 'cache'), - 'endpoint' => env('DYNAMODB_ENDPOINT'), - ], - - 'octane' => [ - 'driver' => 'octane', - ], - - 'failover' => [ - 'driver' => 'failover', - 'stores' => [ - 'database', - 'array', - ], - ], - - ], - - /* - |-------------------------------------------------------------------------- - | Cache Key Prefix - |-------------------------------------------------------------------------- - | - | When utilizing the APC, database, memcached, Redis, and DynamoDB cache - | stores, there might be other applications using the same cache. For - | that reason, you may prefix every cache key to avoid collisions. - | - */ - - 'prefix' => env('CACHE_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-cache-'), - - /* - |-------------------------------------------------------------------------- - | Serializable Classes - |-------------------------------------------------------------------------- - | - | This value determines the classes that can be unserialized from cache - | storage. By default, no PHP classes will be unserialized from your - | cache to prevent gadget chain attacks if your APP_KEY is leaked. - | - */ - 'serializable_classes' => false, ]; diff --git a/config/database.php b/config/database.php deleted file mode 100644 index 64709ce..0000000 --- a/config/database.php +++ /dev/null @@ -1,184 +0,0 @@ - env('DB_CONNECTION', 'sqlite'), - - /* - |-------------------------------------------------------------------------- - | Database Connections - |-------------------------------------------------------------------------- - | - | Below are all of the database connections defined for your application. - | An example configuration is provided for each database system which - | is supported by Laravel. You're free to add / remove connections. - | - */ - - 'connections' => [ - - 'sqlite' => [ - 'driver' => 'sqlite', - 'url' => env('DB_URL'), - 'database' => env('DB_DATABASE', database_path('database.sqlite')), - 'prefix' => '', - 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), - 'busy_timeout' => null, - 'journal_mode' => null, - 'synchronous' => null, - 'transaction_mode' => 'DEFERRED', - ], - - 'mysql' => [ - 'driver' => 'mysql', - 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'laravel'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => env('DB_CHARSET', 'utf8mb4'), - 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), - 'prefix' => '', - 'prefix_indexes' => true, - 'strict' => true, - 'engine' => null, - 'options' => extension_loaded('pdo_mysql') ? array_filter([ - (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), - ]) : [], - ], - - 'mariadb' => [ - 'driver' => 'mariadb', - 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', '3306'), - 'database' => env('DB_DATABASE', 'laravel'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'unix_socket' => env('DB_SOCKET', ''), - 'charset' => env('DB_CHARSET', 'utf8mb4'), - 'collation' => env('DB_COLLATION', 'utf8mb4_unicode_ci'), - 'prefix' => '', - 'prefix_indexes' => true, - 'strict' => true, - 'engine' => null, - 'options' => extension_loaded('pdo_mysql') ? array_filter([ - (PHP_VERSION_ID >= 80500 ? Mysql::ATTR_SSL_CA : PDO::MYSQL_ATTR_SSL_CA) => env('MYSQL_ATTR_SSL_CA'), - ]) : [], - ], - - 'pgsql' => [ - 'driver' => 'pgsql', - 'url' => env('DB_URL'), - 'host' => env('DB_HOST', '127.0.0.1'), - 'port' => env('DB_PORT', '5432'), - 'database' => env('DB_DATABASE', 'laravel'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => env('DB_CHARSET', 'utf8'), - 'prefix' => '', - 'prefix_indexes' => true, - 'search_path' => 'public', - 'sslmode' => env('DB_SSLMODE', 'prefer'), - ], - - 'sqlsrv' => [ - 'driver' => 'sqlsrv', - 'url' => env('DB_URL'), - 'host' => env('DB_HOST', 'localhost'), - 'port' => env('DB_PORT', '1433'), - 'database' => env('DB_DATABASE', 'laravel'), - 'username' => env('DB_USERNAME', 'root'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => env('DB_CHARSET', 'utf8'), - 'prefix' => '', - 'prefix_indexes' => true, - // 'encrypt' => env('DB_ENCRYPT', 'yes'), - // 'trust_server_certificate' => env('DB_TRUST_SERVER_CERTIFICATE', 'false'), - ], - - ], - - /* - |-------------------------------------------------------------------------- - | Migration Repository Table - |-------------------------------------------------------------------------- - | - | This table keeps track of all the migrations that have already run for - | your application. Using this information, we can determine which of - | the migrations on disk haven't actually been run on the database. - | - */ - - 'migrations' => [ - 'table' => 'migrations', - 'update_date_on_publish' => true, - ], - - /* - |-------------------------------------------------------------------------- - | Redis Databases - |-------------------------------------------------------------------------- - | - | Redis is an open source, fast, and advanced key-value store that also - | provides a richer body of commands than a typical key-value system - | such as Memcached. You may define your connection settings here. - | - */ - - 'redis' => [ - - 'client' => env('REDIS_CLIENT', 'phpredis'), - - 'options' => [ - 'cluster' => env('REDIS_CLUSTER', 'redis'), - 'prefix' => env('REDIS_PREFIX', Str::slug((string) env('APP_NAME', 'laravel')).'-database-'), - 'persistent' => env('REDIS_PERSISTENT', false), - ], - - 'default' => [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_DB', '0'), - 'max_retries' => env('REDIS_MAX_RETRIES', 3), - 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), - 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), - 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), - ], - - 'cache' => [ - 'url' => env('REDIS_URL'), - 'host' => env('REDIS_HOST', '127.0.0.1'), - 'username' => env('REDIS_USERNAME'), - 'password' => env('REDIS_PASSWORD'), - 'port' => env('REDIS_PORT', '6379'), - 'database' => env('REDIS_CACHE_DB', '1'), - 'max_retries' => env('REDIS_MAX_RETRIES', 3), - 'backoff_algorithm' => env('REDIS_BACKOFF_ALGORITHM', 'decorrelated_jitter'), - 'backoff_base' => env('REDIS_BACKOFF_BASE', 100), - 'backoff_cap' => env('REDIS_BACKOFF_CAP', 1000), - ], - - ], - -]; diff --git a/config/filesystems.php b/config/filesystems.php index 51005ac..9996815 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -1,52 +1,19 @@ env('FILESYSTEM_DISK', 'local'), - - /* - |-------------------------------------------------------------------------- - | Filesystem Disks - |-------------------------------------------------------------------------- - | - | Below you may configure as many filesystem disks as necessary, and you - | may even configure multiple disks for the same driver. Examples for - | most supported storage drivers are configured here for reference. - | - | Supported drivers: "local", "ftp", "sftp", "s3" - | - */ - 'disks' => [ - 'local' => [ - 'driver' => 'local', - 'root' => storage_path('app/private'), - 'serve' => true, - 'throw' => false, - 'report' => false, - ], - - 'public' => [ - 'driver' => 'local', - 'root' => storage_path('app/public'), - 'url' => rtrim(env('APP_URL', 'http://localhost'), '/').'/storage', - 'visibility' => 'public', - 'throw' => false, - 'report' => false, - ], - 'shares' => [ 'driver' => 'local', 'root' => storage_path('app/shares'), @@ -54,34 +21,6 @@ return [ 'report' => false, ], - 's3' => [ - 'driver' => 's3', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION'), - 'bucket' => env('AWS_BUCKET'), - 'url' => env('AWS_URL'), - 'endpoint' => env('AWS_ENDPOINT'), - 'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false), - 'throw' => false, - 'report' => false, - ], - - ], - - /* - |-------------------------------------------------------------------------- - | Symbolic Links - |-------------------------------------------------------------------------- - | - | Here you may configure the symbolic links that will be created when the - | `storage:link` Artisan command is executed. The array keys should be - | the locations of the links and the values should be their targets. - | - */ - - 'links' => [ - public_path('storage') => storage_path('app/public'), ], ]; diff --git a/config/fortify.php b/config/fortify.php index 7ef977b..34caba5 100644 --- a/config/fortify.php +++ b/config/fortify.php @@ -144,9 +144,7 @@ return [ */ 'features' => [ - // Features::registration(), // Disabled - admin created via setup wizard Features::resetPasswords(), - Features::emailVerification(), Features::twoFactorAuthentication([ 'confirm' => true, 'confirmPassword' => true, diff --git a/config/livewire.php b/config/livewire.php index 8ac2240..8d473e3 100644 --- a/config/livewire.php +++ b/config/livewire.php @@ -1,237 +1,23 @@ [ - resource_path('views/components'), - resource_path('views/livewire'), - ], - - /* - |--------------------------------------------------------------------------- - | Component Namespaces - |--------------------------------------------------------------------------- - | - | This value sets default namespaces that will be used to resolve view-based - | components like single-file and multi-file components. These folders'll - | also be referenced when creating new components via the make command. - | - */ - - 'component_namespaces' => [ - 'layouts' => resource_path('views/layouts'), - 'pages' => resource_path('views/pages'), - ], - - /* - |--------------------------------------------------------------------------- - | Page Layout - |--------------------------------------------------------------------------- - | The view that will be used as the layout when rendering a single component as - | an entire page via `Route::livewire('/post/create', 'pages::create-post')`. - | In this case, the content of pages::create-post will render into $slot. - | - */ - - 'component_layout' => 'layouts::app', - - /* - |--------------------------------------------------------------------------- - | Lazy Loading Placeholder - |--------------------------------------------------------------------------- - | Livewire allows you to lazy load components that would otherwise slow down - | the initial page load. Every component can have a custom placeholder or - | you can define the default placeholder view for all components below. - | - */ - - 'component_placeholder' => null, // Example: 'placeholders::skeleton' - - /* - |--------------------------------------------------------------------------- - | Make Command - |--------------------------------------------------------------------------- - | This value determines the default configuration for the artisan make command - | You can configure the component type (sfc, mfc, class) and whether to use - | the high-voltage (⚡) emoji as a prefix in the sfc|mfc component names. - | - */ - - 'make_command' => [ - 'type' => 'sfc', // Options: 'sfc', 'mfc', 'class' - 'emoji' => true, // Options: true, false - 'with' => [ - 'js' => false, - 'css' => false, - 'test' => false, - ], - ], - - /* - |--------------------------------------------------------------------------- - | Class Namespace - |--------------------------------------------------------------------------- - | - | This value sets the root class namespace for Livewire component classes in - | your application. This value will change where component auto-discovery - | finds components. It's also referenced by the file creation commands. - | - */ - - 'class_namespace' => 'App\\Livewire', - - /* - |--------------------------------------------------------------------------- - | Class Path - |--------------------------------------------------------------------------- - | - | This value is used to specify the path where Livewire component class files - | are created when running creation commands like `artisan make:livewire`. - | This path is customizable to match your projects directory structure. - | - */ - - 'class_path' => app_path('Livewire'), - - /* - |--------------------------------------------------------------------------- - | View Path - |--------------------------------------------------------------------------- - | - | This value is used to specify where Livewire component Blade templates are - | stored when running file creation commands like `artisan make:livewire`. - | It is also used if you choose to omit a component's render() method. - | - */ - - 'view_path' => resource_path('views/livewire'), - - /* - |--------------------------------------------------------------------------- - | Temporary File Uploads - |--------------------------------------------------------------------------- - | - | Livewire handles file uploads by storing uploads in a temporary directory - | before the file is stored permanently. All file uploads are directed to - | a global endpoint for temporary storage. You may configure this below: - | - */ - - 'temporary_file_upload' => [ - 'disk' => env('LIVEWIRE_TEMPORARY_FILE_UPLOAD_DISK'), // Example: 'local', 's3' | Default: 'default' - 'rules' => ['required', 'file'], // No size cap: PHP's upload_max_filesize is the hard limit, the admin limit is enforced per-component - 'directory' => null, // Example: 'tmp' | Default: 'livewire-tmp' - 'middleware' => null, // Example: 'throttle:5,1' | Default: 'throttle:60,1' - 'preview_mimes' => [ // Supported file types for temporary pre-signed file URLs... - 'png', 'gif', 'bmp', 'svg', 'wav', 'mp4', - 'mov', 'avi', 'wmv', 'mp3', 'm4a', - 'jpg', 'jpeg', 'mpga', 'webp', 'wma', - ], - 'max_upload_time' => (int) env('LIVEWIRE_MAX_UPLOAD_TIME', 30), // Max duration (in minutes) before an upload is invalidated... - 'cleanup' => true, // Should cleanup temporary uploads older than 24 hrs... - ], - - /* - |--------------------------------------------------------------------------- - | Render On Redirect - |--------------------------------------------------------------------------- - | - | This value determines if Livewire will run a component's `render()` method - | after a redirect has been triggered using something like `redirect(...)` - | Setting this to true will render the view once more before redirecting - | - */ - - 'render_on_redirect' => false, - - /* - |--------------------------------------------------------------------------- - | Eloquent Model Binding - |--------------------------------------------------------------------------- - | - | Previous versions of Livewire supported binding directly to eloquent model - | properties using wire:model by default. However, this behavior has been - | deemed too "magical" and has therefore been put under a feature flag. - | - */ - - 'legacy_model_binding' => false, - - /* - |--------------------------------------------------------------------------- - | Auto-inject Frontend Assets - |--------------------------------------------------------------------------- - | - | By default, Livewire automatically injects its JavaScript and CSS into the - | and of pages containing Livewire components. By disabling - | this behavior, you need to use @livewireStyles and @livewireScripts. - | - */ - - 'inject_assets' => true, - - /* - |--------------------------------------------------------------------------- - | Navigate (SPA mode) - |--------------------------------------------------------------------------- - | - | By adding `wire:navigate` to links in your Livewire application, Livewire - | will prevent the default link handling and instead request those pages - | via AJAX, creating an SPA-like effect. Configure this behavior here. - | - */ - - 'navigate' => [ - 'show_progress_bar' => true, - 'progress_bar_color' => '#2299dd', - ], - - /* - |--------------------------------------------------------------------------- - | HTML Morph Markers - |--------------------------------------------------------------------------- - | - | Livewire intelligently "morphs" existing HTML into the newly rendered HTML - | after each update. To make this process more reliable, Livewire injects - | "markers" into the rendered Blade surrounding @if, @class & @foreach. - | - */ - - 'inject_morph_markers' => true, - - /* - |--------------------------------------------------------------------------- - | Smart Wire Keys - |--------------------------------------------------------------------------- - | - | Livewire uses loops and keys used within loops to generate smart keys that - | are applied to nested components that don't have them. This makes using - | nested components more reliable by ensuring that they all have keys. - | - */ - - 'smart_wire_keys' => true, - /* |--------------------------------------------------------------------------- | Pagination Theme |--------------------------------------------------------------------------- | - | When enabling Livewire's pagination feature by using the `WithPagination` - | trait, Livewire will use Tailwind templates to render pagination views - | on the page. If you want Bootstrap CSS, you can specify: "bootstrap" - | | livewire-material takes this over itself while it still reads as | Livewire's own default ("tailwind", or the key missing), so this stays | explicit: SealShare states the choice itself rather than relying on @@ -241,40 +27,13 @@ return [ 'pagination_theme' => 'material', - /* - |--------------------------------------------------------------------------- - | Release Token - |--------------------------------------------------------------------------- - | - | This token is stored client-side and sent along with each request to check - | a users session to see if a new release has invalidated it. If there is - | a mismatch it will throw an error and prompt for a browser refresh. - | - */ - - 'release_token' => 'a', - - /* - |--------------------------------------------------------------------------- - | CSP Safe - |--------------------------------------------------------------------------- - | - | This config is used to determine if Livewire will use the CSP-safe version - | of Alpine in its bundle. This is useful for applications that are using - | strict Content Security Policy (CSP) to protect against XSS attacks. - | - */ - - 'csp_safe' => false, - /* |--------------------------------------------------------------------------- | Payload Guards |--------------------------------------------------------------------------- | - | These settings protect against malicious or oversized payloads that could - | cause denial of service. The default values should feel reasonable for - | most web applications. Each can be set to null to disable the limit. + | Livewire's defaults, with at most 20 components per batch request + | instead of 200. | */ @@ -284,4 +43,5 @@ return [ 'max_calls' => 50, // Maximum method calls per request 'max_components' => 20, // Maximum components per batch request ], + ]; diff --git a/config/logging.php b/config/logging.php deleted file mode 100644 index 9e998a4..0000000 --- a/config/logging.php +++ /dev/null @@ -1,132 +0,0 @@ - env('LOG_CHANNEL', 'stack'), - - /* - |-------------------------------------------------------------------------- - | Deprecations Log Channel - |-------------------------------------------------------------------------- - | - | This option controls the log channel that should be used to log warnings - | regarding deprecated PHP and library features. This allows you to get - | your application ready for upcoming major versions of dependencies. - | - */ - - 'deprecations' => [ - 'channel' => env('LOG_DEPRECATIONS_CHANNEL', 'null'), - 'trace' => env('LOG_DEPRECATIONS_TRACE', false), - ], - - /* - |-------------------------------------------------------------------------- - | Log Channels - |-------------------------------------------------------------------------- - | - | Here you may configure the log channels for your application. Laravel - | utilizes the Monolog PHP logging library, which includes a variety - | of powerful log handlers and formatters that you're free to use. - | - | Available drivers: "single", "daily", "slack", "syslog", - | "errorlog", "monolog", "custom", "stack" - | - */ - - 'channels' => [ - - 'stack' => [ - 'driver' => 'stack', - 'channels' => explode(',', (string) env('LOG_STACK', 'single')), - 'ignore_exceptions' => false, - ], - - 'single' => [ - 'driver' => 'single', - 'path' => storage_path('logs/laravel.log'), - 'level' => env('LOG_LEVEL', 'debug'), - 'replace_placeholders' => true, - ], - - 'daily' => [ - 'driver' => 'daily', - 'path' => storage_path('logs/laravel.log'), - 'level' => env('LOG_LEVEL', 'debug'), - 'days' => env('LOG_DAILY_DAYS', 14), - 'replace_placeholders' => true, - ], - - 'slack' => [ - 'driver' => 'slack', - 'url' => env('LOG_SLACK_WEBHOOK_URL'), - 'username' => env('LOG_SLACK_USERNAME', 'Laravel Log'), - 'emoji' => env('LOG_SLACK_EMOJI', ':boom:'), - 'level' => env('LOG_LEVEL', 'critical'), - 'replace_placeholders' => true, - ], - - 'papertrail' => [ - 'driver' => 'monolog', - 'level' => env('LOG_LEVEL', 'debug'), - 'handler' => env('LOG_PAPERTRAIL_HANDLER', SyslogUdpHandler::class), - 'handler_with' => [ - 'host' => env('PAPERTRAIL_URL'), - 'port' => env('PAPERTRAIL_PORT'), - 'connectionString' => 'tls://'.env('PAPERTRAIL_URL').':'.env('PAPERTRAIL_PORT'), - ], - 'processors' => [PsrLogMessageProcessor::class], - ], - - 'stderr' => [ - 'driver' => 'monolog', - 'level' => env('LOG_LEVEL', 'debug'), - 'handler' => StreamHandler::class, - 'handler_with' => [ - 'stream' => 'php://stderr', - ], - 'formatter' => env('LOG_STDERR_FORMATTER'), - 'processors' => [PsrLogMessageProcessor::class], - ], - - 'syslog' => [ - 'driver' => 'syslog', - 'level' => env('LOG_LEVEL', 'debug'), - 'facility' => env('LOG_SYSLOG_FACILITY', LOG_USER), - 'replace_placeholders' => true, - ], - - 'errorlog' => [ - 'driver' => 'errorlog', - 'level' => env('LOG_LEVEL', 'debug'), - 'replace_placeholders' => true, - ], - - 'null' => [ - 'driver' => 'monolog', - 'handler' => NullHandler::class, - ], - - 'emergency' => [ - 'path' => storage_path('logs/laravel.log'), - ], - - ], - -]; diff --git a/config/mail.php b/config/mail.php index 9585c48..69fe20a 100644 --- a/config/mail.php +++ b/config/mail.php @@ -1,140 +1,24 @@ env('MAIL_MAILER', 'log'), - - /* - |-------------------------------------------------------------------------- - | Mailer Configurations - |-------------------------------------------------------------------------- - | - | Here you may configure all of the mailers used by your application plus - | their respective settings. Several examples have been configured for - | you and you are free to add your own as your application requires. - | - | Laravel supports a variety of mail "transport" drivers that can be used - | when delivering an email. You may specify which one you're using for - | your mailers below. You may also add additional mailers if needed. - | - | Supported: "smtp", "sendmail", "mailgun", "ses", "ses-v2", - | "postmark", "resend", "log", "array", - | "failover", "roundrobin" - | - */ - - 'mailers' => [ - - 'smtp' => [ - 'transport' => 'smtp', - 'scheme' => env('MAIL_SCHEME'), - 'url' => env('MAIL_URL'), - 'host' => env('MAIL_HOST', '127.0.0.1'), - 'port' => env('MAIL_PORT', 2525), - 'username' => env('MAIL_USERNAME'), - 'password' => env('MAIL_PASSWORD'), - 'timeout' => null, - 'local_domain' => env('MAIL_EHLO_DOMAIN', parse_url((string) env('APP_URL', 'http://localhost'), PHP_URL_HOST)), - ], - - 'ses' => [ - 'transport' => 'ses', - ], - - 'postmark' => [ - 'transport' => 'postmark', - // 'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'), - // 'client' => [ - // 'timeout' => 5, - // ], - ], - - 'resend' => [ - 'transport' => 'resend', - ], - - 'sendmail' => [ - 'transport' => 'sendmail', - 'path' => env('MAIL_SENDMAIL_PATH', '/usr/sbin/sendmail -bs -i'), - ], - - 'log' => [ - 'transport' => 'log', - 'channel' => env('MAIL_LOG_CHANNEL'), - ], - - 'array' => [ - 'transport' => 'array', - ], - - 'failover' => [ - 'transport' => 'failover', - 'mailers' => [ - 'smtp', - 'log', - ], - 'retry_after' => 60, - ], - - 'roundrobin' => [ - 'transport' => 'roundrobin', - 'mailers' => [ - 'ses', - 'postmark', - ], - 'retry_after' => 60, - ], - - ], - - /* - |-------------------------------------------------------------------------- - | Global "From" Address - |-------------------------------------------------------------------------- - | - | You may wish for all emails sent by your application to be sent from - | the same address. Here you may specify a name and address that is - | used globally for all emails that are sent by your application. - | - */ - - 'from' => [ - 'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'), - 'name' => env('MAIL_FROM_NAME', 'Example'), - ], - - /* - |-------------------------------------------------------------------------- - | Markdown Mail Settings - |-------------------------------------------------------------------------- - | - | Markdown mail wears Livewire Material's theme, coloured from the light - | scheme in resources/css/material-scheme.json. - | - */ - 'markdown' => [ 'theme' => env('MAIL_MARKDOWN_THEME', 'livewire-material::mail.theme'), 'paths' => [ resource_path('views/vendor/mail'), ], - - 'extensions' => [ - // \League\CommonMark\Extension\Strikethrough\StrikethroughExtension::class, - ], ], ]; diff --git a/config/octane.php b/config/octane.php index 8692ee1..7056725 100644 --- a/config/octane.php +++ b/config/octane.php @@ -1,222 +1,27 @@ env('OCTANE_SERVER', 'frankenphp'), /* - |-------------------------------------------------------------------------- - | Force HTTPS - |-------------------------------------------------------------------------- - | - | When this configuration value is set to "true", Octane will inform the - | framework that all absolute links must be generated using the HTTPS - | protocol. Otherwise your links may be generated using plain HTTP. - | + | Absolute links use HTTPS whenever APP_URL does. */ 'https' => env('OCTANE_HTTPS', str_starts_with(env('APP_URL', ''), 'https://')), /* - |-------------------------------------------------------------------------- - | Octane Listeners - |-------------------------------------------------------------------------- - | - | All of the event listeners for Octane's events are defined below. These - | listeners are responsible for resetting your application's state for - | the next request. You may even add your own listeners to the list. - | - */ - - 'listeners' => [ - WorkerStarting::class => [ - EnsureUploadedFilesAreValid::class, - EnsureUploadedFilesCanBeMoved::class, - ], - - RequestReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - ...Octane::prepareApplicationForNextRequest(), - // - ], - - RequestHandled::class => [ - // - ], - - RequestTerminated::class => [ - // FlushUploadedFiles::class, - ], - - TaskReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - // - ], - - TaskTerminated::class => [ - // - ], - - TickReceived::class => [ - ...Octane::prepareApplicationForNextOperation(), - // - ], - - TickTerminated::class => [ - // - ], - - OperationTerminated::class => [ - FlushOnce::class, - FlushTemporaryContainerInstances::class, - // DisconnectFromDatabases::class, - // CollectGarbage::class, - ], - - WorkerErrorOccurred::class => [ - ReportException::class, - StopWorkerIfNecessary::class, - ], - - WorkerStopping::class => [ - CloseMonologHandlers::class, - ], - ], - - /* - |-------------------------------------------------------------------------- - | Warm / Flush Bindings - |-------------------------------------------------------------------------- - | - | The bindings listed below will either be pre-warmed when a worker boots - | or they will be flushed before every new request. Flushing a binding - | will force the container to resolve that binding again when asked. - | - */ - - 'warm' => [ - ...Octane::defaultServicesToWarm(), - ], - - 'flush' => [ - // - ], - - /* - |-------------------------------------------------------------------------- - | Octane Swoole Tables - |-------------------------------------------------------------------------- - | - | While using Swoole, you may define additional tables as required by the - | application. These tables can be used to store data that needs to be - | quickly accessed by other workers on the particular Swoole server. - | - */ - - 'tables' => [ - 'example:1000' => [ - 'name' => 'string:1000', - 'votes' => 'int', - ], - ], - - /* - |-------------------------------------------------------------------------- - | Octane Swoole Cache Table - |-------------------------------------------------------------------------- - | - | While using Swoole, you may leverage the Octane cache, which is powered - | by a Swoole table. You may set the maximum number of rows as well as - | the number of bytes per row using the configuration options below. - | - */ - - 'cache' => [ - 'rows' => 1000, - 'bytes' => 10000, - ], - - /* - |-------------------------------------------------------------------------- - | File Watching - |-------------------------------------------------------------------------- - | - | The following list of files and directories will be watched when using - | the --watch option offered by Octane. If any of the directories and - | files are changed, Octane will automatically reload your workers. - | - */ - - 'watch' => [ - 'app', - 'bootstrap', - 'config/**/*.php', - 'database/**/*.php', - 'public/**/*.php', - 'resources/**/*.php', - 'routes', - 'composer.lock', - '.env', - ], - - /* - |-------------------------------------------------------------------------- - | Garbage Collection Threshold - |-------------------------------------------------------------------------- - | - | When executing long-lived PHP scripts such as Octane, memory can build - | up before being cleared by PHP. You can force Octane to run garbage - | collection if your application consumes this amount of megabytes. - | - */ - - 'garbage' => 50, - - /* - |-------------------------------------------------------------------------- - | Maximum Execution Time - |-------------------------------------------------------------------------- - | - | The following setting configures the maximum execution time for requests - | being handled by Octane. You may set this value to 0 to indicate that - | there isn't a specific time limit on Octane request execution time. - | + | Requests may run for up to 300 seconds instead of Octane's default 30. */ 'max_execution_time' => env('OCTANE_MAX_EXECUTION_TIME', 300), diff --git a/config/queue.php b/config/queue.php deleted file mode 100644 index 79c2c0a..0000000 --- a/config/queue.php +++ /dev/null @@ -1,129 +0,0 @@ - env('QUEUE_CONNECTION', 'database'), - - /* - |-------------------------------------------------------------------------- - | Queue Connections - |-------------------------------------------------------------------------- - | - | Here you may configure the connection options for every queue backend - | used by your application. An example configuration is provided for - | each backend supported by Laravel. You're also free to add more. - | - | Drivers: "sync", "database", "beanstalkd", "sqs", "redis", - | "deferred", "background", "failover", "null" - | - */ - - 'connections' => [ - - 'sync' => [ - 'driver' => 'sync', - ], - - 'database' => [ - 'driver' => 'database', - 'connection' => env('DB_QUEUE_CONNECTION'), - 'table' => env('DB_QUEUE_TABLE', 'jobs'), - 'queue' => env('DB_QUEUE', 'default'), - 'retry_after' => (int) env('DB_QUEUE_RETRY_AFTER', 90), - 'after_commit' => false, - ], - - 'beanstalkd' => [ - 'driver' => 'beanstalkd', - 'host' => env('BEANSTALKD_QUEUE_HOST', 'localhost'), - 'queue' => env('BEANSTALKD_QUEUE', 'default'), - 'retry_after' => (int) env('BEANSTALKD_QUEUE_RETRY_AFTER', 90), - 'block_for' => 0, - 'after_commit' => false, - ], - - 'sqs' => [ - 'driver' => 'sqs', - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'prefix' => env('SQS_PREFIX', 'https://sqs.us-east-1.amazonaws.com/your-account-id'), - 'queue' => env('SQS_QUEUE', 'default'), - 'suffix' => env('SQS_SUFFIX'), - 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), - 'after_commit' => false, - ], - - 'redis' => [ - 'driver' => 'redis', - 'connection' => env('REDIS_QUEUE_CONNECTION', 'default'), - 'queue' => env('REDIS_QUEUE', 'default'), - 'retry_after' => (int) env('REDIS_QUEUE_RETRY_AFTER', 90), - 'block_for' => null, - 'after_commit' => false, - ], - - 'deferred' => [ - 'driver' => 'deferred', - ], - - 'background' => [ - 'driver' => 'background', - ], - - 'failover' => [ - 'driver' => 'failover', - 'connections' => [ - 'database', - 'deferred', - ], - ], - - ], - - /* - |-------------------------------------------------------------------------- - | Job Batching - |-------------------------------------------------------------------------- - | - | The following options configure the database and table that store job - | batching information. These options can be updated to any database - | connection and table which has been defined by your application. - | - */ - - 'batching' => [ - 'database' => env('DB_CONNECTION', 'sqlite'), - 'table' => 'job_batches', - ], - - /* - |-------------------------------------------------------------------------- - | Failed Queue Jobs - |-------------------------------------------------------------------------- - | - | These options configure the behavior of failed queue job logging so you - | can control how and where failed jobs are stored. Laravel ships with - | support for storing failed jobs in a simple file or in a database. - | - | Supported drivers: "database-uuids", "dynamodb", "file", "null" - | - */ - - 'failed' => [ - 'driver' => env('QUEUE_FAILED_DRIVER', 'database-uuids'), - 'database' => env('DB_CONNECTION', 'sqlite'), - 'table' => 'failed_jobs', - ], - -]; diff --git a/config/services.php b/config/services.php deleted file mode 100644 index 6a90eb8..0000000 --- a/config/services.php +++ /dev/null @@ -1,38 +0,0 @@ - [ - 'key' => env('POSTMARK_API_KEY'), - ], - - 'resend' => [ - 'key' => env('RESEND_API_KEY'), - ], - - 'ses' => [ - 'key' => env('AWS_ACCESS_KEY_ID'), - 'secret' => env('AWS_SECRET_ACCESS_KEY'), - 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), - ], - - 'slack' => [ - 'notifications' => [ - 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), - 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), - ], - ], - -]; diff --git a/config/session.php b/config/session.php index 5b541b7..9f47b35 100644 --- a/config/session.php +++ b/config/session.php @@ -2,216 +2,22 @@ use Illuminate\Support\Str; +/* +|-------------------------------------------------------------------------- +| Session Cookie Name +|-------------------------------------------------------------------------- +| +| Kept from earlier releases, where it differs from the framework's +| "_session": renaming the cookie would sign everyone out. Every +| other key comes from the framework's defaults. +| +*/ + return [ - /* - |-------------------------------------------------------------------------- - | Default Session Driver - |-------------------------------------------------------------------------- - | - | This option determines the default session driver that is utilized for - | incoming requests. Laravel supports a variety of storage options to - | persist session data. Database storage is a great default choice. - | - | Supported: "file", "cookie", "database", "memcached", - | "redis", "dynamodb", "array" - | - */ - - 'driver' => env('SESSION_DRIVER', 'database'), - - /* - |-------------------------------------------------------------------------- - | Session Lifetime - |-------------------------------------------------------------------------- - | - | Here you may specify the number of minutes that you wish the session - | to be allowed to remain idle before it expires. If you want them - | to expire immediately when the browser is closed then you may - | indicate that via the expire_on_close configuration option. - | - */ - - 'lifetime' => (int) env('SESSION_LIFETIME', 120), - - 'expire_on_close' => env('SESSION_EXPIRE_ON_CLOSE', false), - - /* - |-------------------------------------------------------------------------- - | Session Encryption - |-------------------------------------------------------------------------- - | - | This option allows you to easily specify that all of your session data - | should be encrypted before it's stored. All encryption is performed - | automatically by Laravel and you may use the session like normal. - | - */ - - 'encrypt' => env('SESSION_ENCRYPT', false), - - /* - |-------------------------------------------------------------------------- - | Session File Location - |-------------------------------------------------------------------------- - | - | When utilizing the "file" session driver, the session files are placed - | on disk. The default storage location is defined here; however, you - | are free to provide another location where they should be stored. - | - */ - - 'files' => storage_path('framework/sessions'), - - /* - |-------------------------------------------------------------------------- - | Session Database Connection - |-------------------------------------------------------------------------- - | - | When using the "database" or "redis" session drivers, you may specify a - | connection that should be used to manage these sessions. This should - | correspond to a connection in your database configuration options. - | - */ - - 'connection' => env('SESSION_CONNECTION'), - - /* - |-------------------------------------------------------------------------- - | Session Database Table - |-------------------------------------------------------------------------- - | - | When using the "database" session driver, you may specify the table to - | be used to store sessions. Of course, a sensible default is defined - | for you; however, you're welcome to change this to another table. - | - */ - - 'table' => env('SESSION_TABLE', 'sessions'), - - /* - |-------------------------------------------------------------------------- - | Session Cache Store - |-------------------------------------------------------------------------- - | - | When using one of the framework's cache driven session backends, you may - | define the cache store which should be used to store the session data - | between requests. This must match one of your defined cache stores. - | - | Affects: "dynamodb", "memcached", "redis" - | - */ - - 'store' => env('SESSION_STORE'), - - /* - |-------------------------------------------------------------------------- - | Session Sweeping Lottery - |-------------------------------------------------------------------------- - | - | Some session drivers must manually sweep their storage location to get - | rid of old sessions from storage. Here are the chances that it will - | happen on a given request. By default, the odds are 2 out of 100. - | - */ - - 'lottery' => [2, 100], - - /* - |-------------------------------------------------------------------------- - | Session Cookie Name - |-------------------------------------------------------------------------- - | - | Here you may change the name of the session cookie that is created by - | the framework. Typically, you should not need to change this value - | since doing so does not grant a meaningful security improvement. - | - */ - 'cookie' => env( 'SESSION_COOKIE', Str::slug((string) env('APP_NAME', 'laravel')).'-session' ), - /* - |-------------------------------------------------------------------------- - | Session Cookie Path - |-------------------------------------------------------------------------- - | - | The session cookie path determines the path for which the cookie will - | be regarded as available. Typically, this will be the root path of - | your application, but you're free to change this when necessary. - | - */ - - 'path' => env('SESSION_PATH', '/'), - - /* - |-------------------------------------------------------------------------- - | Session Cookie Domain - |-------------------------------------------------------------------------- - | - | This value determines the domain and subdomains the session cookie is - | available to. By default, the cookie will be available to the root - | domain without subdomains. Typically, this shouldn't be changed. - | - */ - - 'domain' => env('SESSION_DOMAIN'), - - /* - |-------------------------------------------------------------------------- - | HTTPS Only Cookies - |-------------------------------------------------------------------------- - | - | By setting this option to true, session cookies will only be sent back - | to the server if the browser has a HTTPS connection. This will keep - | the cookie from being sent to you when it can't be done securely. - | - */ - - 'secure' => env('SESSION_SECURE_COOKIE'), - - /* - |-------------------------------------------------------------------------- - | HTTP Access Only - |-------------------------------------------------------------------------- - | - | Setting this value to true will prevent JavaScript from accessing the - | value of the cookie and the cookie will only be accessible through - | the HTTP protocol. It's unlikely you should disable this option. - | - */ - - 'http_only' => env('SESSION_HTTP_ONLY', true), - - /* - |-------------------------------------------------------------------------- - | Same-Site Cookies - |-------------------------------------------------------------------------- - | - | This option determines how your cookies behave when cross-site requests - | take place, and can be used to mitigate CSRF attacks. By default, we - | will set this value to "lax" to permit secure cross-site requests. - | - | See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#samesitesamesite-value - | - | Supported: "lax", "strict", "none", null - | - */ - - 'same_site' => env('SESSION_SAME_SITE', 'lax'), - - /* - |-------------------------------------------------------------------------- - | Partitioned Cookies - |-------------------------------------------------------------------------- - | - | Setting this value to true will tie the cookie to the top-level site for - | a cross-site context. Partitioned cookies are accepted by the browser - | when flagged "secure" and the Same-Site attribute is set to "none". - | - */ - - 'partitioned' => env('SESSION_PARTITIONED_COOKIE', false), - ]; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 1f62ce4..032c78d 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -36,16 +36,6 @@ class UserFactory extends Factory ]; } - /** - * Indicate that the model's email address should be unverified. - */ - public function unverified(): static - { - return $this->state(fn (array $attributes) => [ - 'email_verified_at' => null, - ]); - } - /** * Indicate that the user is an admin. */ diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index c077e84..3e15766 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -4,7 +4,6 @@ namespace Database\Seeders; use App\Models\Setting; use App\Models\User; -// use Illuminate\Database\Console\Seeds\WithoutModelEvents; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder @@ -14,8 +13,6 @@ class DatabaseSeeder extends Seeder */ public function run(): void { - // User::factory(10)->create(); - User::factory()->create([ 'name' => 'Test User', 'email' => 'test@example.com', diff --git a/docker-compose.yml b/docker-compose.yml index 4a85148..a01c17c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,3 +1,18 @@ +# What the app and the scheduler both need +x-environment: &environment + APP_KEY: ${APP_KEY:?Set APP_KEY in .env or environment} + APP_URL: ${APP_URL:-http://localhost} + APP_ENV: ${APP_ENV:-production} + APP_DEBUG: ${APP_DEBUG:-false} + DB_CONNECTION: ${DB_CONNECTION:-sqlite} + DB_HOST: ${DB_HOST:-} + DB_PORT: ${DB_PORT:-} + DB_DATABASE: ${DB_DATABASE:-/app/database/sqlite/database.sqlite} + DB_USERNAME: ${DB_USERNAME:-} + DB_PASSWORD: ${DB_PASSWORD:-} + LOG_CHANNEL: ${LOG_CHANNEL:-stderr} + LOG_LEVEL: ${LOG_LEVEL:-warning} + services: app: image: gitea.nonameweb.ch/nonameweb/sealshare:latest @@ -15,20 +30,9 @@ services: - caddy_data:/data - caddy_config:/config environment: - APP_KEY: ${APP_KEY:?Set APP_KEY in .env or environment} - APP_URL: ${APP_URL:-http://localhost} - APP_ENV: ${APP_ENV:-production} - APP_DEBUG: ${APP_DEBUG:-false} + <<: *environment AUTO_HTTPS: ${AUTO_HTTPS:-false} SERVER_NAME: ${SERVER_NAME:-localhost} - DB_CONNECTION: ${DB_CONNECTION:-sqlite} - DB_HOST: ${DB_HOST:-} - DB_PORT: ${DB_PORT:-} - DB_DATABASE: ${DB_DATABASE:-/app/database/sqlite/database.sqlite} - DB_USERNAME: ${DB_USERNAME:-} - DB_PASSWORD: ${DB_PASSWORD:-} - LOG_CHANNEL: ${LOG_CHANNEL:-stderr} - LOG_LEVEL: ${LOG_LEVEL:-warning} SESSION_DRIVER: ${SESSION_DRIVER:-database} QUEUE_CONNECTION: ${QUEUE_CONNECTION:-database} CACHE_STORE: ${CACHE_STORE:-database} @@ -60,19 +64,7 @@ services: volumes: - sealshare_storage:/app/storage/app - sealshare_database:/app/database/sqlite - environment: - APP_KEY: ${APP_KEY:?Set APP_KEY in .env or environment} - APP_URL: ${APP_URL:-http://localhost} - APP_ENV: ${APP_ENV:-production} - APP_DEBUG: ${APP_DEBUG:-false} - DB_CONNECTION: ${DB_CONNECTION:-sqlite} - DB_HOST: ${DB_HOST:-} - DB_PORT: ${DB_PORT:-} - DB_DATABASE: ${DB_DATABASE:-/app/database/sqlite/database.sqlite} - DB_USERNAME: ${DB_USERNAME:-} - DB_PASSWORD: ${DB_PASSWORD:-} - LOG_CHANNEL: ${LOG_CHANNEL:-stderr} - LOG_LEVEL: ${LOG_LEVEL:-warning} + environment: *environment depends_on: app: condition: service_healthy diff --git a/docker/dev-entrypoint.sh b/docker/dev-entrypoint.sh index f15a8f9..f3dae14 100755 --- a/docker/dev-entrypoint.sh +++ b/docker/dev-entrypoint.sh @@ -3,16 +3,6 @@ set -e cd /app -# Generate PHP ini from environment variables (with defaults) -echo "[dev] Configuring PHP settings..." -cat > /usr/local/etc/php/conf.d/99-uploads.ini <&1 diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 354b8a0..19ee1d7 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -12,16 +12,6 @@ if [ -z "$APP_KEY" ]; then echo "[entrypoint] WARNING: Set this APP_KEY in your docker-compose.yml to persist across restarts!" fi -# Generate PHP ini from environment variables (with defaults) -echo "[entrypoint] Configuring PHP settings..." -cat > /usr/local/etc/php/conf.d/99-uploads.ini <=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/autoprefixer": { - "version": "10.6.1", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.6.1.tgz", - "integrity": "sha512-cL1Qz6ADZhcEbny/8HPfe99J6HhNoYtpX2LFLIbhgGE7Q1hlQVkYFdetDN7Id3KiQxhDrHwzlHr/YQCnZ8+xSA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.9", - "caniuse-lite": "^1.0.30001810", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.25", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.25.tgz", - "integrity": "sha512-gMmEShwwq7FJqMwvfRwvCl00v4kN+KOfJqXn+f4nrufak5gNHJOksd/60Dvjuz7sI8Y5WiSFBa8FEYr+zoyqCw==", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/browserslist": { - "version": "4.29.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.0.tgz", - "integrity": "sha512-3GSvyjvDI4Dur1Meg2BekJquu5uF+9R9a1+5M1Mde192eZoXbeXjzgOsgqPS2V8D5wrrip0gR5Hf/GhWQ9ZzaA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.23", - "caniuse-lite": "^1.0.30001810", - "electron-to-chromium": "^1.5.427", - "node-releases": "^2.0.55", - "update-browserslist-db": "^1.3.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001810", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz", - "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", @@ -445,44 +306,6 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/cliui": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", - "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", - "license": "ISC", - "dependencies": { - "string-width": "^7.2.0", - "strip-ansi": "^7.1.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/concurrently": { - "version": "10.0.5", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-10.0.5.tgz", - "integrity": "sha512-JaP/CoftUrCcAFW/g//RbgEGwlelnEae6cfBLgH6ZdO6s8jPkn6p9SB9u6pdVxYXoiSnFqseOlHfrEfF82TVOg==", - "license": "MIT", - "dependencies": { - "chalk": "5.6.2", - "rxjs": "7.8.2", - "shell-quote": "1.9.0", - "supports-color": "10.2.2", - "tree-kill": "1.2.2", - "yargs": "18.0.0" - }, - "bin": { - "conc": "dist/bin/index.js", - "concurrently": "dist/bin/index.js" - }, - "engines": { - "node": ">=22" - }, - "funding": { - "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" - } - }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", @@ -492,27 +315,6 @@ "node": ">=8" } }, - "node_modules/electron-to-chromium": { - "version": "1.5.432", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.432.tgz", - "integrity": "sha512-4qa5o+sI3qFR+p1C6wZvY99x6q/3tR34n+ezZvWiJ9s0HJaaKwN6h1HTupFfv4fiSdQYMZWtVFBuzHdAXo1BnA==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", - "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT" - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -530,19 +332,6 @@ } } }, - "node_modules/fraction.js": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", - "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -557,27 +346,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", - "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/laravel-vite-plugin": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-3.2.0.tgz", @@ -883,15 +651,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/node-releases": { - "version": "2.0.56", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.56.tgz", - "integrity": "sha512-x0InOIyzgdk+eyaWaRJFH5snEtiImgBgblZ2CyPrLmqqcuMQkEvcDPHbzqbD8eDsSeJbVOjn+crzyzHaM4D+/A==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -967,12 +726,6 @@ "node": "^10 || ^12 || >=14" } }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, "node_modules/readdirp": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", @@ -1020,27 +773,6 @@ "@rolldown/binding-win32-x64-msvc": "1.2.9" } }, - "node_modules/rxjs": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", - "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.1.0" - } - }, - "node_modules/shell-quote": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.10.0.tgz", - "integrity": "sha512-w1aiOKwKuRgtwAReIIj89puqg+I7GvX4IbLrvmhXbzQsj1+Zwi4VO3+fa6ZF91TWSjIxoEkKnMeHcLEODK5ZXA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -1050,50 +782,6 @@ "node": ">=0.10.0" } }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/supports-color": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", - "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, "node_modules/tinyglobby": { "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", @@ -1110,51 +798,6 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tree-kill": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "license": "MIT", - "bin": { - "tree-kill": "cli.js" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/update-browserslist-db": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.3.tgz", - "integrity": "sha512-pJ2sYawQS0R/WI928Gj5GlPhTGzbMelq0+4INtSYNDV9ErKJcX6xjGWkoG/VnB3dpUm00zALaqkrUD77pO5TDQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/vite": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", @@ -1253,58 +896,6 @@ "funding": { "url": "https://github.com/sponsors/jonschlinkert" } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", - "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "18.0.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", - "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", - "license": "MIT", - "dependencies": { - "cliui": "^9.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "string-width": "^7.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^22.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } - }, - "node_modules/yargs-parser": { - "version": "22.0.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", - "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", - "license": "ISC", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=23" - } } } } diff --git a/package.json b/package.json index 0e5f795..62c5dc8 100644 --- a/package.json +++ b/package.json @@ -8,17 +8,12 @@ "dev": "vite" }, "dependencies": { - "autoprefixer": "^10.6.1", - "concurrently": "^10.0.5", "laravel-vite-plugin": "^3.2.0", "vite": "^8.3.0" }, "optionalDependencies": { "lightningcss-linux-x64-gnu": "^1.29.1" }, - "overrides": { - "shell-quote": "^1.9.0" - }, "devDependencies": { "chokidar": "^5.0.0", "playwright": "^1.63.0" diff --git a/phpunit.xml b/phpunit.xml index 602e5cf..c7d992b 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -21,18 +21,18 @@ - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/resources/js/share-uploader.js b/resources/js/share-uploader.js index 24249c7..1780698 100644 --- a/resources/js/share-uploader.js +++ b/resources/js/share-uploader.js @@ -202,10 +202,11 @@ document.addEventListener('alpine:init', () => { xhr.setRequestHeader('Content-Type', 'application/octet-stream') xhr.setRequestHeader('Accept', 'application/json') xhr.setRequestHeader('X-CSRF-TOKEN', csrfToken) + xhr.responseType = 'json' xhr.upload.onprogress = (event) => onProgress(event.loaded) xhr.onload = () => { this.request = null - resolve({ status: xhr.status, uploadedChunks: parseUploadedChunks(xhr.responseText) }) + resolve({ status: xhr.status, uploadedChunks: xhr.response?.uploaded_chunks }) } xhr.onerror = () => { this.request = null @@ -307,11 +308,3 @@ function chunkNonce(prefix, index, isLast) { return nonce } - -function parseUploadedChunks(responseText) { - try { - return JSON.parse(responseText).uploaded_chunks - } catch { - return undefined - } -} diff --git a/resources/views/components/app-logo-icon.blade.php b/resources/views/components/app-logo-icon.blade.php deleted file mode 100644 index f3fbe92..0000000 --- a/resources/views/components/app-logo-icon.blade.php +++ /dev/null @@ -1,7 +0,0 @@ - - {{-- Document outline with folded corner --}} - - - {{-- Upload arrow --}} - - diff --git a/resources/views/livewire/admin/admin-settings.blade.php b/resources/views/livewire/admin/admin-settings.blade.php index f264936..71dbdff 100644 --- a/resources/views/livewire/admin/admin-settings.blade.php +++ b/resources/views/livewire/admin/admin-settings.blade.php @@ -161,14 +161,7 @@ wire:model="defaultExpiration" :label="__('Default Expiration')" :placeholder="$allowNeverExpire ? __('None') : null" - :options="[ - ['id' => '1h', 'name' => __('1 Hour')], - ['id' => '24h', 'name' => __('24 Hours')], - ['id' => '48h', 'name' => __('48 Hours')], - ['id' => '7d', 'name' => __('7 Days')], - ['id' => '14d', 'name' => __('14 Days')], - ['id' => '30d', 'name' => __('30 Days')], - ]" + :options="collect(\App\Models\Share::EXPIRATIONS)->map(fn (array $option, string $id): array => ['id' => $id, 'name' => __($option['label'])])->values()->all()" /> - - - - @if (session('status') == 'verification-link-sent') - - {{ __('A new verification link has been sent to the email address you provided during registration.') }} - - @endif - - - - @csrf - - - - - - {{-- Log out posts elsewhere, so it is a form of its own; it sits under Resend at the same end - edge, the card's two actions end-aligned below its content. --}} - - @csrf - - - - - - - diff --git a/resources/views/pages/settings/⚡profile.blade.php b/resources/views/pages/settings/⚡profile.blade.php index 2ecafee..bef2a15 100644 --- a/resources/views/pages/settings/⚡profile.blade.php +++ b/resources/views/pages/settings/⚡profile.blade.php @@ -1,12 +1,7 @@ validate($this->profileRules($user->id)); - $user->fill($validated); - - if ($user->isDirty('email')) { - $user->email_verified_at = null; - } - - $user->save(); + $user->fill($validated)->save(); $this->dispatch('profile-updated', name: $user->name); $this->success(__('Saved.')); } - - /** - * Send an email verification notification to the current user. - */ - public function resendVerificationNotification(): void - { - $user = Auth::user(); - - if ($user->hasVerifiedEmail()) { - $this->redirectIntended(default: route('admin.dashboard', absolute: false)); - - return; - } - - $user->sendEmailVerificationNotification(); - - Session::flash('status', 'verification-link-sent'); - } - - #[Computed] - public function hasUnverifiedEmail(): bool - { - return Auth::user() instanceof MustVerifyEmail && ! Auth::user()->hasVerifiedEmail(); - } - - #[Computed] - public function showDeleteUser(): bool - { - return ! Auth::user() instanceof MustVerifyEmail - || (Auth::user() instanceof MustVerifyEmail && Auth::user()->hasVerifiedEmail()); - } }; ?> - - - - @if ($this->hasUnverifiedEmail) -

- {{ __('Your email address is unverified.') }} - - -

- - @if (session('status') === 'verification-link-sent') - {{ __('A new verification link has been sent to your email address.') }} - @endif - @endif -
+ @@ -108,8 +50,6 @@ new class extends Component {
- @if ($this->showDeleteUser) - - @endif +
diff --git a/routes/console.php b/routes/console.php index 61f85d1..36974a6 100644 --- a/routes/console.php +++ b/routes/console.php @@ -1,11 +1,5 @@ comment(Inspiring::quote()); -})->purpose('Display an inspiring quote'); - Schedule::command('shares:cleanup')->hourly(); diff --git a/routes/settings.php b/routes/settings.php index db0747f..31a15e4 100644 --- a/routes/settings.php +++ b/routes/settings.php @@ -7,9 +7,6 @@ Route::middleware(['auth'])->group(function () { Route::redirect('settings', 'settings/profile'); Route::livewire('settings/profile', 'pages::settings.profile')->name('profile.edit'); -}); - -Route::middleware(['auth', 'verified'])->group(function () { Route::livewire('settings/password', 'pages::settings.password')->name('user-password.edit'); Route::livewire('settings/appearance', 'pages::settings.appearance')->name('appearance.edit'); diff --git a/tests/Browser/BreakpointsTest.php b/tests/Browser/BreakpointsTest.php index 278603e..20b59e3 100644 --- a/tests/Browser/BreakpointsTest.php +++ b/tests/Browser/BreakpointsTest.php @@ -171,12 +171,6 @@ test('the two-factor challenge page holds at every breakpoint', function () { walkBreakpoints(ready(visit(route('two-factor.login', [], false))), 'button[type="submit"]'); }); -test('the email verification prompt holds at every breakpoint', function () { - $this->actingAs(User::factory()->unverified()->create()); - - walkBreakpoints(ready(visit(route('verification.notice', [], false)))); -}); - test('the password confirmation page holds at every breakpoint', function () { $this->actingAs(User::factory()->create()); diff --git a/tests/Feature/Admin/AdminSettingsTest.php b/tests/Feature/Admin/AdminSettingsTest.php index 9e281f1..8cc90c1 100644 --- a/tests/Feature/Admin/AdminSettingsTest.php +++ b/tests/Feature/Admin/AdminSettingsTest.php @@ -51,6 +51,23 @@ test('admin can save settings', function () { expect(Setting::get('default_expiration'))->toBe('7d'); }); +test('the default expiration only accepts the offered options', function () { + $admin = User::query()->where('is_admin', true)->first(); + + $component = Livewire::actingAs($admin) + ->test(AdminSettings::class) + ->set('defaultExpiration', '99y') + ->call('saveSettings') + ->assertHasErrors(['defaultExpiration' => 'in']); + + expect(Setting::get('default_expiration'))->toBeNull(); + + $component + ->set('defaultExpiration', '') + ->call('saveSettings') + ->assertHasNoErrors(); +}); + test('admin can set system password', function () { $admin = User::query()->where('is_admin', true)->first(); Livewire::actingAs($admin) diff --git a/tests/Feature/Auth/EmailVerificationTest.php b/tests/Feature/Auth/EmailVerificationTest.php deleted file mode 100644 index fa034ac..0000000 --- a/tests/Feature/Auth/EmailVerificationTest.php +++ /dev/null @@ -1,67 +0,0 @@ -unverified()->create(); - - $response = $this->actingAs($user)->get(route('verification.notice')); - - $response->assertOk(); -}); - -test('email can be verified', function () { - $user = User::factory()->unverified()->create(); - - Event::fake(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1($user->email)] - ); - - $response = $this->actingAs($user)->get($verificationUrl); - - Event::assertDispatched(Verified::class); - - expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); - $response->assertRedirect(route('admin.dashboard', absolute: false).'?verified=1'); -}); - -test('email is not verified with invalid hash', function () { - $user = User::factory()->unverified()->create(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1('wrong-email')] - ); - - $this->actingAs($user)->get($verificationUrl); - - expect($user->fresh()->hasVerifiedEmail())->toBeFalse(); -}); - -test('already verified user visiting verification link is redirected without firing event again', function () { - $user = User::factory()->create([ - 'email_verified_at' => now(), - ]); - - Event::fake(); - - $verificationUrl = URL::temporarySignedRoute( - 'verification.verify', - now()->addMinutes(60), - ['id' => $user->id, 'hash' => sha1($user->email)] - ); - - $this->actingAs($user)->get($verificationUrl) - ->assertRedirect(route('admin.dashboard', absolute: false).'?verified=1'); - - expect($user->fresh()->hasVerifiedEmail())->toBeTrue(); - Event::assertNotDispatched(Verified::class); -}); diff --git a/tests/Feature/FileUploadTest.php b/tests/Feature/FileUploadTest.php index e4304ca..095630a 100644 --- a/tests/Feature/FileUploadTest.php +++ b/tests/Feature/FileUploadTest.php @@ -249,6 +249,21 @@ test('file upload with expiration sets expires_at', function () { expect($share->expires_at)->not->toBeNull(); }); +test('a 30 day expiration lasts 30 days, not a calendar month', function () { + Storage::fake('shares'); + $this->travelTo(new DateTimeImmutable('2026-02-01 12:00:00')); + + $component = Livewire::test(FileUploader::class); + uploadThroughPage($component, ['file.txt' => 'content']); + + $component + ->set('expiration', '30d') + ->call('createShare') + ->assertRedirectContains('/share/'); + + expect(Share::query()->first()->expires_at->toDateTimeString())->toBe('2026-03-03 12:00:00'); +}); + test('file upload with max downloads sets limit', function () { Storage::fake('shares'); diff --git a/tests/Feature/PageTemplateTest.php b/tests/Feature/PageTemplateTest.php index 8099720..ab18d70 100644 --- a/tests/Feature/PageTemplateTest.php +++ b/tests/Feature/PageTemplateTest.php @@ -41,7 +41,6 @@ test('every page is one page template, one h1 and the same width', function (Clo return $test->get(route('system-password')); }], - 'verify email' => [fn (TestCase $test): TestResponse => $test->actingAs(User::factory()->unverified()->create())->get(route('verification.notice'))], 'confirm password' => [fn (TestCase $test): TestResponse => $test->actingAs(User::factory()->create())->get(route('password.confirm'))], 'profile' => [fn (TestCase $test): TestResponse => $test->actingAs(User::factory()->create())->get(route('profile.edit'))], 'password' => [fn (TestCase $test): TestResponse => $test->actingAs(User::factory()->create())->get(route('user-password.edit'))], diff --git a/tests/Feature/Settings/ProfileUpdateTest.php b/tests/Feature/Settings/ProfileUpdateTest.php index 70f3788..4f8d07b 100644 --- a/tests/Feature/Settings/ProfileUpdateTest.php +++ b/tests/Feature/Settings/ProfileUpdateTest.php @@ -27,10 +27,9 @@ test('profile information can be updated', function () { expect($user->name)->toEqual('Test User'); expect($user->email)->toEqual('test@example.com'); - expect($user->email_verified_at)->toBeNull(); }); -test('email verification status is unchanged when email address is unchanged', function () { +test('the profile saves with its own unchanged email address', function () { $user = User::factory()->create(); $this->actingAs($user); @@ -42,7 +41,7 @@ test('email verification status is unchanged when email address is unchanged', f $response->assertHasNoErrors(); - expect($user->refresh()->email_verified_at)->not->toBeNull(); + expect($user->refresh()->name)->toEqual('Test User'); }); test('user can delete their account', function () { diff --git a/tests/Feature/ShareDownloadTest.php b/tests/Feature/ShareDownloadTest.php index a6f8be8..b03c95e 100644 --- a/tests/Feature/ShareDownloadTest.php +++ b/tests/Feature/ShareDownloadTest.php @@ -3,7 +3,6 @@ use App\Livewire\ShareDownload; use App\Models\Share; use App\Models\ShareFile; -use App\Services\FileEncryptionService; use App\Services\ShareService; use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Storage; @@ -280,7 +279,7 @@ test('a password share created before key wrapping still unlocks and downloads', $source = tempnam(sys_get_temp_dir(), 'old'); file_put_contents($source, 'old content'); Storage::disk('shares')->makeDirectory($share->token); - app(FileEncryptionService::class)->encryptFile($source, Storage::disk('shares')->path($share->token.'/old.enc'), bin2hex(hash_pbkdf2('sha256', 'old-password', hex2bin($salt), 100000, 32, true)), 1024); + encryptTestFile($source, Storage::disk('shares')->path($share->token.'/old.enc'), bin2hex(hash_pbkdf2('sha256', 'old-password', hex2bin($salt), 100000, 32, true)), 1024); unlink($source); Livewire::test(ShareDownload::class, ['share' => $share]) diff --git a/tests/Pest.php b/tests/Pest.php index b86c146..ba6fd01 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -26,21 +26,6 @@ pest()->extend(TestCase::class) }) ->in('Feature', 'Browser', 'Screenshots'); -/* -|-------------------------------------------------------------------------- -| Expectations -|-------------------------------------------------------------------------- -| -| When you're writing tests, you often need to check that values meet certain conditions. The -| "expect()" function gives you access to a set of "expectations" methods that you can use -| to assert different things. Of course, you may extend the Expectation API at any time. -| -*/ - -expect()->extend('toBeOne', function () { - return $this->toBe(1); -}); - /* |-------------------------------------------------------------------------- | Functions @@ -52,11 +37,6 @@ expect()->extend('toBeOne', function () { | */ -function something() -{ - // .. -} - /** * A page of SealShare, once it can be used: loaded, with Alpine and Livewire started. Shared by * every file under tests/Browser, so a browser test needs no visit() of its own to define it. @@ -77,3 +57,22 @@ function encryptedChunk(ShareFile $file, string $plaintext, int $index, bool $is return $encryption->encryptChunk($plaintext, $file->share->encryption_key, $header['noncePrefix'], $index, $isLast); } + +/** + * Encrypt a whole file in the SEALCHK2 format, as the uploader's browser does chunk by chunk. + */ +function encryptTestFile(string $sourcePath, string $destinationPath, string $key, int $chunkSize): void +{ + $encryption = new FileEncryptionService; + $header = $encryption->createHeader($chunkSize); + $noncePrefix = $encryption->parseHeader($header)['noncePrefix']; + $plaintext = (string) file_get_contents($sourcePath); + $chunkCount = $encryption->chunkCount(strlen($plaintext), $chunkSize); + + $chunks = array_map( + fn (int $index): string => $encryption->encryptChunk(substr($plaintext, $index * $chunkSize, $chunkSize), $key, $noncePrefix, $index, $index === $chunkCount - 1), + range(0, $chunkCount - 1), + ); + + file_put_contents($destinationPath, $header.implode('', $chunks)); +} diff --git a/tests/Unit/FileEncryptionServiceTest.php b/tests/Unit/FileEncryptionServiceTest.php index 2f7219c..7755e5a 100644 --- a/tests/Unit/FileEncryptionServiceTest.php +++ b/tests/Unit/FileEncryptionServiceTest.php @@ -45,7 +45,7 @@ test('encrypt and decrypt round-trip works', function () { $key = $this->service->generateRandomKey(); - $this->service->encryptFile($sourcePath, $encryptedPath, $key, 1024); + encryptTestFile($sourcePath, $encryptedPath, $key, 1024); expect(file_get_contents($encryptedPath))->not->toContain($content); expect(decryptToString($this->service, $encryptedPath, $key))->toBe($content); @@ -57,14 +57,14 @@ test('decrypt with wrong key fails', function () { file_put_contents($sourcePath, 'Secret data'); - $this->service->encryptFile($sourcePath, $encryptedPath, $this->service->generateRandomKey(), 1024); + encryptTestFile($sourcePath, $encryptedPath, $this->service->generateRandomKey(), 1024); decryptToString($this->service, $encryptedPath, $this->service->generateRandomKey()); })->throws(RuntimeException::class, 'Decryption failed'); test('derive key produces consistent results', function () { $password = 'my-secure-password'; - $salt = $this->service->generateSalt(); + $salt = bin2hex(random_bytes(32)); $key1 = $this->service->deriveKey($password, $salt); $key2 = $this->service->deriveKey($password, $salt); @@ -73,7 +73,7 @@ test('derive key produces consistent results', function () { }); test('derive key with different passwords produces different keys', function () { - $salt = $this->service->generateSalt(); + $salt = bin2hex(random_bytes(32)); $key1 = $this->service->deriveKey('password1', $salt); $key2 = $this->service->deriveKey('password2', $salt); @@ -84,8 +84,8 @@ test('derive key with different passwords produces different keys', function () test('derive key with different salts produces different keys', function () { $password = 'same-password'; - $key1 = $this->service->deriveKey($password, $this->service->generateSalt()); - $key2 = $this->service->deriveKey($password, $this->service->generateSalt()); + $key1 = $this->service->deriveKey($password, bin2hex(random_bytes(32))); + $key2 = $this->service->deriveKey($password, bin2hex(random_bytes(32))); expect($key1)->not->toBe($key2); }); @@ -97,13 +97,6 @@ test('generate random key returns 64 char hex string', function () { expect(ctype_xdigit($key))->toBeTrue(); }); -test('generate salt returns 64 char hex string', function () { - $salt = $this->service->generateSalt(); - - expect(strlen($salt))->toBe(64); - expect(ctype_xdigit($salt))->toBeTrue(); -}); - test('password-derived key encrypt/decrypt round-trip works', function () { $sourcePath = $this->tempDir.'/source.txt'; $encryptedPath = $this->tempDir.'/encrypted.enc'; @@ -111,9 +104,9 @@ test('password-derived key encrypt/decrypt round-trip works', function () { file_put_contents($sourcePath, $content); - $key = bin2hex($this->service->deriveKey('user-password', $this->service->generateSalt())); + $key = bin2hex($this->service->deriveKey('user-password', bin2hex(random_bytes(32)))); - $this->service->encryptFile($sourcePath, $encryptedPath, $key, 1024); + encryptTestFile($sourcePath, $encryptedPath, $key, 1024); expect(decryptToString($this->service, $encryptedPath, $key))->toBe($content); }); @@ -124,7 +117,7 @@ test('an encrypted file starts with the SEALCHK2 header and its chunk size', fun file_put_contents($sourcePath, 'test content'); - $this->service->encryptFile($sourcePath, $encryptedPath, $this->service->generateRandomKey(), 1024); + encryptTestFile($sourcePath, $encryptedPath, $this->service->generateRandomKey(), 1024); expect(file_get_contents($encryptedPath, false, null, 0, 12))->toBe('SEALCHK2'.pack('N', 1024)); }); @@ -137,7 +130,7 @@ test('multi-chunk round-trip works', function () { file_put_contents($sourcePath, $content); $key = $this->service->generateRandomKey(); - $this->service->encryptFile($sourcePath, $encryptedPath, $key, 1000); + encryptTestFile($sourcePath, $encryptedPath, $key, 1000); expect(filesize($encryptedPath))->toBe(19 + 3 * 16 + 2500); expect(decryptToString($this->service, $encryptedPath, $key))->toBe($content); @@ -151,7 +144,7 @@ test('exact chunk boundary round-trip works', function () { file_put_contents($sourcePath, $content); $key = $this->service->generateRandomKey(); - $this->service->encryptFile($sourcePath, $encryptedPath, $key, 1000); + encryptTestFile($sourcePath, $encryptedPath, $key, 1000); expect(filesize($encryptedPath))->toBe(19 + 2 * 16 + 2000); expect(decryptToString($this->service, $encryptedPath, $key))->toBe($content); @@ -164,7 +157,7 @@ test('empty file round-trip works', function () { file_put_contents($sourcePath, ''); $key = $this->service->generateRandomKey(); - $this->service->encryptFile($sourcePath, $encryptedPath, $key, 1000); + encryptTestFile($sourcePath, $encryptedPath, $key, 1000); expect(filesize($encryptedPath))->toBe(19 + 16); expect(decryptToString($this->service, $encryptedPath, $key))->toBe(''); @@ -197,7 +190,7 @@ test('a file cut short at a chunk boundary fails to decrypt', function () { file_put_contents($sourcePath, random_bytes(3000)); $key = $this->service->generateRandomKey(); - $this->service->encryptFile($sourcePath, $encryptedPath, $key, 1000); + encryptTestFile($sourcePath, $encryptedPath, $key, 1000); $handle = fopen($encryptedPath, 'r+b'); ftruncate($handle, 19 + 2 * (1000 + 16)); @@ -265,7 +258,7 @@ test('wrong key on chunked file throws exception', function () { file_put_contents($sourcePath, random_bytes(2500)); - $this->service->encryptFile($sourcePath, $encryptedPath, $this->service->generateRandomKey(), 1000); + encryptTestFile($sourcePath, $encryptedPath, $this->service->generateRandomKey(), 1000); decryptToString($this->service, $encryptedPath, $this->service->generateRandomKey()); })->throws(RuntimeException::class, 'Decryption failed'); diff --git a/website/img/logo.svg b/website/img/logo.svg index 6e6ba80..a60ce21 100644 --- a/website/img/logo.svg +++ b/website/img/logo.svg @@ -1,5 +1,5 @@ - +