Files

367 lines
11 KiB
Go
Raw Permalink Normal View History

2026-04-01 11:47:03 +04:00
package main
import (
"database/sql"
"fmt"
2026-04-03 09:55:36 +04:00
"strconv"
2026-04-01 11:47:03 +04:00
"strings"
)
2026-04-03 09:55:36 +04:00
const (
settingViewProofInView = "view_proof_in_view"
settingLiveActiveView = "live_active_view"
settingLiveShowGroupLive = "live_show_group_live"
settingLiveGroupDisplayMode = "live_group_display_mode"
settingLiveGroupFixedCode = "live_group_fixed_code"
settingLiveShowPrelimTie = "live_show_prelim_tie"
settingLiveShowPrelimOverall = "live_show_prelim_overall"
settingLiveShowFinalGroups = "live_show_final_groups"
settingLiveFinalDisplayMode = "live_final_display_mode"
settingLiveFinalFixedGroup = "live_final_fixed_group"
settingLiveShowFinalTie = "live_show_final_tie"
settingLiveShowPodium = "live_show_podium"
settingLiveRotationInterval = "live_rotation_interval_sec"
settingLiveRotationPlayers = "live_rotation_player_count"
)
func defaultLiveModeSettings() LiveModeSettings {
return LiveModeSettings{
ActiveView: "group_live",
ShowGroupLive: true,
GroupDisplayMode: "rotate",
GroupFixedCode: "",
ShowPrelimTie: true,
ShowPrelimOverall: true,
ShowFinalGroups: true,
FinalDisplayMode: "rotate",
FinalFixedGroup: 1,
ShowFinalTie: true,
ShowPodium: true,
RotationIntervalSec: 5,
RotationPlayerCount: 12,
}
}
2026-04-01 11:47:03 +04:00
func (a *App) readSettings() (AppSettings, error) {
2026-04-03 09:55:36 +04:00
live := defaultLiveModeSettings()
viewProofRaw, err := a.readSettingValue(settingViewProofInView)
2026-04-01 11:47:03 +04:00
if err != nil {
2026-04-03 09:55:36 +04:00
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingViewProofInView, err)
}
if raw, err := a.readSettingValue(settingLiveShowGroupLive); err == nil {
live.ShowGroupLive = settingBool(raw)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowGroupLive, err)
}
if raw, err := a.readSettingValue(settingLiveActiveView); err == nil {
live.ActiveView = normalizeLiveActiveView(raw, live.ActiveView)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveActiveView, err)
}
if raw, err := a.readSettingValue(settingLiveGroupDisplayMode); err == nil {
live.GroupDisplayMode = normalizeLiveDisplayMode(raw, live.GroupDisplayMode)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveGroupDisplayMode, err)
}
if raw, err := a.readSettingValue(settingLiveGroupFixedCode); err == nil {
live.GroupFixedCode = strings.ToUpper(strings.TrimSpace(raw))
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveGroupFixedCode, err)
}
if raw, err := a.readSettingValue(settingLiveShowPrelimTie); err == nil {
live.ShowPrelimTie = settingBool(raw)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowPrelimTie, err)
}
if raw, err := a.readSettingValue(settingLiveShowPrelimOverall); err == nil {
live.ShowPrelimOverall = settingBool(raw)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowPrelimOverall, err)
}
if raw, err := a.readSettingValue(settingLiveShowFinalGroups); err == nil {
live.ShowFinalGroups = settingBool(raw)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowFinalGroups, err)
}
if raw, err := a.readSettingValue(settingLiveFinalDisplayMode); err == nil {
live.FinalDisplayMode = normalizeLiveDisplayMode(raw, live.FinalDisplayMode)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveFinalDisplayMode, err)
}
if raw, err := a.readSettingValue(settingLiveFinalFixedGroup); err == nil {
live.FinalFixedGroup = normalizeLiveFinalFixedGroup(raw, live.FinalFixedGroup)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveFinalFixedGroup, err)
}
if raw, err := a.readSettingValue(settingLiveShowFinalTie); err == nil {
live.ShowFinalTie = settingBool(raw)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowFinalTie, err)
}
if raw, err := a.readSettingValue(settingLiveShowPodium); err == nil {
live.ShowPodium = settingBool(raw)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveShowPodium, err)
}
if raw, err := a.readSettingValue(settingLiveRotationInterval); err == nil {
live.RotationIntervalSec = normalizeRotationInterval(raw, live.RotationIntervalSec)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveRotationInterval, err)
}
if raw, err := a.readSettingValue(settingLiveRotationPlayers); err == nil {
live.RotationPlayerCount = normalizeRotationPlayerCount(raw, live.RotationPlayerCount)
} else {
return AppSettings{}, fmt.Errorf("read app setting %s: %w", settingLiveRotationPlayers, err)
}
return AppSettings{
ViewProofInView: settingBool(viewProofRaw),
LiveMode: live,
}, nil
}
func (a *App) updateSettings(req AdminSettingsUpdateRequest) error {
tx, err := a.db.Begin()
if err != nil {
return fmt.Errorf("begin settings transaction: %w", err)
}
defer tx.Rollback()
hasUpdate := false
if req.ViewProofInView != nil {
hasUpdate = true
if err := upsertSetting(tx, settingViewProofInView, settingString(*req.ViewProofInView)); err != nil {
return err
}
}
if req.LiveMode != nil {
patch := req.LiveMode
if patch.ActiveView != nil {
hasUpdate = true
value := normalizeLiveActiveView(*patch.ActiveView, "group_live")
if err := upsertSetting(tx, settingLiveActiveView, value); err != nil {
return err
}
}
if patch.ShowGroupLive != nil {
hasUpdate = true
if err := upsertSetting(tx, settingLiveShowGroupLive, settingString(*patch.ShowGroupLive)); err != nil {
return err
}
}
if patch.GroupDisplayMode != nil {
hasUpdate = true
value := normalizeLiveDisplayMode(*patch.GroupDisplayMode, "rotate")
if err := upsertSetting(tx, settingLiveGroupDisplayMode, value); err != nil {
return err
}
}
if patch.GroupFixedCode != nil {
hasUpdate = true
value := strings.ToUpper(strings.TrimSpace(*patch.GroupFixedCode))
if err := upsertSetting(tx, settingLiveGroupFixedCode, value); err != nil {
return err
}
}
if patch.ShowPrelimTie != nil {
hasUpdate = true
if err := upsertSetting(tx, settingLiveShowPrelimTie, settingString(*patch.ShowPrelimTie)); err != nil {
return err
}
}
if patch.ShowPrelimOverall != nil {
hasUpdate = true
if err := upsertSetting(tx, settingLiveShowPrelimOverall, settingString(*patch.ShowPrelimOverall)); err != nil {
return err
}
}
if patch.ShowFinalGroups != nil {
hasUpdate = true
if err := upsertSetting(tx, settingLiveShowFinalGroups, settingString(*patch.ShowFinalGroups)); err != nil {
return err
}
}
if patch.FinalDisplayMode != nil {
hasUpdate = true
value := normalizeLiveDisplayMode(*patch.FinalDisplayMode, "rotate")
if err := upsertSetting(tx, settingLiveFinalDisplayMode, value); err != nil {
return err
}
}
if patch.FinalFixedGroup != nil {
hasUpdate = true
value := normalizeLiveFinalFixedGroupInt(*patch.FinalFixedGroup)
if err := upsertSetting(tx, settingLiveFinalFixedGroup, strconv.Itoa(value)); err != nil {
return err
}
}
if patch.ShowFinalTie != nil {
hasUpdate = true
if err := upsertSetting(tx, settingLiveShowFinalTie, settingString(*patch.ShowFinalTie)); err != nil {
return err
}
}
if patch.ShowPodium != nil {
hasUpdate = true
if err := upsertSetting(tx, settingLiveShowPodium, settingString(*patch.ShowPodium)); err != nil {
return err
}
}
if patch.RotationIntervalSec != nil {
hasUpdate = true
value := normalizeRotationIntervalInt(*patch.RotationIntervalSec)
if err := upsertSetting(tx, settingLiveRotationInterval, strconv.Itoa(value)); err != nil {
return err
}
}
if patch.RotationPlayerCount != nil {
hasUpdate = true
value := normalizeRotationPlayerCountInt(*patch.RotationPlayerCount)
if err := upsertSetting(tx, settingLiveRotationPlayers, strconv.Itoa(value)); err != nil {
return err
}
2026-04-01 11:47:03 +04:00
}
}
2026-04-03 09:55:36 +04:00
if !hasUpdate {
return fmt.Errorf("no settings to update")
}
if err := tx.Commit(); err != nil {
return fmt.Errorf("commit settings transaction: %w", err)
}
return nil
2026-04-01 11:47:03 +04:00
}
2026-04-03 09:55:36 +04:00
func upsertSetting(tx *sql.Tx, key string, value string) error {
_, err := tx.Exec(`
2026-04-01 11:47:03 +04:00
INSERT INTO app_settings(key, value, updated_at)
2026-04-03 09:55:36 +04:00
VALUES(?, ?, CURRENT_TIMESTAMP)
2026-04-01 11:47:03 +04:00
ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = CURRENT_TIMESTAMP
2026-04-03 09:55:36 +04:00
`, key, value)
2026-04-01 11:47:03 +04:00
if err != nil {
2026-04-03 09:55:36 +04:00
return fmt.Errorf("update app setting %s: %w", key, err)
2026-04-01 11:47:03 +04:00
}
return nil
}
2026-04-03 09:55:36 +04:00
func (a *App) readSettingValue(key string) (string, error) {
var raw string
err := a.db.QueryRow(`SELECT value FROM app_settings WHERE key = ?`, key).Scan(&raw)
if err != nil {
if err == sql.ErrNoRows {
return "", nil
}
return "", err
}
return raw, nil
}
2026-04-01 11:47:03 +04:00
func settingBool(value string) bool {
switch strings.TrimSpace(strings.ToLower(value)) {
case "1", "true", "yes", "on":
return true
default:
return false
}
}
func settingString(value bool) string {
if value {
return "1"
}
return "0"
}
2026-04-03 09:55:36 +04:00
func normalizeLiveDisplayMode(value string, fallback string) string {
normalized := strings.TrimSpace(strings.ToLower(value))
if normalized == "fixed" {
return "fixed"
}
if normalized == "rotate" {
return "rotate"
}
if strings.TrimSpace(strings.ToLower(fallback)) == "fixed" {
return "fixed"
}
return "rotate"
}
func normalizeLiveFinalFixedGroup(value string, fallback int) int {
parsed, err := strconv.Atoi(strings.TrimSpace(value))
if err != nil {
return normalizeLiveFinalFixedGroupInt(fallback)
}
return normalizeLiveFinalFixedGroupInt(parsed)
}
func normalizeLiveFinalFixedGroupInt(value int) int {
if value == 2 {
return 2
}
return 1
}
func normalizeRotationInterval(raw string, fallback int) int {
parsed, err := strconv.Atoi(strings.TrimSpace(raw))
if err != nil {
return normalizeRotationIntervalInt(fallback)
}
return normalizeRotationIntervalInt(parsed)
}
func normalizeRotationIntervalInt(value int) int {
if value < 3 {
return 3
}
if value > 30 {
return 30
}
return value
}
func normalizeRotationPlayerCount(raw string, fallback int) int {
parsed, err := strconv.Atoi(strings.TrimSpace(raw))
if err != nil {
return normalizeRotationPlayerCountInt(fallback)
}
return normalizeRotationPlayerCountInt(parsed)
}
func normalizeRotationPlayerCountInt(value int) int {
if value < 3 {
return 3
}
if value > 40 {
return 40
}
return value
}
func normalizeLiveActiveView(value string, fallback string) string {
switch strings.TrimSpace(strings.ToLower(value)) {
case "group_live", "prelim_tie", "prelim_overall", "final_groups", "final_tie", "podium":
return strings.TrimSpace(strings.ToLower(value))
default:
if strings.TrimSpace(strings.ToLower(fallback)) != "" {
return strings.TrimSpace(strings.ToLower(fallback))
}
return "group_live"
}
}