package app import ( "context" "database/sql" "errors" ) func (a *App) getUserWebhookToken(ctx context.Context, userID string) (UserWebhookToken, error) { var t UserWebhookToken err := a.db.QueryRowContext(ctx, `SELECT user_id, token, created_at FROM user_webhook_tokens WHERE user_id=$1`, userID). Scan(&t.UserID, &t.Token, &t.CreatedAt) if err != nil { if errors.Is(err, sql.ErrNoRows) { return UserWebhookToken{}, errors.New("token not found") } return UserWebhookToken{}, err } return t, nil } func (a *App) ensureUserWebhookToken(ctx context.Context, userID string) (UserWebhookToken, error) { t, err := a.getUserWebhookToken(ctx, userID) if err == nil { return t, nil } token, err := newDeployToken() if err != nil { return UserWebhookToken{}, err } var out UserWebhookToken err = a.db.QueryRowContext(ctx, `INSERT INTO user_webhook_tokens (user_id, token) VALUES ($1,$2) ON CONFLICT (user_id) DO UPDATE SET token=user_webhook_tokens.token RETURNING user_id, token, created_at`, userID, token). Scan(&out.UserID, &out.Token, &out.CreatedAt) if err != nil { return UserWebhookToken{}, err } return out, nil } func (a *App) rotateUserWebhookToken(ctx context.Context, userID string) (UserWebhookToken, error) { token, err := newDeployToken() if err != nil { return UserWebhookToken{}, err } var out UserWebhookToken err = a.db.QueryRowContext(ctx, `INSERT INTO user_webhook_tokens (user_id, token, created_at) VALUES ($1,$2,NOW()) ON CONFLICT (user_id) DO UPDATE SET token=EXCLUDED.token, created_at=NOW() RETURNING user_id, token, created_at`, userID, token). Scan(&out.UserID, &out.Token, &out.CreatedAt) if err != nil { return UserWebhookToken{}, err } return out, nil } func (a *App) validateUserWebhookToken(ctx context.Context, userID, token string) (bool, error) { if userID == "" || token == "" { return false, nil } var exists bool err := a.db.QueryRowContext(ctx, `SELECT EXISTS(SELECT 1 FROM user_webhook_tokens WHERE user_id=$1 AND token=$2)`, userID, token).Scan(&exists) return exists, err }