Three orthogonal fixes that together close the gap where social tokens
were silently aging out without ever being refreshed, then dying at the
provider when the refresh_token also got revoked.
The original failure mode: a user's X token expired because the hourly
proactive-refresh cron's smart `verify()` skip-logic kept saying 'token
still works, no need to refresh', and once the token actually expired,
the cron's WHERE clause excluded it from future runs. By the time anyone
noticed, the refresh_token at X was also gone.
(C) ConnectionVerifier: rename private `refreshTokenIfNeeded` →
public `refreshToken`. Callers that want the smart 'try
access_token first' behavior keep using `verify()`. Callers that
want a proactive refresh (the cron) call `refreshToken` directly.
(B) RefreshExpiringTokens command: drop the
`where('token_expires_at', '>', now())` filter. Already-expired
tokens now get a last-chance refresh attempt before the
refresh_token also dies at the provider. Status filter
(`Connected`) still excludes accounts already marked TokenExpired.
(D) RefreshSocialToken job: switch from `verify()` to
`refreshToken()`, and on `TokenExpiredException` call
`markAsTokenExpired` so the user is notified immediately. The lock
+ transition detection in markAsTokenExpired prevents notification
spam if subsequent cron passes also fail.
Tests:
- 3 new tests for RefreshSocialToken (calls refreshToken not verify,
marks TokenExpired on TokenExpiredException, logs warning on other
errors)
- Updated RefreshExpiringTokens test to assert already-expired tokens
are now dispatched (was previously asserted as 'should NOT')
Settings pages:
- Redesign layout to match Sendkit (max-w-4xl, space-y-12, Separator sections)
- Merge Members page into Workspace settings with Table, invite Dialog, ConfirmDeleteModal
- Add workspace logo upload/delete routes and controller methods
- Translate all hardcoded strings in Workspace.vue modals
Language system:
- Drop languages table, replace language_id FK with locale string column on users
- Create config/languages.php for available languages and default locale
- Add Spanish (es) translations (13 files)
- Simplify HandleInertiaRequests, ProfileController, RegisteredUserController
Code quality:
- Add declare(strict_types=1) to all PHP files
- Fix MastodonPublisher using wrong attribute (filename -> original_filename)
- Fix HasMediaTest for new has_photo/photo_url accessors
- Fix PublishToSocialPlatformTest type error revealed by strict_types
- Remove orphaned Language model from AppServiceProvider morph map
- Update User TypeScript interface (has_photo, photo_url, locale)
- Eager load media relation on workspaces to prevent N+1
- Add 8 new tests for workspace logo upload/delete
- Update workspace settings test to assert members/invitations props
All 710 tests passing.