Files
flowsight/backend/internal/config/config.go
T
asepharyana 21e8568108
CI / go test + web typecheck (push) Canceled after 0s
CI / build + deploy (Nix) — flowsight (push) Canceled after 0s
feat: CI + Nix deploy + prod sectors-hackaton.asepharyana.my.id
- Backend serve web dist (WEB_DIST_DIR + SPA fallback, /api 404 JSON)
- flake.nix: single flowsight derivation (go 1.25 + pnpm build)
- deploy/flowsight.service: systemd on :4022 via bws-exec
- .github/workflows/ci.yml: go test + tsc gate, nix build, attic, SSH deploy
2026-09-15 14:45:23 +07:00

91 lines
2.6 KiB
Go

// Package config loads FlowSight runtime configuration from the environment.
// Secrets come only from env vars, never from files.
package config
import (
"os"
"strconv"
"strings"
"github.com/joho/godotenv"
)
// Config holds all runtime settings for the FlowSight backend.
type Config struct {
Port string
SectorsAPIKey string
SectorsBaseURL string
DBPath string
RedisURL string
DemoUserKey string
LLMBaseURL string
LLMAPIKey string
LLMTriage string
LLMSynth string
TelegramBotToken string
TelegramChatID string
DiscordWebhookURL string
CreditCapPerCycle int
Watchlist []string
// StaticDir serves the prebuilt web dist (WEB_DIST_DIR). Empty = API only.
StaticDir string
}
// HasSectorsKey reports whether live Sectors API calls are possible.
// Without a key the server runs in offline/seed mode.
func (c Config) HasSectorsKey() bool { return strings.TrimSpace(c.SectorsAPIKey) != "" }
// HasLLM reports whether LLM-backed refinement is available.
func (c Config) HasLLM() bool {
return strings.TrimSpace(c.LLMBaseURL) != "" && strings.TrimSpace(c.LLMAPIKey) != ""
}
func getenv(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}
func getenvInt(key string, def int) int {
v := os.Getenv(key)
if v == "" {
return def
}
n, err := strconv.Atoi(v)
if err != nil {
return def
}
return n
}
// Load reads configuration from the environment (.env supported for dev).
func Load() Config {
_ = godotenv.Load()
watch := getenv("WATCHLIST", "BBCA,BBRI,BMRI,TLKM,ASII")
tickers := make([]string, 0, 8)
for _, t := range strings.Split(watch, ",") {
if t = strings.ToUpper(strings.TrimSpace(t)); t != "" {
tickers = append(tickers, t)
}
}
return Config{
Port: getenv("PORT", "8080"),
SectorsAPIKey: os.Getenv("SECTORS_API_KEY"),
SectorsBaseURL: getenv("SECTORS_BASE_URL", "https://api.sectors.app/v2/"),
DBPath: getenv("DB_PATH", "data/flowsight.db"),
RedisURL: os.Getenv("REDIS_URL"),
DemoUserKey: getenv("DEMO_USER_KEY", "demo"),
LLMBaseURL: os.Getenv("LLM_BASE_URL"),
LLMAPIKey: os.Getenv("LLM_API_KEY"),
LLMTriage: getenv("LLM_MODEL_TRIAGE", "gpt-4o-mini"),
LLMSynth: getenv("LLM_MODEL_SYNTH", "gpt-4o"),
TelegramBotToken: os.Getenv("TELEGRAM_BOT_TOKEN"),
TelegramChatID: os.Getenv("TELEGRAM_CHAT_ID"),
DiscordWebhookURL: os.Getenv("DISCORD_WEBHOOK_URL"),
CreditCapPerCycle: getenvInt("CREDIT_CAP_PER_CYCLE", 120),
Watchlist: tickers,
StaticDir: os.Getenv("WEB_DIST_DIR"),
}
}