Commit Graph

4404 Commits (b291b7bf6553b1e2b99ea13dc4364347b4971dbb)

Author SHA1 Message Date
Shlee a82dc91295
Merge branch 'staging' into dev 4 weeks ago
Your Name de850836ca fix: make admin:fixPostCounts summary report only changed metrics
The resynced summary printed all three counts unconditionally, which
made an untouched metric (e.g. an already-correct comments count) look
like it had been resynced. Drive the summary from the drifted set and
show before->after values, so it matches the drift detection exactly.
4 weeks ago
Your Name d50024a578 fix: display comments count as 0 instead of blank in admin:fixPostCounts
reply_count is a nullable column, so NULL rendered as an empty string in
the resync summary. Cast the summary output to int so a null/absent
comment count prints as 0. No behavior change to the reconcile logic.
4 weeks ago
Your Name 73b8353dab refactor: move admin:fix*Counts commands to Admin/
FixProfileCounts and FixPostCounts use the admin: signature prefix and
are operator-run maintenance tools, so move them from FixBugs/ to Admin/
(namespace updated) and refresh the README tables to match.
4 weeks ago
Your Name 744e453606 feat: add admin:fixPostCounts to resync post like/boost/comment counts
Add a FixPostCounts command mirroring admin:fixProfileCounts (single-id,
--all --scope, --active, --type, --dry-run, --force). It reconciles the
statuses likes_count, reblogs_count, and reply_count columns against
source-of-truth tables.

Add canonical recompute helpers and reconcileStatusCounts() to
StatusService (mirroring AccountStatService), busting the status cache
only when a column actually drifted.
4 weeks ago
Shlee 4699c2fe91
Merge branch 'staging' into refactor/artisan-command-subfolders 4 weeks ago
Your Name cd353a8305 refactor: move resolved one-off migrations to Deprecated/
status:dedup and fix:avatars address historical data states that can no
longer occur (unique statuses.uri index since 2019; SVG identicon avatars
no longer generated). Move both to a Deprecated/ folder and update the
README audit accordingly. media:fix stays in FixBugs/ since image filters
are still an active feature.
4 weeks ago
Shlee cafec250fe
Update README.md 4 weeks ago
Your Name c99b8068a2 docs: add README for Artisan commands with listing and audit 4 weeks ago
Your Name 1eae4bbd43 refactor: organize Artisan commands into subfolders
Group console commands into Admin, Dev, FixBugs, Install, Internal, and
User subfolders (matching the earlier reorganization), and add a new
Status subfolder for the status:user, status:profile, and status:post
debug commands. Namespaces updated to match; command signatures and the
total command count are unchanged.
4 weeks ago
Your Name 16c7c5d2e3 refactor: rename status debug commands to status: prefix
Rename user:status, profile:status, and post:status console commands
to status:user, status:profile, and status:post. Rename the command
files and classes to match (StatusUser, StatusProfile, StatusPost) and
update the cross-reference tip in StatusProfile.
4 weeks ago
Your Name 0d01d5a963 Fix duplicate-key violation when importing remote media attachments
Helpers::importNoteAttachment unconditionally inserted a new Media row per
attachment, so re-importing a remote status (an Announce racing another
inbox job, a re-fetch, or a duplicate url within one activity) hit the
media_status_id_media_path_unique constraint and crashed the queue job with
a 1062 UniqueConstraintViolationException, dropping the boost/import.

Make createMediaAttachment idempotent on (status_id, media_path): skip when
a row already exists, and catch the unique-constraint violation as a
lost-race no-op, returning null so the caller skips re-dispatching storage.

Adds regression tests (re-import no-op, distinct urls still stored,
concurrent-insert returns null).
4 weeks ago
Shlee f017e03286
Merge pull request #6932 from pixelfed/feature/media-url-migrate
Feature/media url migrate
4 weeks ago
Your Name 70b4a05b5c Add admin:MediaMoveStorageCloudToCloud for cold S3->S3 migration
Cold-migrate existing media from an old S3 bucket to the current cloud
bucket, one media row at a time (like MigrateLocalS3MediaURL):
- Source = --sourceDisk (default s3-old, reads AWS_OLD_*); destination = the
  current cloud disk (config filesystems.cloud). No .env editing: operators
  point AWS_* at the new bucket first (restarting workers as usual) so new
  uploads/downloads land on the new bucket, then run this to backfill old data.
