The codebase that powers boop.cat boop.cat
at main 116 lines 3.0 kB view raw
1// Copyright 2025 boop.cat 2// Licensed under the Apache License, Version 2.0 3// See LICENSE file for details. 4 5package db 6 7import ( 8 "crypto/sha256" 9 "database/sql" 10 "encoding/hex" 11 "time" 12) 13 14type APIKey struct { 15 ID string `json:"id"` 16 UserID string `json:"userId"` 17 Name string `json:"name"` 18 KeyHash string `json:"-"` 19 KeyPrefix string `json:"prefix"` 20 CreatedAt string `json:"createdAt"` 21 LastUsedAt sql.NullString `json:"lastUsedAt"` 22} 23 24type User struct { 25 ID string 26 Email string 27 Username sql.NullString 28 EmailVerified bool 29 Banned bool 30} 31 32func ListAPIKeys(db *sql.DB, userID string) ([]APIKey, error) { 33 rows, err := db.Query(` 34 SELECT id, userId, name, keyHash, keyPrefix, createdAt, lastUsedAt 35 FROM apiKeys WHERE userId = ? 36 `, userID) 37 if err != nil { 38 return nil, err 39 } 40 defer rows.Close() 41 42 var keys []APIKey 43 for rows.Next() { 44 var k APIKey 45 if err := rows.Scan(&k.ID, &k.UserID, &k.Name, &k.KeyHash, &k.KeyPrefix, &k.CreatedAt, &k.LastUsedAt); err != nil { 46 return nil, err 47 } 48 keys = append(keys, k) 49 } 50 return keys, rows.Err() 51} 52 53func CreateAPIKey(db *sql.DB, id, userID, name, keyHash, keyPrefix string) error { 54 _, err := db.Exec(` 55 INSERT INTO apiKeys (id, userId, name, keyHash, keyPrefix, createdAt, lastUsedAt) 56 VALUES (?, ?, ?, ?, ?, ?, NULL) 57 `, id, userID, name, keyHash, keyPrefix, time.Now().UTC().Format(time.RFC3339)) 58 return err 59} 60 61func DeleteAPIKey(db *sql.DB, userID, keyID string) error { 62 result, err := db.Exec(`DELETE FROM apiKeys WHERE id = ? AND userId = ?`, keyID, userID) 63 if err != nil { 64 return err 65 } 66 rows, _ := result.RowsAffected() 67 if rows == 0 { 68 return sql.ErrNoRows 69 } 70 return nil 71} 72 73func CountAPIKeys(db *sql.DB, userID string) (int, error) { 74 var count int 75 err := db.QueryRow(`SELECT COUNT(*) FROM apiKeys WHERE userId = ?`, userID).Scan(&count) 76 return count, err 77} 78 79func ValidateAPIKey(db *sql.DB, key string) (*User, string, error) { 80 81 hash := sha256.Sum256([]byte(key)) 82 keyHash := hex.EncodeToString(hash[:]) 83 84 var apiKey APIKey 85 err := db.QueryRow(` 86 SELECT id, userId, name, keyHash, keyPrefix, createdAt, lastUsedAt 87 FROM apiKeys WHERE keyHash = ? 88 `, keyHash).Scan(&apiKey.ID, &apiKey.UserID, &apiKey.Name, &apiKey.KeyHash, &apiKey.KeyPrefix, &apiKey.CreatedAt, &apiKey.LastUsedAt) 89 if err != nil { 90 return nil, "", err 91 } 92 93 var user User 94 var emailVerified, banned int 95 err = db.QueryRow(` 96 SELECT id, email, username, emailVerified, banned 97 FROM users WHERE id = ? 98 `, apiKey.UserID).Scan(&user.ID, &user.Email, &user.Username, &emailVerified, &banned) 99 if err != nil { 100 return nil, "", err 101 } 102 user.EmailVerified = emailVerified != 0 103 user.Banned = banned != 0 104 105 if user.Banned { 106 return nil, "", sql.ErrNoRows 107 } 108 if !user.EmailVerified { 109 return nil, "", sql.ErrNoRows 110 } 111 112 _, _ = db.Exec(`UPDATE apiKeys SET lastUsedAt = ? WHERE id = ?`, 113 time.Now().UTC().Format(time.RFC3339), apiKey.ID) 114 115 return &user, apiKey.ID, nil 116}