mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-08-31 16:21:26 +02:00
Move one-time access tokens and signup tokens onto the Francis actor framework, so their state is coordinated cluster-wide and expired entries are purged without the periodic db_cleanup job. One-time access tokens: - Each token is its own actor, keyed by the token value, storing the user ID and optional device token in actor state with a TTL matching the token's expiration. Expired state is purged automatically. - Exchange consumes the token by invoking the actor (atomic validate + delete) outside of any DB transaction, then loads the user and issues an access token. On a later failure it compensates by restoring the token (best-effort, using a non-cancelable context). - The one_time_access_tokens table is dropped; the CLI writes token state through a minimal, non-running actor host. Signup tokens: - A singleton actor holds every signup token in its state and keeps a single cleanup alarm scheduled for the earliest expiration; when it fires it purges expired tokens and reschedules. Listing uses Peek. - Sign-up consumes a token by invoking the actor to atomically increment its usage count, performs user creation in a transaction, and, on failure, compensates by releasing the token (best-effort). - Existing tokens are migrated from the database into the actor state on first startup; the tables are retained only for that migration. Also removes ClearOneTimeAccessTokens and ClearSignupTokens, and lets the importer skip tables no longer present in the schema. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UBMz9v9s8S8RPYNWzshkBu
49 lines
1.3 KiB
Go
49 lines
1.3 KiB
Go
//go:build e2etest
|
|
|
|
package usersignup
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/italypaleale/francis/actor"
|
|
"github.com/italypaleale/francis/host/local"
|
|
)
|
|
|
|
// SignupTokenSeed describes a signup token to seed into the actor state, used by E2E test setup.
|
|
type SignupTokenSeed struct {
|
|
ID string
|
|
Token string
|
|
ExpiresAt time.Time
|
|
UsageLimit int
|
|
UsageCount int
|
|
UserGroupIDs []string
|
|
CreatedAt time.Time
|
|
}
|
|
|
|
// SeedSignupTokens replaces the signup token singleton actor's state with the given tokens.
|
|
// It's intended for E2E test setup, where fixture tokens need to be created with exact values.
|
|
func SeedSignupTokens(ctx context.Context, actors *local.Host, seeds []SignupTokenSeed) error {
|
|
tokens := make([]storedSignupToken, len(seeds))
|
|
for i, s := range seeds {
|
|
tokens[i] = storedSignupToken{
|
|
ID: s.ID,
|
|
Token: s.Token,
|
|
ExpiresAt: s.ExpiresAt,
|
|
UsageLimit: s.UsageLimit,
|
|
UsageCount: s.UsageCount,
|
|
UserGroupIDs: s.UserGroupIDs,
|
|
CreatedAt: s.CreatedAt,
|
|
}
|
|
}
|
|
|
|
_, err := actors.Service().Invoke(ctx, SignupTokenActorType, actor.SingletonActorID, signupTokenMethodReplace, signupTokenReplaceRequest{
|
|
Tokens: tokens,
|
|
})
|
|
if err != nil {
|
|
return fmt.Errorf("failed to seed signup tokens into actor: %w", err)
|
|
}
|
|
return nil
|
|
}
|