Laravel 13's Image facade wraps Intervention Image v4 internally, so
switching our upload width probe and resize job/command to it required
bumping intervention/image ^3 -> ^4 (and its intervention/gif ^5
dependency). Removes our own ImageManager container binding and
config/image.php in favour of Laravel's built-in driver resolution.
Also fixes a latent filename mismatch in ProcessMediaJobTest that Pint's
stricter typing on the new Image API turned into a hard TypeError, and
tidies config/flare.php to use imported class names instead of FQCNs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018Sorsgn85nw7uQyRMNvzyD
Uses Laravel's built-in URL::forceHttps() so route(), url(), and asset()
always generate https:// links in production, without affecting local dev.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RP5TeZbk9KS754xuJMobjE
Replaces every raw GuzzleHttp\Client usage (Nominatim, web.archive.org,
webmention fetch/discover/send, Bridgy syndication, IndieAuth client_id
lookup, contact avatar/h-card fetch, profile image download, and the
CloudConvert screenshot pipeline) with Illuminate\Support\Facades\Http,
and enables Http::preventStrayRequests() globally in tests so any
un-faked outbound call now fails loudly instead of silently hitting the
network.
The CloudConvert retry-until-finished middleware in AppServiceProvider
is replaced by a plain polling loop in SaveScreenshot, which also fixes
a latent bug where the old middleware decoded a response object instead
of its body. Bridgy syndication jobs keep their tries=1/no-retry
semantics unchanged to avoid duplicate publishes. Guzzle's PSR-7 helpers
(Header, UriResolver, Utils) stay in SendWebMentions since Http has no
equivalent for them.
Every affected test file's Guzzle MockHandler/HandlerStack boilerplate
is replaced with Http::fake()/Http::sequence().
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P8j7cJhCiYDsGUgB7obKNQ
Swap the syntax highlighting library and its CSS theme (zenburn -> nord,
matching tempest's class names), add padding to code blocks, and commit
the missing compressed winter.js assets from the earlier asset rename.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Bridgy's Micropub endpoint required per-silo access tokens and posted
raw markdown content directly. Switch to Bridgy's webmention publish
endpoint instead: POST source/target to /publish/webmention and let
Bridgy re-fetch the note page, which now carries hidden verification
links for silos not yet syndicated. Removes the now-unused
config/bridgy.php and BRIDGY_MASTODON_TOKEN env var, and caps both
jobs to a single try since retries would send Bridgy a duplicate
publish webmention.
max-width: 100% alone lets img/picture height stay at intrinsic size,
so on narrow viewports images could still overflow vertically or look
squashed. Adding height: auto keeps the aspect ratio correct as width
scales down.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ProcessMedia was using resize(), which distorts images that aren't the
target aspect ratio; scale() preserves it. Existing medium/small
variants generated before this fix need regenerating, so add an
artisan command (with --dry-run) to do that, plus feature tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
OwnYourSwarm sends content as an array of objects with `value` and `html`
keys per the Micropub spec. The handler now handles both forms: plain
strings and rich objects, preferring `html` over `value`.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Real OwnYourSwarm requests send the checkin h-card inside
properties.checkin (as an array), not at the top level. The code
was reading from the top-level key so no Place was ever created,
leaving checkin notes blank.
Update tests to match the real request format.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Long brid.gy Bluesky source URLs exceed 255 characters, causing
SQLSTATE[22001] errors. Changed source and target from varchar(255)
to text (no performance impact in PostgreSQL).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each DTO now owns its own parsing logic via fromRequest(), removing
the normalization layer from MicropubRequest entirely. UpdateHandler
gains delete support and allows replace/add/delete to compose in a
single request rather than being mutually exclusive.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each handler now declares the data class it needs via dataClass(). The
controller builds the appropriate typed DTO from the raw request array
before calling handle(), giving handlers typed property access instead
of raw array lookups.
Handlers moved to App\Services\Micropub\Handlers, data objects to
App\Services\Micropub\Data. MicropubHandlerRegistry and the interface
are documented with flow diagrams and guidance for adding new types.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MicropubUnsupportedModelException for non-note update attempts
- Refactor UpdateHandler to throw exceptions instead of returning JsonResponse objects; extract applySyndication() to DRY up duplicated syndication URL mapping
- Fix MicropubController: InvalidTokenScopeException now returns 401 + insufficient_scope; add catches for ModelNotFoundException (404) and MicropubUnsupportedModelException (500); updates return 200 not 201
- Slim MicropubRequest.normalizeMicropubJson() to branch on action type, keeping update and create fields cleanly separated
- Update tests to match corrected status codes and error keys; remove markTestSkipped() from all update tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Trying to organise the code better. It now temporarily doesn’t support
update requests. Thought the spec defines them as SHOULD features and
not MUST features. So safe for now :)