Commit Graph

38 Commits (b5760285aa3eb43e6a0b07a4ff6bc3436142257d)

Author SHA1 Message Date
Your Name 163cd9f589 polish 1 week ago
Your Name c9c897c4e7 Fix stale-CSRF login recovery render callback 2 weeks ago
Daniel Supernault 9e69d449d5
Update NotificationService 2 weeks ago
Your Name 42620e584b Upgrade to Laravel 13
- laravel/framework ^12.0 -> ^13.0
- spatie/laravel-backup ^9.2.9 -> ^10.0 (forced: 9.x pins
  illuminate/notifications ^12.40, incompatible with L13)
- Drop psalm/plugin-laravel + vimeo/psalm (dev-only static analysis):
  the only version chain compatible with L13's testbench-core needs
  vimeo/psalm ^7.0.0-beta, which requires narrowing the project's
  declared PHP floor (composer platform.php is pinned to 8.3.0 to
  keep composer.lock installable on the oldest supported PHP patch;
  the psalm 7 betas require specific 8.3.16+/8.4.3+/8.5.0+ floors).
  Its CI workflow (.github/workflows/php-psalm.yml) was already
  disabled (`on: []`, "too many errors"). Larastan/PHPStan remains
  as the project's static analysis tool, unaffected.
- Rename VerifyCsrfToken/ValidateCsrfToken -> PreventRequestForgery
  in bootstrap/app.php and config/sanctum.php (the L13 rename; old
  classes remain as deprecated aliases but new code should reference
  the new name), and validateCsrfTokens() -> preventRequestForgery()
  in the middleware config.

Everything else (cache serializable_classes, cache/session/redis key
prefixes, upsert() uniqueBy, JobAttempted/QueueBusy event properties,
pagination view names, Manager::extend bindings, model-boot nested
instantiation) was checked against the app's actual code and found
to be either already handled, already using the new convention, or
not applicable to any pattern in this codebase.

