feat(tenancy): add team invitation acceptance page
Some checks failed
Some checks failed
New standalone page at /store/{store_code}/invitation/accept?token=xxx
where invited team members can:
- Review their name and email (pre-filled from invitation)
- Set their password
- Accept the invitation
Page handles all routing modes (dev path, platform path, prod subdomain,
custom domain) via store context middleware. After acceptance, redirects
to the platform-aware store login page.
New service method get_invitation_info() validates the token and returns
invitation details without modifying anything.
Error states: expired token, already accepted, invalid token.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -228,6 +228,52 @@ class StoreTeamService:
|
||||
logger.error(f"Error inviting team member: {str(e)}")
|
||||
raise
|
||||
|
||||
def get_invitation_info(self, db: Session, token: str) -> dict[str, Any] | None:
|
||||
"""
|
||||
Get invitation details without accepting it.
|
||||
|
||||
Used by the acceptance page to pre-fill the form.
|
||||
Returns None if token is invalid.
|
||||
"""
|
||||
store_user = (
|
||||
db.query(StoreUser)
|
||||
.execution_options(include_deleted=True)
|
||||
.filter(StoreUser.invitation_token == token)
|
||||
.first()
|
||||
)
|
||||
|
||||
if not store_user:
|
||||
return None
|
||||
|
||||
user = store_user.user
|
||||
store = store_user.store
|
||||
role_name = store_user.role.name if store_user.role else "member"
|
||||
|
||||
# Check if already accepted
|
||||
if store_user.invitation_accepted_at is not None:
|
||||
return {
|
||||
"valid": False,
|
||||
"error": "already_accepted",
|
||||
"store_name": store.name,
|
||||
}
|
||||
|
||||
# Check expiration (7 days)
|
||||
is_expired = False
|
||||
if store_user.invitation_sent_at:
|
||||
elapsed = datetime.utcnow() - store_user.invitation_sent_at
|
||||
is_expired = elapsed > timedelta(days=7)
|
||||
|
||||
return {
|
||||
"valid": not is_expired,
|
||||
"error": "expired" if is_expired else None,
|
||||
"email": user.email,
|
||||
"first_name": user.first_name,
|
||||
"last_name": user.last_name,
|
||||
"store_name": store.name,
|
||||
"store_code": store.store_code,
|
||||
"role_name": role_name,
|
||||
}
|
||||
|
||||
def accept_invitation(
|
||||
self,
|
||||
db: Session,
|
||||
|
||||
Reference in New Issue
Block a user