package tenant import ( "context" "crypto/rand" "database/sql" "encoding/hex" "time" ) func (q *Queries) ListAPIKeys(ctx context.Context) ([]APIKey, error) { rows, err := q.db.QueryContext(ctx, `SELECT key, name, created_at, last_used_at FROM api_keys ORDER BY created_at DESC`) if err != nil { return nil, err } defer rows.Close() var keys []APIKey for rows.Next() { k, err := scanAPIKey(rows) if err != nil { return nil, err } keys = append(keys, k) } return keys, rows.Err() } func (q *Queries) CreateAPIKey(ctx context.Context, name string) (*APIKey, error) { key, err := generateAPIKey() if err != nil { return nil, err } now := time.Now().UTC().Format(time.RFC3339) _, err = q.db.ExecContext(ctx, `INSERT INTO api_keys (key, name, created_at) VALUES (?, ?, ?)`, key, name, now) if err != nil { return nil, err } return &APIKey{ Key: key, Name: name, CreatedAt: time.Now().UTC(), }, nil } func (q *Queries) ValidateAPIKey(ctx context.Context, key string) (bool, error) { var dummy int64 err := q.db.QueryRowContext(ctx, `SELECT 1 FROM api_keys WHERE key = ?`, key).Scan(&dummy) if err == sql.ErrNoRows { return false, nil } if err != nil { return false, nil } go func() { now := time.Now().UTC().Format(time.RFC3339) q.db.ExecContext(ctx, `UPDATE api_keys SET last_used_at = ? WHERE key = ?`, now, key) }() return true, nil } func (q *Queries) DeleteAPIKey(ctx context.Context, key string) error { _, err := q.db.ExecContext(ctx, `DELETE FROM api_keys WHERE key = ?`, key) return err } func scanAPIKey(s scanner) (APIKey, error) { var k APIKey var createdAt, lastUsedAt sql.NullString err := s.Scan(&k.Key, &k.Name, &createdAt, &lastUsedAt) if err != nil { return k, err } k.CreatedAt = parseTime(createdAt.String) if lastUsedAt.Valid { t := parseTime(lastUsedAt.String) k.LastUsedAt = &t } return k, nil } func generateAPIKey() (string, error) { b := make([]byte, 24) if _, err := rand.Read(b); err != nil { return "", err } return "wk_" + hex.EncodeToString(b), nil }