Load .env.<APP_ENV>, validate config at boot, keep secrets out of the image
`main.go` only ever loaded `.env`; the `APP_ENV` switch described in `.env.local` / `.env.production` did not exist, and a missing variable surfaced one restart at a time as a log.Fatalf inside db.Connect. config.Load now picks `.env.<APP_ENV>` (default local) then `.env`, with real environment winning, reads every setting into one typed Config and reports everything missing in one message. Production insists on a POS signing secret; local warns when DB_HOST is not a local address. db, redis and the image store take the Config instead of reading env themselves. Also: - livehub read MQTT_USERNAME while everything else uses MQTT_USER, so the console stream connected to the broker unauthenticated. Both accepted. - .dockerignore: `COPY . .` was baking .env.production into the image. Dockerfile sets APP_ENV=production. - Drop utils/config.go (dead viper loader) and create_table.go (unused, hardcoded production DSN); go mod tidy removes viper. - .env.example lists every variable the code reads; docs/ENVIRONMENT.md. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
44
main.go
44
main.go
@@ -3,12 +3,14 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"nearle/config"
|
||||
"nearle/db"
|
||||
"nearle/facade"
|
||||
"nearle/messaging"
|
||||
"nearle/models"
|
||||
"nearle/repositories"
|
||||
"nearle/routes"
|
||||
"nearle/utils"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strings"
|
||||
@@ -18,15 +20,14 @@ import (
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/fiber/v2/middleware/cors"
|
||||
"github.com/joho/godotenv"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func init() {
|
||||
godotenv.Load()
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Loads `.env.<APP_ENV>` (default `.env.local`) and `.env`, then checks
|
||||
// every required setting at once. Nothing below runs against a half
|
||||
// configured environment — see config/config.go for the precedence rules.
|
||||
cfg := config.MustLoad()
|
||||
|
||||
app := fiber.New()
|
||||
|
||||
@@ -38,13 +39,13 @@ func main() {
|
||||
}))
|
||||
|
||||
fmt.Println("🌐 Connecting to databases...")
|
||||
db.Connect()
|
||||
db.Connect(cfg)
|
||||
fmt.Println("✅ Database connections established!")
|
||||
|
||||
// Shared with the express backend. POS terminal presence lives here under a
|
||||
// TTL; optional, because losing the health board is an inconvenience and
|
||||
// losing a sale is not.
|
||||
db.InitRedis()
|
||||
db.InitRedis(cfg.Redis)
|
||||
|
||||
// Ensure schema is updated
|
||||
db.DB.AutoMigrate(&models.StockRequest{})
|
||||
@@ -263,7 +264,19 @@ func main() {
|
||||
log.Fatal("could not add catalogueuploads.sheetrows:", err)
|
||||
}
|
||||
|
||||
f := facade.NewFacade(db.DB, db.CatalogueDB)
|
||||
// The model behind scan-to-order. Optional: without EMBEDDING_PROVIDER the
|
||||
// search matches on words, which works but ranks less well.
|
||||
embedder, err := utils.NewEmbedder(cfg.Embedding)
|
||||
if err != nil {
|
||||
log.Fatal("embedding provider:", err)
|
||||
}
|
||||
if embedder == nil {
|
||||
log.Println("scan: EMBEDDING_PROVIDER not set, product search is text-only")
|
||||
} else {
|
||||
log.Printf("scan: product search uses %s/%s", cfg.Embedding.Provider, cfg.Embedding.Model)
|
||||
}
|
||||
|
||||
f := facade.NewFacade(db.DB, db.CatalogueDB, embedder)
|
||||
|
||||
routes.RegisterRoutes(app, f)
|
||||
|
||||
@@ -293,17 +306,10 @@ func main() {
|
||||
repositories.SetCatalogueNotifier(posMqtt)
|
||||
}
|
||||
|
||||
// Start server
|
||||
//
|
||||
// The port comes from APP_PORT, defaulting to the 1122 this has always
|
||||
// served on. It was hardcoded, which left `config.Load()`'s Port field
|
||||
// dead and the Dockerfile's `EXPOSE 1009` describing a port nothing
|
||||
// listened on — and made running a second copy locally, on a free port,
|
||||
// impossible without editing this line.
|
||||
port := os.Getenv("APP_PORT")
|
||||
if port == "" {
|
||||
port = "1122"
|
||||
}
|
||||
// Start server on APP_PORT (1122 locally, 1009 in production — see the
|
||||
// env files). Running a second copy beside something else is a one-line
|
||||
// change there rather than here.
|
||||
port := cfg.Port
|
||||
go func() {
|
||||
log.Printf("🚀 listening on :%s", port)
|
||||
if err := app.Listen(":" + port); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user