Commit Graph

4485 Commits (fff81fe596070a2b6d5a0ae39840ecd67f03f2b3)

Author SHA1 Message Date
Shlee 7cf6e77148
Update Localization.php 4 weeks ago
Your Name 9db2218ca6 chore: move resources/lang to top-level lang/ per Laravel 9+ convention
- Relocate translation files from resources/lang to lang/ via git mv
- Update PHP references to use the lang_path() helper
- Update crowdin.yml source/translation paths
- Update phpstan.neon translationDirectories
4 weeks ago
Your Name b8ca4da3a6 Add per-file transfer output and --debug detail to MediaMoveStorageLocalToCloud 4 weeks ago
Your Name 6b14b229d1 Fix media storage migration crash when no .env file exists
The media storage migration commands read/parsed the .env file directly to
check and flip PF_ENABLE_CLOUD. In containerized deploys there is no .env on
disk (config is injected via env vars), so updateEnvFile() threw
'file_get_contents(.env): Failed to open stream' and the scheduled command
exited 1.

- Check the live setting via config_cache('pixelfed.cloud_storage') like the
  rest of the app, instead of parsing .env.
- Make the .env write best-effort in ManagesMediaStorageEnv: skip gracefully
  when the file is missing or read-only, and still apply the runtime + DB
  config-cache updates (the load-bearing changes on a hot server).
- Apply the same fix to the sibling unstable:MediaMoveStorageCloudToLocal.
- Add a regression test covering the no-.env container scenario.
4 weeks ago
Your Name ef7e485e7d Fix Larastan error: correct Status import in NotificationService
Use App\Models\Status instead of the non-existent App\Status class.
4 weeks ago
Shlee 003953eb3e
Merge pull request #7045 from pixelfed/perf/follower-service-following-ids
Deduplocation: add FollowerService::getFollowingIds for common function
4 weeks ago
Daniel Supernault c9b0ee3bdd
Refactor NotificationService 4 weeks ago
Shlee 9c7c33969a
Merge pull request #7044 from pixelfed/perf/delete-jobs-streaming
Stream deletions with cursor and batch notification lookups in delete jobs
4 weeks ago
Your Name 667f6e2fc9 Extract following-ids lookup into FollowerService::getFollowingIds
The Cache::remember('profile:following:'.$pid, ...) block that plucks
following_id and appends the caller's own id was copy-pasted across four
call sites, with inconsistent TTLs (1440 minutes vs 1209600 seconds).

Add FollowerService::getFollowingIds($pid), which owns the cache key that
add()/remove() already invalidate, and use it from InternalApiController,
PublicApiController, ApiV1Controller and HashtagUnfollowPipeline. Removes
the now-unused Follower/Cache imports left behind.

Adds a test covering the followed-ids-plus-self result and the
follows-nobody case.
4 weeks ago
Your Name c4e5b96d25 Stream deletions with cursor and batch notification lookups in delete jobs
The status- and account-deletion jobs loaded whole collections with
->get() and then looped, running a per-row Notification lookup inside
each iteration.

- StatusDelete / RemoteStatusDelete: resolve associated DirectMessage and
  MediaTag ids, fetch their notifications in a single whereIn query,
  clear each via cursor (NotificationService::del must run per row for
  cache/redis cleanup), then bulk delete the DMs and media tags.
- DeleteAccountPipeline / DeleteRemoteProfilePipeline: stream Story and
  Collection deletions with cursor() instead of loading every row into
  memory. Per-row file unlink and item deletes are preserved.

Adds StatusDeleteCleanupTest covering DM + notification cleanup, media
tag + notification cleanup, and the no-associations case.
4 weeks ago
Your Name 661b841428 Remove dead debug methods that echoed the raw request
CollectionController::index and StoryComposeController::createPoll had no
route mapping and simply returned $request->all(). Both are unreachable
debug leftovers; remove them. The live poll route maps to
ComposeController::createPoll, which is unaffected.
4 weeks ago
Your Name e4e12fad7c Extract duplicated blocked-id and duplicate-shortcode query patterns
Two query patterns were copy-pasted across several call sites:

