Files
baby-nas/internal/db/queries.go
T

287 lines
7.0 KiB
Go

package db
import (
"database/sql"
"encoding/json"
"fmt"
"time"
)
func parseTime(value string) time.Time {
t, err := time.Parse(time.RFC3339, value)
if err != nil {
t, _ = time.Parse("2006-01-02 15:04:05", value)
}
return t
}
func encodeJSONStrings(values []string) (string, error) {
if values == nil {
values = []string{}
}
data, err := json.Marshal(values)
if err != nil {
return "", err
}
return string(data), nil
}
func decodeJSONStrings(raw string) ([]string, error) {
if raw == "" {
return []string{}, nil
}
var values []string
if err := json.Unmarshal([]byte(raw), &values); err != nil {
return nil, err
}
return values, nil
}
func scanSambaShare(row interface {
Scan(dest ...any) error
}) (SambaShare, error) {
var share SambaShare
var readOnly, guestOK int
var validUsers, validGroups, createdAt, updatedAt string
if err := row.Scan(
&share.ID,
&share.Name,
&share.Path,
&share.Comment,
&readOnly,
&guestOK,
&validUsers,
&validGroups,
&createdAt,
&updatedAt,
); err != nil {
return SambaShare{}, err
}
share.ReadOnly = readOnly != 0
share.GuestOK = guestOK != 0
var err error
share.ValidUsers, err = decodeJSONStrings(validUsers)
if err != nil {
return SambaShare{}, err
}
share.ValidGroups, err = decodeJSONStrings(validGroups)
if err != nil {
return SambaShare{}, err
}
share.CreatedAt = parseTime(createdAt)
share.UpdatedAt = parseTime(updatedAt)
return share, nil
}
func (d *DB) MarkDirty(module string) error {
_, err := d.conn.Exec(
`INSERT INTO dirty_modules (module, marked_at) VALUES (?, datetime('now'))
ON CONFLICT(module) DO UPDATE SET marked_at = datetime('now')`,
module,
)
if err != nil {
return fmt.Errorf("mark dirty %q: %w", module, err)
}
return nil
}
func (d *DB) ClearDirty(module string) error {
_, err := d.conn.Exec(`DELETE FROM dirty_modules WHERE module = ?`, module)
if err != nil {
return fmt.Errorf("clear dirty %q: %w", module, err)
}
return nil
}
func (d *DB) IsDirty(module string) (bool, error) {
var count int
err := d.conn.QueryRow(`SELECT COUNT(1) FROM dirty_modules WHERE module = ?`, module).Scan(&count)
if err != nil {
return false, fmt.Errorf("is dirty %q: %w", module, err)
}
return count > 0, nil
}
func (d *DB) ListDirty() ([]DirtyModule, error) {
rows, err := d.conn.Query(`SELECT module, marked_at FROM dirty_modules ORDER BY marked_at ASC`)
if err != nil {
return nil, fmt.Errorf("list dirty: %w", err)
}
defer rows.Close()
var modules []DirtyModule
for rows.Next() {
var module DirtyModule
var markedAt string
if err := rows.Scan(&module.Module, &markedAt); err != nil {
return nil, fmt.Errorf("scan dirty: %w", err)
}
module.MarkedAt = parseTime(markedAt)
modules = append(modules, module)
}
if err := rows.Err(); err != nil {
return nil, err
}
return modules, nil
}
func (d *DB) AppendApplyLog(module, message string, success bool) error {
successInt := 0
if success {
successInt = 1
}
_, err := d.conn.Exec(
`INSERT INTO apply_log (module, message, success, created_at) VALUES (?, ?, ?, datetime('now'))`,
module, message, successInt,
)
if err != nil {
return fmt.Errorf("append apply log: %w", err)
}
return nil
}
func (d *DB) ListApplyLog(limit int) ([]ApplyLogEntry, error) {
if limit <= 0 {
limit = 100
}
rows, err := d.conn.Query(
`SELECT id, module, message, success, created_at FROM apply_log ORDER BY id DESC LIMIT ?`,
limit,
)
if err != nil {
return nil, fmt.Errorf("list apply log: %w", err)
}
defer rows.Close()
var entries []ApplyLogEntry
for rows.Next() {
var entry ApplyLogEntry
var success int
var createdAt string
if err := rows.Scan(&entry.ID, &entry.Module, &entry.Message, &success, &createdAt); err != nil {
return nil, fmt.Errorf("scan apply log: %w", err)
}
entry.Success = success != 0
entry.CreatedAt = parseTime(createdAt)
entries = append(entries, entry)
}
return entries, rows.Err()
}
func (d *DB) ListSambaShares() ([]SambaShare, error) {
rows, err := d.conn.Query(`
SELECT id, name, path, comment, read_only, guest_ok, valid_users, valid_groups, created_at, updated_at
FROM samba_shares ORDER BY name ASC`)
if err != nil {
return nil, fmt.Errorf("list samba shares: %w", err)
}
defer rows.Close()
var shares []SambaShare
for rows.Next() {
share, err := scanSambaShare(rows)
if err != nil {
return nil, fmt.Errorf("scan samba share: %w", err)
}
shares = append(shares, share)
}
return shares, rows.Err()
}
func (d *DB) GetSambaShare(id int64) (SambaShare, error) {
row := d.conn.QueryRow(`
SELECT id, name, path, comment, read_only, guest_ok, valid_users, valid_groups, created_at, updated_at
FROM samba_shares WHERE id = ?`, id)
share, err := scanSambaShare(row)
if err == sql.ErrNoRows {
return SambaShare{}, fmt.Errorf("samba share not found")
}
if err != nil {
return SambaShare{}, fmt.Errorf("get samba share: %w", err)
}
return share, nil
}
func (d *DB) CreateSambaShare(share SambaShare) (SambaShare, error) {
validUsers, err := encodeJSONStrings(share.ValidUsers)
if err != nil {
return SambaShare{}, err
}
validGroups, err := encodeJSONStrings(share.ValidGroups)
if err != nil {
return SambaShare{}, err
}
readOnly := 0
if share.ReadOnly {
readOnly = 1
}
guestOK := 0
if share.GuestOK {
guestOK = 1
}
result, err := d.conn.Exec(`
INSERT INTO samba_shares (name, path, comment, read_only, guest_ok, valid_users, valid_groups)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
share.Name, share.Path, share.Comment, readOnly, guestOK, validUsers, validGroups,
)
if err != nil {
return SambaShare{}, fmt.Errorf("create samba share: %w", err)
}
id, err := result.LastInsertId()
if err != nil {
return SambaShare{}, fmt.Errorf("last insert id: %w", err)
}
return d.GetSambaShare(id)
}
func (d *DB) UpdateSambaShare(id int64, share SambaShare) (SambaShare, error) {
validUsers, err := encodeJSONStrings(share.ValidUsers)
if err != nil {
return SambaShare{}, err
}
validGroups, err := encodeJSONStrings(share.ValidGroups)
if err != nil {
return SambaShare{}, err
}
readOnly := 0
if share.ReadOnly {
readOnly = 1
}
guestOK := 0
if share.GuestOK {
guestOK = 1
}
result, err := d.conn.Exec(`
UPDATE samba_shares
SET name = ?, path = ?, comment = ?, read_only = ?, guest_ok = ?, valid_users = ?, valid_groups = ?, updated_at = datetime('now')
WHERE id = ?`,
share.Name, share.Path, share.Comment, readOnly, guestOK, validUsers, validGroups, id,
)
if err != nil {
return SambaShare{}, fmt.Errorf("update samba share: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return SambaShare{}, err
}
if rows == 0 {
return SambaShare{}, fmt.Errorf("samba share not found")
}
return d.GetSambaShare(id)
}
func (d *DB) DeleteSambaShare(id int64) error {
result, err := d.conn.Exec(`DELETE FROM samba_shares WHERE id = ?`, id)
if err != nil {
return fmt.Errorf("delete samba share: %w", err)
}
rows, err := result.RowsAffected()
if err != nil {
return err
}
if rows == 0 {
return fmt.Errorf("samba share not found")
}
return nil
}