Skip to content

feat: add expirable invite links with 7-day default expiry#188

Merged
ManukMinasyan merged 20 commits intomainfrom
feat/expirable-invite-links
Mar 23, 2026
Merged

feat: add expirable invite links with 7-day default expiry#188
ManukMinasyan merged 20 commits intomainfrom
feat/expirable-invite-links

Conversation

@ManukMinasyan
Copy link
Copy Markdown
Contributor

Summary

  • Add expires_at column to team_invitations table with configurable default (7 days via config('jetstream.invitation_expiry_days'))
  • Custom AcceptTeamInvitationController with expiry check (friendly expired page) and email-match verification (403 on mismatch)
  • New table actions: Extend (resets expiry), Copy Link (signed URL to clipboard), plus existing Resend/Cancel
  • Expiry display in pending invitations table using relative time (diffForHumans()) or "Expired" label
  • invitations:cleanup artisan command (scheduled daily) purges expired invitations older than 30 days
  • NULL expires_at treated as already expired (safe default for any legacy data)

Test plan

  • 19 tests passing (48 assertions) across 3 test files:
    • InviteTeamMemberTest (3) — invite creates expiry, config override, cancel
    • AcceptTeamInvitationTest (6) — accept, expired page, null expiry, wrong email, invalid sig, deletion
    • ManageTeamInvitationsTest (10) — isExpired unit tests, extend/copy actions, table display, cleanup command
  • Manual QA: all 10 scenarios passed, health score 100/100
  • Pint formatting applied

Closes #94

- Add expires_at column to team_invitations table
- Auto-set expiry from config('jetstream.invitation_expiry_days')
- Custom AcceptTeamInvitationController with expiry + email-match checks
- Friendly expired invitation landing page
- Extend invitation and copy invite link table actions
- Scheduled cleanup command for expired invitations
- 19 tests covering invite, accept, manage, and cleanup flows
Copilot AI review requested due to automatic review settings March 19, 2026 07:06
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds expiration semantics to Jetstream team invitations (default 7 days, configurable), improves UX for expired links, and introduces operational cleanup for stale invitations.

Changes:

  • Add expires_at to team_invitations, set on invite creation, display in the pending invitations table, and provide “Extend” + “Copy Link” actions.
  • Replace Jetstream’s invitation acceptance endpoint with a custom controller that blocks expired invites and enforces invited-email matching.
  • Add a scheduled Artisan command to purge long-expired invitations, plus new Pest coverage for the invitation lifecycle.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
tests/Feature/Teams/ManageTeamInvitationsTest.php Adds model/action/table/command coverage around expiry + cleanup.
tests/Feature/Teams/InviteTeamMemberTest.php Updates invitation tests to new Livewire components and asserts expiry is set/configurable.
tests/Feature/Teams/AcceptTeamInvitationTest.php Covers accept flow for valid/expired/null-expiry/wrong-email/invalid-signature cases.
routes/web.php Swaps invitation accept route to the new invokable controller.
resources/views/teams/invitation-expired.blade.php Adds friendly expired-invitation page.
resources/views/emails/team-invitation.blade.php Displays expiry info in invitation emails.
lang/en/teams.php Adds labels/notifications for extend + copy-link actions.
database/migrations/2026_03_18_202956_add_expires_at_to_team_invitations_table.php Adds nullable expires_at + index.
database/factories/TeamInvitationFactory.php Adds factory defaults/states for expiry scenarios.
config/jetstream.php Introduces invitation_expiry_days setting (default 7).
bootstrap/app.php Schedules daily invitations:cleanup.
app/Models/TeamInvitation.php Adds expires_at casting + isExpired() helper.
app/Livewire/App/Teams/PendingTeamInvitations.php Shows expiry status and adds Extend/Copy Link table actions.
app/Http/Controllers/AcceptTeamInvitationController.php Implements expiry + invited-email enforcement on accept.
app/Console/Commands/CleanupExpiredInvitationsCommand.php Adds cleanup command for old expired invitations.
app/Actions/Jetstream/InviteTeamMember.php Sets expires_at when creating invitations.

…nent

Instead of cramming the invitation message into the <p> subheading
alongside the navigation link, render it as a visually distinct
banner using Filament's Html schema component in the page content().
The subheading reverts to its default behavior (just the sign-up/in link).
Rector's EloquentMagicMethodToQueryBuilderRector rule requires
User::query()->where() instead of User::where().
@ManukMinasyan ManukMinasyan merged commit 9f00004 into main Mar 23, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhanced Workspace Invitations with Expirable Invite Links

2 participants