- Copies media (+thumbnail) source->destination, verifies by size and by
  sha256 of the freshly-written destination object (against original_sha256),
  rewrites cdn_url/optimized_url/thumbnail_url to the destination host, and
  GCs the source objects (unless --keep-source). Busts caches.
- Only touches rows whose cdn_url still points at the source host; idempotent.
- --sourceDisk / --limit / --dry-run / --force.
- Adds the s3-old disk (AWS_OLD_*) to config/filesystems.php and feature tests.
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
Shlee 137bc91b41
Merge pull request #6922 from pixelfed/security/ssrf-media-fetch
Fix media fetch
4 weeks ago
Your Name da9e73dd22 Rename to admin:MigrateLocalS3MediaURL and drop --avatars
Rename the command (and test) to admin:MigrateLocalS3MediaURL to reflect its
scope: rewriting stale S3/cloud media URLs only. Remove avatar handling and
the --avatars option; the command now focuses solely on status media
(cdn_url, thumbnail_url, optimized_url).
4 weeks ago
Your Name 04536a6e32 Add admin:MigrateLocalMediaURL; replace media:cloud-url-rewrite
Rebuilds stale local media URLs (cdn_url, thumbnail_url, optimized_url) and
avatar cdn_urls from their storage paths via the configured cloud disk.

- Default target host comes from the configured cloud disk (AWS_URL);
  requires confirmation (or --force) and can be overridden with --newDomain.
- Optional --oldDomain filters to a single old backend host; by default all
  stale hosts are rewritten.
- Refuses to run when PF_ENABLE_CLOUD is false (local storage) and, when
  auto-detecting, refuses a target equal to the app domain — so local-storage
  instances are never rewritten.
- Single status id / post URL, --all, --avatars; --dry-run; busts
  MediaService/StatusService caches for affected statuses.
- Removes the superseded media:cloud-url-rewrite command.
- Adds feature tests covering rewrite/skip/dry-run/oldDomain/newDomain/
  remote-skip/local-storage-refusal.
4 weeks ago
Your Name 4aa7b57280 Add post:status command for post/media diagnostics
Dumps a Status and its media for debugging. Accepts a post id or URL
(/p/username/ID). Shows status columns, author, every media row's storage
fields (media_path, thumbnail_path, cdn_url, thumbnail_url, optimized_url,
remote_url, etc.), computed url()/thumbnailUrl()/expected-from-path, a URL
health check comparing stored URL hosts against the configured cloud disk
host (flags stale hosts), and the cached MediaService media_attachments
actually served to clients.
4 weeks ago
Shlee 1ba3f8c9cf
Merge pull request #6928 from pixelfed/feature/user-status-command
Require --scope (local/remote/both) for admin:fixProfileCounts --all
4 weeks ago
Your Name bcd5a5bd7b Require --scope (local/remote/both) for admin:fixProfileCounts --all
Bulk --all reconciliation previously scanned both local and remote profiles
implicitly. Now --all requires an explicit --scope of local, remote, or
both. --active stays local-only and rejects a non-local --scope. Adds the
BelongsTo return type to Profile::user() so the whereHas('user') scope
filter passes Larastan, and adds tests for scope requirement/validation and
local/remote filtering.
4 weeks ago
Shlee c086f20d3f
Merge pull request #6926 from pixelfed/feature/user-status-command
Update stale command-name reference in comment to admin:fixProfileCounts
4 weeks ago
Your Name 55e9201b1a Update stale command-name reference in comment to admin:fixProfileCounts 4 weeks ago
Shlee 511527fc5a
Merge pull request #6923 from pixelfed/feature/user-status-command
Refactor: FixProfileCounts
4 weeks ago
Your Name 96f26405f1 Rename to admin:fixProfileCounts, make --active its own mode, add --type
- Rename command signature fix:profilecounts -> admin:fixProfileCounts.
- --active is now its own bulk mode (recently-active local accounts),
  mutually exclusive with --all and a single id.
- Add --type=followers|following|statuses to restrict reconciliation to a
  single metric (validated).
- Update/extend tests for the new name, --type restriction and invalid-type
  rejection.