- The 'users who blocked me, plus myself' list used to filter profile
  search (UserFilter::whereFilterableId($pid)->pluck('user_id')->push($pid))
  appeared in ComposeController (x2) and DirectMessageController. Extracted
  to UserFilterService::searchExcludedProfileIds(). Note this is the
  inverse of blocks() (who I blocked), so it is a distinct method.

- CustomEmoji duplicate detection (groupBy('shortcode')->havingRaw(
  'count(*) > 1')) appeared three times in AdminController. Extracted to a
  CustomEmoji::duplicateShortcodes() query scope.

Adds tests for both. No behaviour change.
4 weeks ago
Shlee 7ad550d2f7
Merge pull request #7037 from pixelfed/perf/seasonal-sql-aggregation
Compute Year-in-Review averages in SQL instead of in PHP
4 weeks ago
Your Name cd873c8976 Compute Year-in-Review averages in SQL instead of in PHP
SeasonalController::getData computed the average posts/likes per profile
by grouping in SQL, then pulling every grouped row into a collection and
calling ->pluck('count')->avg() in PHP. This loaded one row per profile
into memory just to average.

Wrap the grouped per-profile counts in a subquery and let the database
compute AVG(count), returning a single value. Also drops the invalid
SELECT * with GROUP BY (ONLY_FULL_GROUP_BY) by selecting count(*) only.

Adds a test verifying the average-of-per-profile-counts and its
exclusions (remote, wrong type, out-of-range date), plus the empty case.
4 weeks ago
Shlee 660880eac7
Merge pull request #7036 from pixelfed/fix/remote-auth-http-timeout
Add timeout, retry and error handling to remote auth HTTP calls
4 weeks ago
Your Name 2ad6e28318 Add timeout, retry and error handling to remote auth HTTP calls
RemoteAuthService::getVerifyCredentials, getFollowing and getToken made
outbound HTTP requests to a user-controlled remote instance during the
Mastodon login flow with no timeout, no retry and no exception handling.
A slow or hostile instance could hang the request or surface an uncaught
exception.

Wrap all three in timeout(20)->retry(3, 750) with try/catch that returns
false on failure, matching the existing pattern in isDomainCompatible().
Callers already treat a falsy return as a failure; add the missing guard
at the one verify_credentials call site that accessed the result array
without checking it first.

Adds RemoteAuthServiceTest covering connection failure, server error and
success paths.
4 weeks ago
Your Name fbfd26d775 Mark direct messages read with a single bulk update
DirectMessageController@read fetched every matching DirectMessage and
saved each one individually in a loop, issuing one UPDATE per row. On an
active thread this is N queries.

Pluck the matching ids and perform a single bulk update, preserving the
existing response (the list of affected message ids) and updated_at
behaviour.

Adds regression tests covering the marked-read ids, the status_id lower
bound, and sender isolation.
4 weeks ago
Your Name 29280cd950 Fix silent failure in avatar upload endpoints
AvatarController@store and BaseApiController@avatarUpdate wrapped the
upload flow in an empty catch(\Exception) block and returned a success
response even when the upload or save failed.

Log the exception and return a real error response (500 JSON for the
API endpoint, a redirect with validation errors for the web endpoint).

