Files
2026-02-12 17:52:32 +03:00

155 lines
3.7 KiB
Go

package config
import (
"fmt"
"os"
"regexp"
"github.com/BurntSushi/toml"
)
var timeFormatRe = regexp.MustCompile(`^\d{2}:\d{2}$`)
type Config struct {
Memos MemosConfig `toml:"memos"`
Database DatabaseConfig `toml:"database"`
Search SearchConfig `toml:"search"`
Telegram TelegramConfig `toml:"telegram"`
Web WebConfig `toml:"web"`
General GeneralConfig `toml:"general"`
}
type MemosConfig struct {
URL string `toml:"url"`
Token string `toml:"token"`
PublicURL string `toml:"public_url"`
}
type DatabaseConfig struct {
Path string `toml:"path"`
}
type SearchConfig struct {
CooldownDays int `toml:"cooldown_days"`
RelaxedCooldownDays int `toml:"relaxed_cooldown_days"`
PageSize int `toml:"page_size"`
MaxYearsBack int `toml:"max_years_back"`
PreferOlder bool `toml:"prefer_older"`
TierWeights TierWeights `toml:"tier_weights"`
}
type TierWeights struct {
Tier1 int `toml:"tier1"`
Tier2 int `toml:"tier2"`
Tier3 int `toml:"tier3"`
Tier4 int `toml:"tier4"`
Tier5 int `toml:"tier5"`
Tier6 int `toml:"tier6"`
Tier7 int `toml:"tier7"`
}
func (tw TierWeights) Sum() int {
return tw.Tier1 + tw.Tier2 + tw.Tier3 + tw.Tier4 + tw.Tier5 + tw.Tier6 + tw.Tier7
}
func (tw TierWeights) AsSlice() [7]int {
return [7]int{tw.Tier1, tw.Tier2, tw.Tier3, tw.Tier4, tw.Tier5, tw.Tier6, tw.Tier7}
}
type TelegramConfig struct {
Enabled bool `toml:"enabled"`
Token string `toml:"token"`
ChatID int64 `toml:"chat_id"`
SendAt string `toml:"send_at"`
}
type WebConfig struct {
Listen string `toml:"listen"`
}
type GeneralConfig struct {
Timezone string `toml:"timezone"`
LogLevel string `toml:"log_level"`
AllowLoadMore bool `toml:"allow_load_more"`
}
func Load(path string) (*Config, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read config: %w", err)
}
var cfg Config
if err := toml.Unmarshal(data, &cfg); err != nil {
return nil, fmt.Errorf("parse config: %w", err)
}
setDefaults(&cfg)
if err := validate(&cfg); err != nil {
return nil, fmt.Errorf("validate config: %w", err)
}
return &cfg, nil
}
func setDefaults(cfg *Config) {
if cfg.Database.Path == "" {
cfg.Database.Path = "remembos.db"
}
if cfg.Search.CooldownDays == 0 {
cfg.Search.CooldownDays = 90
}
if cfg.Search.RelaxedCooldownDays == 0 {
cfg.Search.RelaxedCooldownDays = 30
}
if cfg.Search.PageSize == 0 {
cfg.Search.PageSize = 50
}
if cfg.Search.MaxYearsBack == 0 {
cfg.Search.MaxYearsBack = 10
}
if cfg.Search.TierWeights.Sum() == 0 {
cfg.Search.TierWeights = TierWeights{
Tier1: 35, Tier2: 15, Tier3: 15,
Tier4: 12, Tier5: 10, Tier6: 5, Tier7: 8,
}
}
if cfg.Web.Listen == "" {
cfg.Web.Listen = "127.0.0.1:8080"
}
if cfg.General.Timezone == "" {
cfg.General.Timezone = "Europe/Moscow"
}
if cfg.General.LogLevel == "" {
cfg.General.LogLevel = "info"
}
if cfg.Telegram.SendAt == "" {
cfg.Telegram.SendAt = "09:00"
}
}
func validate(cfg *Config) error {
if cfg.Memos.URL == "" {
return fmt.Errorf("memos.url is required")
}
if cfg.Memos.Token == "" {
return fmt.Errorf("memos.token is required")
}
if sum := cfg.Search.TierWeights.Sum(); sum != 100 {
return fmt.Errorf("search.tier_weights must sum to 100, got %d", sum)
}
if cfg.Telegram.Enabled {
if cfg.Telegram.Token == "" {
return fmt.Errorf("telegram.token is required when telegram.enabled = true")
}
if cfg.Telegram.ChatID == 0 {
return fmt.Errorf("telegram.chat_id is required when telegram.enabled = true")
}
if !timeFormatRe.MatchString(cfg.Telegram.SendAt) {
return fmt.Errorf("telegram.send_at must be in HH:MM format, got %q", cfg.Telegram.SendAt)
}
}
return nil
}