4 weeks ago
Your Name 698ba224e3 Schedule weekly profile-count reconcile and add reconciliation tests
- Add --force flag to fix:profilecounts for unattended runs and schedule
  'fix:profilecounts --all --force' weekly (Sun 03:37) as a safety-net
  reconcile. Kept as a low-frequency full scan rather than a new event-driven
  dirty-set; it only writes profiles that actually drifted.
- Add Feature tests for AccountStatService recompute helpers and
  reconcileProfileCounts (media-type status_count semantics, follower/
  following counts, drift/no-drift/no-write, metric restriction, missing
  profile) plus fix:profilecounts command behavior (silent-when-synced,
  dry-run makes no changes).
4 weeks ago
Your Name a187ab6639 Refactor profile count reconciliation into shared AccountStatService methods
Extract canonical source-of-truth count logic into AccountStatService:
recalculateStatusCount/FollowerCount/FollowingCount and a
reconcileProfileCounts() that fixes only drifted columns and busts caches.

Both the scheduled app:account-post-count-stat-update (status-only, its
correct scope) and fix:profilecounts now use these instead of duplicating
the SQL. Also corrects the status_count definition to match the actual
increment logic in StatusEntityLexer/StatusDelete (media post types only:
photo/video albums), rather than the previous inconsistent all-statuses /
scoped counts that could themselves cause drift.

The scheduled updater keeps its incremental, dirty-set design and remains
status-only; follower/following stay owned by FollowServiceWarmCache.
4 weeks ago
Your Name aadde946d2 Apply Pint formatting to SecureMediaFetchService 4 weeks ago
Your Name 037f1ac0b9 Add fix:profilecounts (total profile cache resync); remove redundant count commands
Consolidate cached-count reconciliation into a single fix:profilecounts
command that resyncs followers_count, following_count and status_count
from the source-of-truth tables for one profile or --all. Only reports
profiles with actual drift (silent when in sync); supports --dry-run and
--dispatch (queues FollowServiceWarmCache and rebuilds Redis sets).

Removes the superseded manual commands fix:followercount, fix:statuscount
and fix:rpc. Keeps app:account-post-count-stat-update, which is scheduled
(runs every 6 hours) and queue-driven.
4 weeks ago
Your Name 9fe1fe55af Add fix:followercount command to resync drifted follower/following counts
profiles.followers_count/following_count are cached columns reconciled
lazily by FollowServiceWarmCache (throttled up to 7 days), so they can
drift from the followers table. This command recomputes them from the
source-of-truth table for a single profile or --all drifted local
profiles, with --dry-run to report and --dispatch to queue the warm-cache
job (which also rebuilds the Redis sets). Mirrors the existing
fix:statuscount convention.
4 weeks ago
Shlee 8123dcf934
Update SecureMediaFetchService.php 4 weeks ago
Your Name 7482befd8f Allow gif and webp mime types for custom emoji import
Add image/gif and image/webp to the accepted custom emoji image types via
a shared CustomEmojiService::ALLOWED_MIME_TYPES constant used by both the
ActivityPub mediaType check and the response-content headCheck, so the
allowlist stays in sync. File extension derives from the mime type.
4 weeks ago
Your Name 3d82a8e8b2 Fix unauthenticated SSRF in remote media/avatar fetch (variant of CVE-2026-71246)
The remote media path validated URLs only as strings (Helpers::validateUrl
normalizes the host + checks a ban list) and then downloaded them with
Http::head + file_get_contents($url), which resolve DNS themselves and
follow redirects with no private-IP checks and no address pinning. A remote
actor whose icon.url redirected to an internal address (e.g. 172.18.0.1 or
169.254.169.254) made the queue worker fetch internal content and, for
image responses, republish it at a public avatar URL. No account required.

Fixes:
- Add SecureMediaFetchService: validates URL, resolves + rejects non-global
  IPs (fail-closed), pins the connection to the validated IP via
  CURLOPT_RESOLVE, disables auto-redirects with per-hop re-validation, and
  enforces https-only + a byte cap. Mirrors the ActivityPubFetchService
  hardening from CVE-2026-71246.
- Route MediaStorageService head()/fetchAvatar()/remoteToCloud() through it,
  removing the bare Http::head and file_get_contents($url) sinks.
