Files
2026-05-10 18:07:51 +02:00

132 lines
3.9 KiB
Go

package db
import (
"context"
"database/sql"
"time"
)
type PanelAdmin struct {
ID int64
DiscordID string
DiscordUsername string
DiscordAvatar string
PasswordHash string
TOTPSecret sql.NullString
TOTPEnabled bool
IsSuperadmin bool
CreatedAt time.Time
UpdatedAt time.Time
}
type PanelAdminRepo struct{ db *sql.DB }
func NewPanelAdminRepo(db *sql.DB) *PanelAdminRepo { return &PanelAdminRepo{db: db} }
const adminCols = `id,discord_id,discord_username,discord_avatar,password_hash,totp_secret,totp_enabled,is_superadmin,created_at,updated_at`
func (r *PanelAdminRepo) Create(ctx context.Context, discordID, username, passwordHash string, isSuperadmin bool) (*PanelAdmin, error) {
now := time.Now().UTC()
res, err := r.db.ExecContext(ctx,
`INSERT INTO panel_admins(discord_id,discord_username,password_hash,is_superadmin,created_at,updated_at)
VALUES(?,?,?,?,?,?)`,
discordID, username, passwordHash, boolInt(isSuperadmin), now, now)
if err != nil {
return nil, err
}
id, _ := res.LastInsertId()
return r.GetByID(ctx, id)
}
func (r *PanelAdminRepo) GetByID(ctx context.Context, id int64) (*PanelAdmin, error) {
row := r.db.QueryRowContext(ctx, `SELECT `+adminCols+` FROM panel_admins WHERE id=?`, id)
return scanAdmin(row)
}
func (r *PanelAdminRepo) GetByDiscordID(ctx context.Context, discordID string) (*PanelAdmin, error) {
row := r.db.QueryRowContext(ctx, `SELECT `+adminCols+` FROM panel_admins WHERE discord_id=?`, discordID)
return scanAdmin(row)
}
func (r *PanelAdminRepo) List(ctx context.Context) ([]*PanelAdmin, error) {
rows, err := r.db.QueryContext(ctx, `SELECT `+adminCols+` FROM panel_admins ORDER BY created_at`)
if err != nil {
return nil, err
}
defer rows.Close()
var out []*PanelAdmin
for rows.Next() {
a, err := scanAdminRows(rows)
if err != nil {
return nil, err
}
out = append(out, a)
}
return out, rows.Err()
}
func (r *PanelAdminRepo) UpdateDiscordProfile(ctx context.Context, id int64, username, avatar string) error {
_, err := r.db.ExecContext(ctx,
`UPDATE panel_admins SET discord_username=?,discord_avatar=?,updated_at=? WHERE id=?`,
username, avatar, time.Now().UTC(), id)
return err
}
func (r *PanelAdminRepo) UpdatePassword(ctx context.Context, id int64, hash string) error {
_, err := r.db.ExecContext(ctx,
`UPDATE panel_admins SET password_hash=?,updated_at=? WHERE id=?`,
hash, time.Now().UTC(), id)
return err
}
func (r *PanelAdminRepo) UpdateTOTP(ctx context.Context, id int64, secret string, enabled bool) error {
_, err := r.db.ExecContext(ctx,
`UPDATE panel_admins SET totp_secret=?,totp_enabled=?,updated_at=? WHERE id=?`,
secret, boolInt(enabled), time.Now().UTC(), id)
return err
}
func (r *PanelAdminRepo) ResetTOTP(ctx context.Context, id int64) error {
_, err := r.db.ExecContext(ctx,
`UPDATE panel_admins SET totp_secret=NULL,totp_enabled=0,updated_at=? WHERE id=?`,
time.Now().UTC(), id)
return err
}
func (r *PanelAdminRepo) Delete(ctx context.Context, discordID string) error {
_, err := r.db.ExecContext(ctx, `DELETE FROM panel_admins WHERE discord_id=?`, discordID)
return err
}
func scanAdmin(row *sql.Row) (*PanelAdmin, error) {
a, err := scanAdminFields(func(dest ...any) error { return row.Scan(dest...) })
if err == sql.ErrNoRows {
return nil, nil
}
return a, err
}
func scanAdminRows(rows *sql.Rows) (*PanelAdmin, error) {
return scanAdminFields(func(dest ...any) error { return rows.Scan(dest...) })
}
func scanAdminFields(scan func(...any) error) (*PanelAdmin, error) {
var a PanelAdmin
var totpEnabled, isSuperadmin int
err := scan(&a.ID, &a.DiscordID, &a.DiscordUsername, &a.DiscordAvatar,
&a.PasswordHash, &a.TOTPSecret, &totpEnabled, &isSuperadmin, &a.CreatedAt, &a.UpdatedAt)
if err != nil {
return nil, err
}
a.TOTPEnabled = totpEnabled != 0
a.IsSuperadmin = isSuperadmin != 0
return &a, nil
}
func boolInt(b bool) int {
if b {
return 1
}
return 0
}