Files
kanban/backend/users.go
T
egutierrez c9e15513c7 chore: auto-commit (23 archivos)
- app.md
- backend/dist/assets/index-CFDWXN9Z.js
- backend/dist/index.html
- backend/handlers.go
- backend/main.go
- backend/users.go
- e2e/smoke_live.sh
- frontend/src/App.tsx
- frontend/src/api.ts
- frontend/src/components/CardChatPanel.tsx
- ...

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-21 18:22:44 +02:00

149 lines
3.9 KiB
Go

package main
import (
"database/sql"
"errors"
"fmt"
"strings"
"fn-registry/functions/infra"
)
type User struct {
ID string `json:"id"`
Username string `json:"username"`
DisplayName string `json:"display_name"`
Color string `json:"color"`
IsAdmin bool `json:"is_admin"`
CreatedAt string `json:"created_at"`
}
var (
errUserNotFound = errors.New("user not found")
errUserAlreadyExists = errors.New("username already exists")
errInvalidCredentials = errors.New("invalid credentials")
)
func (db *DB) CreateUser(username, password, displayName string) (*User, error) {
username = strings.TrimSpace(strings.ToLower(username))
if username == "" {
return nil, fmt.Errorf("username required")
}
if len(password) < 4 {
return nil, fmt.Errorf("password must be at least 4 characters")
}
hash, err := infra.PasswordHash(password, 0)
if err != nil {
return nil, fmt.Errorf("hash: %w", err)
}
u := User{ID: newID(), Username: username, DisplayName: displayName, CreatedAt: nowRFC3339()}
_, err = db.conn.Exec(
`INSERT INTO users (id, username, password_hash, display_name, created_at) VALUES (?, ?, ?, ?, ?)`,
u.ID, u.Username, hash, u.DisplayName, u.CreatedAt,
)
if err != nil {
if strings.Contains(err.Error(), "UNIQUE") {
return nil, errUserAlreadyExists
}
return nil, err
}
return &u, nil
}
func (db *DB) GetUserByID(id string) (*User, error) {
var u User
var isAdmin int
err := db.conn.QueryRow(
`SELECT id, username, display_name, color, is_admin, created_at FROM users WHERE id=?`, id,
).Scan(&u.ID, &u.Username, &u.DisplayName, &u.Color, &isAdmin, &u.CreatedAt)
if errors.Is(err, sql.ErrNoRows) {
return nil, errUserNotFound
}
if err != nil {
return nil, err
}
u.IsAdmin = isAdmin == 1
return &u, nil
}
func (db *DB) IsAdmin(userID string) (bool, error) {
if userID == "" {
return false, nil
}
var n int
err := db.conn.QueryRow(`SELECT COALESCE(is_admin, 0) FROM users WHERE id=?`, userID).Scan(&n)
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
return n == 1, err
}
func (db *DB) GetUserByUsername(username string) (*User, string, error) {
username = strings.TrimSpace(strings.ToLower(username))
var u User
var hash string
var isAdmin int
err := db.conn.QueryRow(
`SELECT id, username, display_name, color, is_admin, created_at, password_hash FROM users WHERE username=?`, username,
).Scan(&u.ID, &u.Username, &u.DisplayName, &u.Color, &isAdmin, &u.CreatedAt, &hash)
if errors.Is(err, sql.ErrNoRows) {
return nil, "", errUserNotFound
}
if err != nil {
return nil, "", err
}
u.IsAdmin = isAdmin == 1
return &u, hash, nil
}
func (db *DB) ListUsers() ([]User, error) {
rows, err := db.conn.Query(`SELECT id, username, display_name, color, is_admin, created_at FROM users ORDER BY username`)
if err != nil {
return nil, err
}
defer rows.Close()
out := []User{}
for rows.Next() {
var u User
var isAdmin int
if err := rows.Scan(&u.ID, &u.Username, &u.DisplayName, &u.Color, &isAdmin, &u.CreatedAt); err != nil {
return nil, err
}
u.IsAdmin = isAdmin == 1
out = append(out, u)
}
return out, rows.Err()
}
func (db *DB) Authenticate(username, password string) (*User, error) {
u, hash, err := db.GetUserByUsername(username)
if err != nil {
if errors.Is(err, errUserNotFound) {
return nil, errInvalidCredentials
}
return nil, err
}
if err := infra.PasswordVerify(password, hash); err != nil {
return nil, errInvalidCredentials
}
return u, nil
}
func (db *DB) CountUsers() (int, error) {
var n int
if err := db.conn.QueryRow(`SELECT COUNT(*) FROM users`).Scan(&n); err != nil {
return 0, err
}
return n, nil
}
func (db *DB) UpdateUserColor(id, color string) error {
_, err := db.conn.Exec(`UPDATE users SET color=? WHERE id=?`, color, id)
return err
}
func (db *DB) DeleteSessionByToken(token string) error {
_, err := db.conn.Exec(`DELETE FROM sessions WHERE token=?`, token)
return err
}