- validateUrl(): when DNS verification is enabled, reject hosts that resolve
  into reserved ranges, closing the metadata.google.internal bypass.
- Harden adjacent same-class sinks: CustomEmojiService (emoji doc + image +
  head), FetchCacheService/webfinger, and DiscoverActor.
- Add regression tests (tests/Unit/ActivityPub/SsrfUrlValidationTest.php).
4 weeks ago
Shlee 350b8fdca5
Merge pull request #6920 from pixelfed/feature/user-status-command
new admin commands
4 weeks ago
Your Name 92d09ffaaf Add profile:status command for local and remote profile diagnostics
Unlike user:status (local users only, keyed on the users table),
profile:status keys on the profiles table so it works for remote/
federated actors too. Resolves id, username, user@domain, @user@domain,
webfinger, or remote_url. Shows full column dump, derived/federation
metadata, linked local user (local) or Instance row (remote), and
health checks for orphans, missing keys, and count desync.
4 weeks ago
Your Name 93f8138482 Expand user:status profile section with full column dump and derived metadata
Dump every profiles column dynamically (keys redacted, long text trimmed),
add derived metadata (local/remote type, urls, live vs cached follower/
following/status counts, avatar, federation fields), and profile health
checks (soft-delete, id mismatches, missing crypto keys, count desync).
4 weeks ago
Shlee 3dfa249f3c
Merge pull request #6914 from pixelfed/fix/cached-eloquent-model-incomplete-object
Refactor: stop caching raw Eloquent models to prevent incomplete-object 500s
4 weeks ago
Shlee 80b7e99efe
Merge pull request #6915 from pixelfed/feature/user-status-command
New artisan admin commands
4 weeks ago
Your Name 2c7227a9c1 Add user:checkpassword read-only command to diagnose rejected logins 4 weeks ago
Daniel Supernault 51beaa30d1
Update UserAccountDelete command 4 weeks ago
Your Name 7148c58283 Add user:setpassword artisan command for CLI password reset 4 weeks ago
Daniel Supernault 0679216fa2
Update AdminReportController 4 weeks ago
Your Name e39b0f1b50 Add user:status artisan command for account login/reset diagnostics 4 weeks ago
Your Name f0e951dcce fix: stop caching raw Eloquent models to prevent incomplete-object 500s
Caching an Eloquent model in a Cache::remember closure could deserialize
into a __PHP_Incomplete_Class on read, throwing 'attempt to access a
property on an incomplete object' and returning a 500. This surfaced on
guest profile pages (ProfileController::buildProfile reading
$user->user->settings) and affected several other latent call sites.

Changes:
- ProfileController: cache a plain settings array instead of the
  UserSetting model; fall back to defaults when the settings row is missing
- StoryService::getById: fetch a live model instead of caching it
- InstanceService::getByDomain, CustomEmoji::scan: cache arrays
- Site/MobileController: cache Page data as an array via a shared
  ManagesCachedPages trait; update blade views to array access
- Add public-route smoke/regression tests covering the cache-read path
4 weeks ago
Daniel Supernault 5b63f5f225
Fix AdminReports 4 weeks ago
Daniel Supernault 81245ec467
Fix ProfileMigrationStorageRequest, use signed requests for gts and other compat 4 weeks ago
Your Name 302edf09d5 refactor: rename PixelfedUsername rule to ValidUsername
Pure rename of the App\Rules\PixelfedUsername validation rule to
App\Rules\ValidUsername for a clearer, more idiomatic name. Updates
the class, filename, test, and all 8 controller call sites. No
behavior change.
4 weeks ago
Your Name 7c5d93e96b refactor: consolidate username validation into PixelfedUsername rule
Replace 7 duplicated inline username validation closures across 6
controllers (ApiV1Dot1, RemoteAuth, CuratedRegister, AdminInvite x2,
AppRegister, Auth/Register) with the existing PixelfedUsername rule.

Add the 'must contain at least one alphabetical character' check to
the rule so all call sites share consistent, stricter validation.

Add PixelfedUsernameTest covering all validation branches.
4 weeks ago
Daniel Supernault e3a2640704
Fix endsWith. Closes #6904 4 weeks ago
Your Name 8b53f23e77 refactor: migrate LikePipeline to use NotificationService::firstOrCreateNotification 1 month ago