Initial MVP commit
All checks were successful
ci/woodpecker/push/build-image Pipeline was successful

This commit is contained in:
2026-03-17 08:35:16 -05:00
parent 8c092c2247
commit 27817f9e43
29 changed files with 1890 additions and 1 deletions

102
cmd/weatherapi/main.go Normal file
View File

@@ -0,0 +1,102 @@
package main
import (
"context"
"flag"
"fmt"
"log"
"net/http"
"gitea.maximumdirect.net/ejr/weatherapi/internal/adapters/httpapi"
adapterpg "gitea.maximumdirect.net/ejr/weatherapi/internal/adapters/postgres"
"gitea.maximumdirect.net/ejr/weatherapi/internal/application/alerts"
"gitea.maximumdirect.net/ejr/weatherapi/internal/application/forecasts"
"gitea.maximumdirect.net/ejr/weatherapi/internal/application/observations"
"gitea.maximumdirect.net/ejr/weatherapi/internal/application/units"
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/config"
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/constants"
"gitea.maximumdirect.net/ejr/weatherapi/internal/platform/datasource"
dspostgres "gitea.maximumdirect.net/ejr/weatherapi/internal/platform/datasource/postgres"
)
func main() {
cfgPath := flag.String("config", "config.yml", "path to YAML config")
addr := flag.String("addr", constants.DefaultHTTPAddr, "HTTP listen address")
dbName := flag.String("db-name", "", "optional configured database name to use")
flag.Parse()
cfg, err := config.Load(*cfgPath)
if err != nil {
log.Fatalf("load config: %v", err)
}
dbCfg, err := chooseDatabase(cfg, *dbName)
if err != nil {
log.Fatalf("select database: %v", err)
}
registry := datasource.NewRegistry()
if err := registry.Register(dspostgres.Factory{}); err != nil {
log.Fatalf("register datasource factories: %v", err)
}
ds, err := registry.Open(context.Background(), dbCfg)
if err != nil {
log.Fatalf("open datasource %q: %v", dbCfg.Name, err)
}
defer ds.Close()
pgDataSource, ok := ds.(*dspostgres.DataSource)
if !ok {
log.Fatalf("unsupported datasource type %T for driver %q", ds, dbCfg.Driver)
}
converter, err := newDefaultConverter()
if err != nil {
log.Fatalf("configure units: %v", err)
}
obsRepo := adapterpg.NewObservationRepository(pgDataSource.Pool())
fcRepo := adapterpg.NewForecastRepository(pgDataSource.Pool())
alertRepo := adapterpg.NewAlertRepository(pgDataSource.Pool())
obsSvc := observations.NewService(obsRepo, converter, constants.ObservationWindow)
fcSvc := forecasts.NewService(fcRepo, converter, constants.ForecastQueryLimit)
alertsSvc := alerts.NewService(alertRepo)
server := httpapi.NewServer(obsSvc, fcSvc, alertsSvc)
httpServer := &http.Server{
Addr: *addr,
Handler: server.Handler(),
}
log.Printf("weatherapi listening on %s (db=%s driver=%s units=%s)", *addr, dbCfg.Name, dbCfg.Driver, constants.DefaultOutputUnitSystem)
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("http server: %v", err)
}
}
func chooseDatabase(cfg *config.Config, name string) (config.DatabaseConfig, error) {
if cfg == nil {
return config.DatabaseConfig{}, fmt.Errorf("config is nil")
}
if name == "" {
return cfg.Databases[0], nil
}
db, ok := cfg.FindDatabase(name)
if !ok {
return config.DatabaseConfig{}, fmt.Errorf("database %q not found in config", name)
}
return db, nil
}
func newDefaultConverter() (units.Converter, error) {
switch constants.DefaultOutputUnitSystem {
case "us":
return units.USConverter{}, nil
default:
return nil, fmt.Errorf("unsupported default output unit system %q", constants.DefaultOutputUnitSystem)
}
}