Adds regression tests covering the failure path, the success path, and
non-image rejection.
4 weeks ago
dansup 7332e251cd
Merge pull request #7006 from darkdi/fix/groups-topic-pf-type
Drop the no-op pf_type assignment in the group topic feed
4 weeks ago
Daniel Supernault 9e33bed630
Add only_reposts 4 weeks ago
Daniel Supernault 4c4a457fe4
Update ApiV1Controller, fix napi in timelines 4 weeks ago
Dmitry Rantovov 71cade540a Drop the no-op pf_type assignment in the group topic feed 4 weeks ago
Daniel Supernault a6117a2407
Fix reblog handling 4 weeks ago
Daniel Supernault 5468eaeb56
Update ASF 4 weeks ago
Daniel Supernault ccac8b31bd
Update Report endpoint, add support for optional message 4 weeks ago
Shlee 26d3b9f532
Merge pull request #6991 from pixelfed/fix/remote-status-delete-media-cleanup
Fix/remote status delete media cleanup
4 weeks ago
Shlee 32bec52edb
Merge pull request #6985 from pixelfed/chore/laravel12-skeleton-uplift-v2
Laravel 12 skeleton uplift v2
4 weeks ago
Daniel Supernault 584ce27f71
Add Sanctum support 4 weeks ago
Shlee c623a7afbf
Remove 'true' argument from usernameToId call 4 weeks ago
Shlee 7c3644c3e1
Update ApiV1Controller.php 4 weeks ago
Shlee 45e4de3928
Add success message for profile update action 4 weeks ago
Shlee 10ae3fa8cb
Update AdminReportController.php 4 weeks ago
Shlee 8dafe75567
Merge pull request #6992 from pixelfed/feat/status-inspector-commands
Feat/status inspector commands
4 weeks ago
Your Name 9888923a84 Add status:instance, status:avatar, status:emoji inspector commands
Diagnostic commands mirroring status:statuses/status:media:
- status:instance {id|domain|url|@user@domain}: instance row, moderation
  state (banned/unlisted/auto_cw), sync timestamps, local profile count.
- status:avatar {avatar_id|profile_id} [--check]: avatar row, storage state
  (local/cloud existence), owning profile, optional live HEAD on remote_url.
- status:emoji {id|:shortcode:|filename} [--check]: emoji row, origin
  (local/remote), local file existence, optional live HEAD on image_remote_url.

Also includes the status:post -> status:statuses rename.
4 weeks ago
Your Name 237ed61b5d Rename status:post to status:statuses
Rename command signature (status:post -> status:statuses), class
(StatusPost -> StatusStatuses), and file to match.
4 weeks ago
Your Name d62c589881 Rename media:maintenance to media:filtercleanup
Rename the command signature (media:maintenance -> media:filtercleanup), class
(MediaMaintenance -> MediaFilterCleanup), and file to match. Behavior
unchanged.
4 weeks ago
Your Name 4dfb34de75 Drop live from --status on media:maintenance
Orphaned media never references a live status, so --status only accepts soft
and hard. --profile still accepts live/soft/hard. Removes the now-redundant
live short-circuit and makes valid values per-option.
4 weeks ago
Your Name 4ad6e91ef9 Add --status and --profile state filters to media:maintenance
--status live|soft|hard and --profile live|soft|hard narrow orphaned media by
the lifecycle state of the referenced status/profile row. Filters are applied
at the SQL level (whereExists/whereNotExists on deleted_at) so they compose
correctly with --limit. --status=live short-circuits since orphaned media never
has a live status. Options are validated up front.
4 weeks ago
Your Name 838a6b999c Add TODO.md; enhance media:maintenance with --server filter and state annotations
- TODO.md: capture follow-ups (centralized status media teardown, DM leak fix,
  no-DB-cascade rationale, remote-edit orphaning, media:gc re-check).
- media:maintenance: add --server remote|local|both (default both) to filter
  orphaned media by origin.
- Annotate each row with status state (live/soft-deleted/hard-deleted) next to
  status_id and profile state (live/soft-deleted/hard-deleted) next to
  profile_id, in both dry-run table and verbose run output. States are resolved
  in batched, trashed-aware queries.
4 weeks ago
Your Name 48a1fe5e5e Add verbose output to media:maintenance
With -v, print per-row detail (media_id, original status_id, remote_media,
profile_id, mime, size, path) as each orphaned row is processed instead of the
progress bar, and expand the dry-run table with extra columns. Uses Laravel's
built-in verbosity flag.
4 weeks ago
Your Name 87dad44d09 Add media:maintenance command with orphanedMedia scope
media:maintenance --scope orphanedMedia cleans up media whose status_id
references a status that no longer exists (hard-deleted) or is soft-deleted.
These dangling references predate the delete-path fix and MediaDeletePipeline's
attached guard would otherwise refuse to delete them, leaking files.

