Phase 1: Go backend scaffold
- Project structure: cmd/boosapi, internal/, pkg/ - Config loader (all env vars from Node.js original) - Database layer with SQLite/MySQL/Postgres support - Migration runner (sequential SQL files) - Health check endpoint (GET /healthz) - HTTP middleware: logger, auth, CORS, panic recovery - chi router with /admin, /v1, /api route groups - Graceful shutdown (SIGINT/SIGTERM) - Multi-stage Dockerfile (alpine, ~20MB image) - Initial schema migration (users, api_keys, sites, usage_logs) Signed-off-by: Boos4721 <boos4721@icloud.com>
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
// Package config loads application configuration from environment variables.
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Config holds all application configuration.
|
||||
type Config struct {
|
||||
// Server
|
||||
Port int `env:"PORT" default:"4000"`
|
||||
Host string `env:"HOST" default:"0.0.0.0"`
|
||||
|
||||
// Auth
|
||||
AuthToken string `env:"AUTH_TOKEN" required:"true"`
|
||||
ProxyToken string `env:"PROXY_TOKEN" required:"true"`
|
||||
|
||||
// Database
|
||||
DBType string `env:"DB_TYPE" default:"sqlite"`
|
||||
DBURL string `env:"DB_URL" default:""`
|
||||
DBSSL bool `env:"DB_SSL" default:"false"`
|
||||
|
||||
// Storage
|
||||
DataDir string `env:"DATA_DIR" default:"/app/data"`
|
||||
|
||||
// Redis (optional)
|
||||
RedisURL string `env:"REDIS_URL" default:""`
|
||||
|
||||
// Runtime
|
||||
TZ string `env:"TZ" default:"UTC"`
|
||||
LogLevel string `env:"LOG_LEVEL" default:"info"`
|
||||
JWTSecret string `env:"JWT_SECRET" default:""`
|
||||
|
||||
// Desktop (set by Electron wrapper, not user)
|
||||
Desktop bool `env:"BOOSAPI_DESKTOP" default:"false"`
|
||||
DesktopPort int `env:"BOOSAPI_DESKTOP_SERVER_PORT" default:"0"`
|
||||
DesktopDev string `env:"BOOSAPI_DESKTOP_DEV_SERVER_URL" default:""`
|
||||
DesktopExt string `env:"BOOSAPI_DESKTOP_EXTERNAL_SERVER_URL" default:""`
|
||||
|
||||
// Derived
|
||||
location *time.Location
|
||||
}
|
||||
|
||||
// Load reads configuration from environment variables.
|
||||
func Load() (*Config, error) {
|
||||
cfg := &Config{}
|
||||
|
||||
if v := os.Getenv("PORT"); v != "" {
|
||||
p, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cfg.Port = p
|
||||
}
|
||||
|
||||
if v := os.Getenv("HOST"); v != "" {
|
||||
cfg.Host = v
|
||||
}
|
||||
|
||||
cfg.AuthToken = os.Getenv("AUTH_TOKEN")
|
||||
if cfg.AuthToken == "" {
|
||||
cfg.AuthToken = "change-me-admin-token"
|
||||
}
|
||||
|
||||
cfg.ProxyToken = os.Getenv("PROXY_TOKEN")
|
||||
|
||||
if v := os.Getenv("DB_TYPE"); v != "" {
|
||||
cfg.DBType = v
|
||||
}
|
||||
|
||||
if v := os.Getenv("DB_URL"); v != "" {
|
||||
cfg.DBURL = v
|
||||
}
|
||||
|
||||
if v := os.Getenv("DB_SSL"); v == "true" || v == "1" {
|
||||
cfg.DBSSL = true
|
||||
}
|
||||
|
||||
if v := os.Getenv("DATA_DIR"); v != "" {
|
||||
cfg.DataDir = v
|
||||
}
|
||||
|
||||
if v := os.Getenv("REDIS_URL"); v != "" {
|
||||
cfg.RedisURL = v
|
||||
}
|
||||
|
||||
if v := os.Getenv("TZ"); v != "" {
|
||||
cfg.TZ = v
|
||||
}
|
||||
|
||||
if v := os.Getenv("LOG_LEVEL"); v != "" {
|
||||
cfg.LogLevel = v
|
||||
}
|
||||
|
||||
cfg.JWTSecret = os.Getenv("JWT_SECRET")
|
||||
|
||||
if v := os.Getenv("BOOSAPI_DESKTOP"); v == "1" {
|
||||
cfg.Desktop = true
|
||||
}
|
||||
if v := os.Getenv("BOOSAPI_DESKTOP_SERVER_PORT"); v != "" {
|
||||
p, _ := strconv.Atoi(v)
|
||||
cfg.DesktopPort = p
|
||||
}
|
||||
cfg.DesktopDev = os.Getenv("BOOSAPI_DESKTOP_DEV_SERVER_URL")
|
||||
cfg.DesktopExt = os.Getenv("BOOSAPI_DESKTOP_EXTERNAL_SERVER_URL")
|
||||
|
||||
loc, err := time.LoadLocation(cfg.TZ)
|
||||
if err != nil {
|
||||
loc = time.UTC
|
||||
}
|
||||
cfg.location = loc
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// Location returns the configured timezone location.
|
||||
func (c *Config) Location() *time.Location {
|
||||
return c.location
|
||||
}
|
||||
|
||||
// ListenAddr returns the address string for net/http to listen on.
|
||||
func (c *Config) ListenAddr() string {
|
||||
return c.Host + ":" + strconv.Itoa(c.Port)
|
||||
}
|
||||
|
||||
// DataPath returns the path under DataDir for a given subdirectory.
|
||||
func (c *Config) DataPath(sub string) string {
|
||||
return c.DataDir + "/" + sub
|
||||
}
|
||||
Reference in New Issue
Block a user