All 715 tests pass (verified against a clean baseline with Redis
available locally via Docker); Pint and Larastan (the project's
configured `composer analyse` scope) are both clean.
2 weeks ago
Your Name 1a5274954d polish 2 weeks ago
Your Name 9b6eea0664 polish 2 weeks ago
Your Name cc183ec8ab Move scheduled tasks file from routes/ to bootstrap/
The schedule definitions are bootstrap wiring, not route definitions, so
bootstrap/scheduledtasks.php is a better home. Updated the require path in
bootstrap/app.php. Verified with schedule:list.
2 weeks ago
Your Name 61c087e560 Extract scheduled tasks into routes/scheduledtasks.php
Move the schedule definitions out of the withSchedule() closure in
bootstrap/app.php into a dedicated routes/scheduledtasks.php, required with
the Schedule instance in scope. Behavior-preserving; verified with
schedule:list.
2 weeks ago
Your Name 4f284e0893 Gate storage reconciler schedule behind a disabled-by-default flag
The upload/delete/read paths now self-heal stale storage_used counters and
the upgrade backfill migration repairs existing accounts, so the weekly
reconciler is no longer required. Gate it behind pixelfed.account_storage_
reconcile (ACCOUNT_STORAGE_RECONCILE), defaulting off, so operators can opt
in to the background hygiene job without editing source.
2 weeks ago
Your Name 28573e863f Run storage recalculate reconciler weekly instead of daily
Now that the upload/delete hot path and get() self-heal stale counters, the
scheduled reconciler is a background drift safety net rather than the primary
unblock mechanism, so weekly is sufficient.
2 weeks ago
Your Name 6496904293 Fix account storage limit not freeing on media deletion (#7169)
users.storage_used only ever grew: uploads incremented it but no deletion
path decremented it, so users hit the account size limit even when their
real media usage was well below it.

- Decrement storage_used in MediaDeletePipeline when media is removed
- Add UserStorageService::increaseStorageUsed / decrementStorageUsed as the
  fast, symmetric hot-path counter updates (floor-based, clamped at zero)
- Refactor the 6 upload call sites to use increaseStorageUsed instead of
  duplicated inline writes (also fixes ceil/floor drift vs the reconciler)
- Add (user_id, size) covering index so per-user SUM(size) is not a full
  table scan (INPLACE/LOCK=NONE, skipped on sqlite)
- Add user:storage:recalculate command to repair affected accounts, with a
  daily --stale=168 scheduled reconciler to correct any drift
- Add regression tests for the pipeline and UserStorageService
2 weeks ago
Daniel Supernault 1d96c94054
Refactor Auth, remove expensive middleware 2 weeks ago
Daniel Supernault ac34213121
Fix typo 2 weeks ago
Daniel Supernault f37c5fc95c
Add notification gc 2 weeks ago
Shlee 5cc0a68ffe
Update app.php 3 weeks ago
Daniel Supernault 584ce27f71
Add Sanctum support 3 weeks ago
Your Name 400f00c5e1 feat: storage:maintenance command + in-flow cleanup of emptied dirs
Replace the remcache GC (GarbageCollectorRemcache / gc:remcache) with a
broader storage:maintenance command that sweeps stale remcache temp files and
recursively removes the random empty directories accumulated under the media,
story, avatar and import trees (--hours/--only/--except/--dry-run), scheduled
daily.

Fix the root causes so flows clean up after themselves rather than relying on
the sweep:
- MediaDeletePipeline removes its own emptied m/_v2 leaf dir
- AvatarOptimize logs the previously-swallowed exception, still cleans up the
  old avatar on failure, and removes the old file's now-empty splayed dir
- AvatarController::deleteAvatar removes the emptied splayed dir
- StoryExpire/StoryDelete remove the story's own emptied leaf dir
- TransformImports removes imports/{userId} once its files are moved out
- StoryFetch cleans up its remcache temp file in a finally block
3 weeks ago
Your Name 3c6ba88e6e chore: modernize service providers for Laravel 13 readiness
Remove deprecated Foundation\Support\Providers\AuthServiceProvider and
EventServiceProvider base classes. Move policy and event listener
registrations into AppServiceProvider using Gate::policy() and
Event::listen(). Behavior is unchanged (verified via event:list and
auth test suite).
4 weeks ago
Your Name 40b323bca4 revert: remove emoji local-to-cloud storage changes
Back out all emoji cloud-storage work from staging so it can be reworked and
re-landed separately (the URL resolution flips to cloud on a global config
flag, which created a broken-URL window, and the migration approach needs
revisiting).

Reverts to pre-emoji state:
- CustomEmoji model URL/storage helpers (urlForPath, storageTarget, storeMedia,
  storeMediaFromFile, deleteMedia, url) and callers in ImportEmojis,
  CustomEmojiService, AdminController
- admin custom-emoji blade views back to local /storage URLs
- Remove admin:EmojiMoveStorageLocalToCloud command
- Remove the deploy migration and its scheduler entry

Media (and the already-reverted story) scheduler entries are untouched.
4 weeks ago
Your Name 9f55f7204c revert: story cloud-migration work
Back out the story local->cloud migration so we can land and verify the
emoji cloud work first, one change at a time.

Reverts:
- 9f110bb74 feat: migrate local story media to cloud storage
- 842681b99 fix: schedule StoryMoveStorageLocalToCloud command

Removes StoryMoveStorageLocalToCloud command, its scheduler entry, and the
StoryExpire explicit-disk changes. Remcache and emoji work are untouched.
Will revisit story once emoji is confirmed.
4 weeks ago
Your Name 842681b99b fix: schedule StoryMoveStorageLocalToCloud command
The story cloud-migration command was merged (#6958) but its scheduler
entry was dropped when the emoji branch (based off staging before that
merge) later merged and overwrote the scheduler block. Restore the hourly
schedule so local story media is migrated to cloud automatically.
4 weeks ago
Your Name 979df6e39e feat: migrate all local emoji to cloud in one pass by default
Change --limit default to 0 (no limit) so the emoji migration processes
every local emoji in a single run instead of capping at 1000, and drop the
limit from the scheduled invocation. Avoids a multi-run window where
not-yet-migrated emoji resolve to missing cloud URLs.
4 weeks ago
Your Name fa76e1014a feat: store custom emoji on cloud storage when enabled
Custom emoji were always written locally and served via hardcoded /storage
URLs, so they never used S3 even on cloud instances.

- CustomEmoji: centralize URL + storage on the active disk (cloud when
  pixelfed.cloud_storage is enabled, else local public/ disk) via
  urlForPath/url/storageTarget/storeMedia/storeMediaFromFile/deleteMedia
- Route emoji writes/deletes and URL generation (scan, CustomEmojiService::all)
  through those helpers in ImportEmojis, CustomEmojiService::import and
  AdminController
- Add admin:EmojiMoveStorageLocalToCloud to migrate existing local emoji to
  cloud: copy, verify by size, delete local, bust caches
- Schedule it daily when cloud storage is enabled
4 weeks ago
Your Name 9f110bb74c feat: migrate local story media to cloud storage
Ensure story media lands on and stays on cloud storage for S3 instances.

- StoryExpire: archive expiring story media on the same explicit disk the
  media lives on (S3 move is a server-side copy+delete), with error handling
- Add admin:StoryMoveStorageLocalToCloud to migrate local story media
  (active + story_archives) to cloud: copy, verify by size, then delete local
- --orphans option relocates untracked story_archives/ files to cloud using
  the same copy/verify/delete flow (media is moved, never discarded)
- Schedule it hourly alongside the media migration when cloud storage is on
4 weeks ago
Your Name 3232761a74 fix: prevent remcache temp file leaks and add GC command
The remote avatar/media fetchers wrote temp files to storage/app/remcache/
and only unlinked them on the happy path. Any exception between the write
and the unlink (e.g. a cloud upload failure) leaked the file, and nothing
swept the directory.

- Wrap post-write logic in fetchAvatar() and remoteToCloud() in try/finally
  so the temp file is always removed, even on failure
- Add gc:remcache command to delete stale remcache files (default >24h old,
  preserves .gitignore, supports --hours and --dry-run)
- Schedule gc:remcache daily to clean up any stragglers

StoryFetch already handled cleanup via try/catch and was left unchanged.
4 weeks ago
Your Name 6ff9ffbbb8 Add media storage migration commands (local<->cloud) with integrated GC
Add admin:MediaMoveStorageLocalToCloud and admin:MediaMoveStorageCloudToLocal:
- Copy media (+thumbnail) between local and cloud disks, verify by size (and
  sha256 against original_sha256 when present) before deleting the source.
- Integrated GC: delete the verified source copy (local on upload, cloud on
  download), set version=4 / reset to 3, and bust MediaService/StatusService
  caches. --keep-local / --keep-cloud opt out.
- Manage PF_ENABLE_CLOUD in .env AND the live runtime + config cache so new
  uploads route to the correct backend mid-migration on a hot server. Uses the
  installer's atomic .env writer (shared ManagesMediaStorageEnv trait).
- --limit / --dry-run / --force.

Replaces media:migrate2cloud (CloudMediaMigrate) and media:s3gc
(MediaS3GarbageCollector); scheduler now runs MediaMoveStorageLocalToCloud
hourly for straggler upload + GC. Keeps media:fix-nonlocal-driver.

Adds feature tests (download+GC, --keep-cloud, dry-run, env-flag flip both
directions, unknown-disk guard).
4 weeks ago
Your Name a142db87b4 polish 4 weeks ago
Shlee 46393bd9fc
Move ValidateCsrfToken middleware to a new position 4 weeks ago
Shlee 2c704d9a76
Update app.php 4 weeks ago
dansup 90af7d8a2d
Merge pull request #6879 from pixelfed/fix/csrf-middleware-rename
Laravel 13 prep: rename VerifyCsrfToken to PreventRequestForgery
4 weeks ago
Your Name 9958b095dd refactor: rename VerifyCsrfToken to PreventRequestForgery
Prepares for Laravel 13 where VerifyCsrfToken is deprecated in favor
of PreventRequestForgery. The old class remains as an alias in v13 but
will be removed in a future version.
4 weeks ago
Your Name ed90e619fb feat: add throttle:api middleware to the api route group
Adds a global rate limiter (240 req/min per user or IP) to all API
routes. Previously rate limiting was only applied ad-hoc on individual
routes, leaving some endpoints unprotected.
4 weeks ago
Your Name 7a96cd2e91 fix: replace removed Passport scope middleware with current classes
Laravel Passport 13 renamed:
- CheckScopes → CheckToken (verifies ALL listed scopes)
- CheckForAnyScope → CheckTokenForAnyScope (verifies ANY listed scope)

The old class names no longer exist, causing BindingResolutionException
on all /api/v1/admin/* routes that use the 'scope' or 'scopes' middleware
aliases.
4 weeks ago
Your Name 8a2649b3ff feat: add critical path test suite and fix auth/config issues
Test Infrastructure:
- Modernize phpunit.xml (bootstrap, source block, Laravel 12 env vars)
- Configure tests/Pest.php with pest()->extend(TestCase::class)->in('Feature')
- Add docker-compose.test.yml (Redis for test suite)
- Add composer test/test:quick scripts
- Rename CACHE_DRIVER to CACHE_STORE across config (backwards compatible)
- Update .env.testing for in-memory SQLite + Docker Redis

Test Coverage (190 tests):
- CriticalRoutes: public routes, auth routes, API endpoints, middleware, schedule
- Auth/LoginTest: login, logout, rate limiting, redirect behavior
- Auth/RegisterTest: registration flow, validation, disabled registration
- Auth/PasswordResetTest: reset request, token validation, password update
- Auth/TwoFactorTest: 2FA checkpoint, setup behind password confirmation
- Auth/PasswordConfirmationTest: sudo mode flow via Laravel password.confirm
- Api/ScopeTest: scope enforcement, public endpoints, admin access

Bugs Fixed:
- Fix unauthenticated API returning 500 instead of 401 (AuthenticationException
  not handled in custom exception renderer in bootstrap/app.php)
- Replace custom DangerZone middleware with Laravel password.confirm
- Add HasFactory trait to Profile model for test factories

Bugs Documented (known-bugs group):
- Registration crashes with str_ends_with TypeError (RegisterController:82)
- OAuth routes use legacy array syntax causing ReflectionFunction TypeError
4 weeks ago
Your Name ee7d7124d0 refactor: remove thin middleware wrappers, use framework classes directly
Delete 4 middleware wrapper classes that added no custom logic:
- EncryptCookies (empty $except)
- TrimStrings ($except matches framework default)
- VerifyCsrfToken (exceptions moved to validateCsrfTokens() in bootstrap)
- TrustProxies (headers matched framework default)

CSRF exceptions (/api/v1/*, oauth/token) are now configured via
$middleware->validateCsrfTokens(except: [...]) in bootstrap/app.php.

All 107 tests pass.
4 weeks ago
Your Name 8e41f6fdf8 refactor: migrate to modern bootstrap/app.php architecture
Consolidate the legacy Laravel 5-era kernel/handler architecture into
the modern Application::configure() pattern introduced in Laravel 11:

- HTTP middleware stack → bootstrap/app.php withMiddleware()
- Console schedule → bootstrap/app.php withSchedule()
- Exception handling → bootstrap/app.php withExceptions()
- Route registration → bootstrap/app.php withRouting()
- Service providers → bootstrap/providers.php

Deleted files:
- app/Http/Kernel.php
- app/Console/Kernel.php
- app/Exceptions/Handler.php
- app/Providers/RouteServiceProvider.php
- app/Providers/BroadcastServiceProvider.php

Removed framework providers from config/app.php (auto-registered by
Application::configure). Package providers use auto-discovery.

All 107 tests pass. Schedule, routes, and middleware verified working.
4 weeks ago
Your Name 3b0fd708c8 Apply Pint formatting to bootstrap/ 4 weeks ago
Daniel Supernault a034358c98 Add Laravel Framework 9 years ago