Detaches (status_id = null) before dispatching deletion via MediaStorageService,
so the guard sees a genuinely orphaned row. Supports --limit (batched),
--dry-run, and --force. The --scope map is extensible for future routines.
4 weeks ago
Your Name 69869536a1 Fix remote status deletion leaking attached media, add status:media command
MediaDeletePipeline skips deletion when media->status_id is set. status_id has
no FK/cascade, so deleting a status never clears it, and the delete jobs
dispatched by the status-delete paths were always skipped, leaking media files.

Detach media (status_id = null) before dispatching the delete in StatusDelete,
RemoteStatusDelete and DeleteRemoteStatusPipeline, so the row is genuinely
orphaned by the time the guard checks it and the deletion proceeds.

Also adds a status:media diagnostic command that dumps all metadata for a
media id (DB columns, computed URLs, attachment state, parent status including
the dangling status_id case, owner, metadata, and an optional live URL check).
4 weeks ago
Your Name 9214e9680a Add admin:resyncemoji command to re-download remote emoji locally
Adds CustomEmojiService::resync() which re-fetches a remote custom emoji's
media from its origin (image_remote_url) via the SSRF-hardened
SecureMediaFetchService and stores it locally under public/{media_path},
reusing the existing headCheck validation and cache busting.

The admin:resyncemoji command takes a comma-separated list of emoji
filenames, looks each up by media_path, and resyncs remote ones. Supports
--missingonly, --dry-run and --force.
4 weeks ago
Your Name d456b7b64a Add structured metadata to MediaDeletePipeline skip/failure logs
Replace interpolated log strings with structured context (media/status/profile/
user ids, mime, size, order, paths, hls_path, remote flag, timestamps) so
operators can trace why orphan-purge deletions are skipped or fail.
4 weeks ago
Your Name 0df4c7117d Add domain-mismatch metadata to Announce status fetch and stop noisy ERROR logs
storeStatus() now throws with JSON metadata (checked id/url hosts, expected
rule, and the full activity payload) when status domains mismatch. The Announce
inbox handler catches this, logs the context at debug level, and returns
gracefully instead of surfacing a full production ERROR stack trace.
4 weeks ago
Your Name b6d645a4d4 fix: delete superseded image/thumbnail files instead of orphaning them
Image::handleImageTransform derives the output filename from the current
media_path and applies the encoder's output extension. When that differs from
what is already stored (heic/avif -> jpg, or a thumbnail regenerated to a new
extension), the new file landed at a different path and the previous file was
left orphaned in the media directory — the source of the leftover _thumb files
under public/m/_v2.

Capture the path each transform supersedes and delete it after a successful
write (only when the new output path differs, so we never delete what we just
wrote). Remove the stale MediaDeleteLeafCleanupTest whose source change is not
in the tree.
4 weeks ago
Your Name fce75030e0 refactor: simplify storage:maintenance flags and make it quiet by default
- Drop --except (--only already covers task selection)
- Quiet by default; per-root/per-item and summary lines now require -v/--verbose
- Errors are always shown regardless of verbosity
- Document the command in the console README
4 weeks ago
Your Name 8ca3ac4dcc polish 4 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
4 weeks ago
Your Name a81270770c refactor: keep MediaMoveStorageLocalToCloud as a stable admin command
MediaMoveStorageLocalToCloud is stable, so move it back out of the Unstable
namespace: restore App\Console\Commands\Admin\MediaMoveStorageLocalToCloud and
its admin:MediaMoveStorageLocalToCloud signature, and update the scheduler,
README, and feature test. CloudToLocal and CloudToCloud remain under unstable:.
4 weeks ago