Move mem/pg activate/live/meta/plan/token/seed paths into ≤800-line siblings without behavior change; short tests green.
61 lines
1.9 KiB
Go
61 lines
1.9 KiB
Go
package projectinstance
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
)
|
|
|
|
func hashActivationToken(token string) string {
|
|
sum := sha256.Sum256([]byte(token))
|
|
return hex.EncodeToString(sum[:])
|
|
}
|
|
|
|
func (s *pgStore) SaveActivationToken(ctx context.Context, token string, projectID int64, planHash string, expectedVersion int, snapshot WorkflowPlan, createdBy int64, expiresAt time.Time, policyRevision *int64) error {
|
|
raw, _ := json.Marshal(snapshot)
|
|
tokenHash := hashActivationToken(token)
|
|
_, err := s.pool.Exec(ctx, `
|
|
INSERT INTO workflow_activation_tokens (token, project_id, plan_hash, expected_project_version, config_snapshot, created_by, expires_at, author_only_policy_revision)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
|
|
`, tokenHash, projectID, planHash, expectedVersion, raw, createdBy, expiresAt, policyRevision)
|
|
return err
|
|
}
|
|
|
|
func (s *pgStore) ConsumeActivationToken(ctx context.Context, token string, projectID int64, planHash string) (*WorkflowPlan, error) {
|
|
tokenHash := hashActivationToken(token)
|
|
var snapRaw []byte
|
|
var storedHash string
|
|
var usedAt *time.Time
|
|
var expiresAt time.Time
|
|
err := s.pool.QueryRow(ctx, `
|
|
SELECT plan_hash, config_snapshot, used_at, expires_at
|
|
FROM workflow_activation_tokens WHERE token = $1 AND project_id = $2
|
|
`, tokenHash, projectID).Scan(&storedHash, &snapRaw, &usedAt, &expiresAt)
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
return nil, ErrTokenInvalid
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if usedAt != nil || time.Now().After(expiresAt) || storedHash != planHash {
|
|
return nil, ErrTokenInvalid
|
|
}
|
|
tag, err := s.pool.Exec(ctx, `
|
|
UPDATE workflow_activation_tokens SET used_at = now() WHERE token = $1 AND used_at IS NULL
|
|
`, tokenHash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if tag.RowsAffected() == 0 {
|
|
return nil, ErrTokenInvalid
|
|
}
|
|
var plan WorkflowPlan
|
|
_ = json.Unmarshal(snapRaw, &plan)
|
|
return &plan, nil
|
|
}
|