fix(config): fix configuration management

This commit is contained in:
2025-10-13 16:47:09 +02:00
parent 3d85723de9
commit fac5eb8a66
7 changed files with 113 additions and 58 deletions

View File

@@ -1,7 +1,8 @@
version: '3' version: '3'
vars: vars:
API_IMAGE_NAME: realz REGISTRY: reg.cadoles.com/lacanne
API_IMAGE_NAME: realzapi
API_IMAGE_VERSION: v1.0.0 API_IMAGE_VERSION: v1.0.0
DB_IMAGE_NAME: realzdb DB_IMAGE_NAME: realzdb
DB_IMAGE_VERSION: v1.0.0 DB_IMAGE_VERSION: v1.0.0
@@ -14,15 +15,25 @@ tasks:
build-api: build-api:
desc: Construction de l'image Docker realz API desc: Construction de l'image Docker realz API
cmds: cmds:
- docker build -t {{.API_IMAGE_NAME}}:{{.API_IMAGE_VERSION}} -t {{.API_IMAGE_NAME}}:latest -f docker/Dockerfile.api . - docker build -t {{.REGISTRY}}/{{.API_IMAGE_NAME}}:{{.API_IMAGE_VERSION}} -t {{.REGISTRY}}/{{.API_IMAGE_NAME}}:latest -f docker/Dockerfile.api .
silent: true silent: true
build-db: build-db:
desc: Construction de l'image Docker realz DB desc: Construction de l'image Docker realz DB
cmds: cmds:
- docker build -t {{.DB_IMAGE_NAME}}:{{.DB_IMAGE_VERSION}} -t {{.DB_IMAGE_NAME}}:latest -f docker/Dockerfile.db . - docker build -t {{.REGISTRY}}/{{.DB_IMAGE_NAME}}:{{.DB_IMAGE_VERSION}} -t {{.REGISTRY}}/{{.DB_IMAGE_NAME}}:latest -f docker/Dockerfile.db .
silent: true silent: true
build: build:
desc: Construction des images Docker realz desc: Construction des images Docker realz
deps: [ build-api, build-db ] deps: [ build-api, build-db ]
publish:
deps: [ build-api, build-db ]
cmds:
- docker login {{.REGISTRY}}
- docker push {{.REGISTRY}}/{{.API_IMAGE_NAME}}:{{.API_IMAGE_VERSION}}
- docker push {{.REGISTRY}}/{{.API_IMAGE_NAME}}:latest
- docker push {{.REGISTRY}}/{{.DB_IMAGE_NAME}}:{{.DB_IMAGE_VERSION}}
- docker push {{.REGISTRY}}/{{.DB_IMAGE_NAME}}:latest
silent: true

View File

@@ -1,7 +1,8 @@
package api package main
import ( import (
"database/sql" "database/sql"
"fmt"
"log" "log"
"net/http" "net/http"
@@ -99,11 +100,13 @@ func getCorrectedAltitude(db *database.Service) gin.HandlerFunc {
} }
func main() { func main() {
cfg, err := config.Load() cfg, err := config.LoadConfig(".")
if err != nil { if err != nil {
log.Fatalf("Error loading configuration: %v", err) log.Fatalf("Error loading configuration: %v", err)
} }
fmt.Printf("Config: %+v\n", cfg)
db, err := database.NewService(&cfg.Database) db, err := database.NewService(&cfg.Database)
if err != nil { if err != nil {
log.Fatalf("Could not initialize database: %v", err) log.Fatalf("Could not initialize database: %v", err)
@@ -113,8 +116,14 @@ func main() {
router := gin.Default() router := gin.Default()
// Servir les fichiers statiques (index.html, etc.) depuis le répertoire courant. // Configure les proxies de confiance pour Gin
router.StaticFS("/", http.Dir(".")) if len(cfg.Server.TrustedProxies) > 0 {
router.SetTrustedProxies(cfg.Server.TrustedProxies)
log.Printf("Trusted proxies set to: %v", cfg.Server.TrustedProxies)
}
// Servir les fichiers statiques de l'UI go-app
router.Static("/web", "./ui/web")
// La route est maintenant un POST pour recevoir un corps de requête JSON. // La route est maintenant un POST pour recevoir un corps de requête JSON.
router.POST("/getorthocorrec", getOrthometricCorrection(db)) router.POST("/getorthocorrec", getOrthometricCorrection(db))

View File

@@ -1,5 +1,8 @@
server: server:
address: "0.0.0.0:8088" address: "0.0.0.0:8088"
trustes_proxies:
- "127.0.0.1"
- "::1"
database: database:
user: "realz" user: "realz"

View File

@@ -6,8 +6,9 @@ COPY go.mod go.sum ./
RUN go mod download RUN go mod download
COPY . . COPY . .
RUN go build RUN go build cmd/api/realz.go
FROM scratch FROM scratch
COPY --from=builder /app/realz /realz COPY --from=builder /app/realz /realz
COPY --from=builder /app/index.html /index.html
ENTRYPOINT ["/realz"] ENTRYPOINT ["/realz"]

View File

@@ -1,17 +1,47 @@
services: services:
api:
image: reg.cadoles.com/lacanne/realzapi:latest
env_file: .env
restart: unless-stopped
depends_on:
postgis: postgis:
image: realz condition: service_healthy
container_name: postgis_initialized ports:
- "8088:8080" # Expose API port (host:container)
environment: environment:
- POSTGRES_USER=${POSTGRES_USER} APP_SERVER_ADDRESS: 0.0.0.0:8080
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD} APP_DATABASE_HOST: postgis
- POSTGRES_DB=${POSTGRES_DB} # Base de données pour l'administration, pas celle de l'app APP_DATABASE_PORT: 5432
- PGRST_AUTHUSER=${PGRST_AUTHUSER} APP_DATABASE_USER: ${POSTGRES_USER}
- PGRST_PASSWORD=${PGRST_PASSWORD} APP_DATABASE_PASSWORD: ${POSTGRES_PASSWORD}
APP_DATABASE_DBNAME: ${POSTGRES_DB}
APP_DATABASE_SSLMODE: disable
networks:
- realz-net
postgis:
image: reg.cadoles.com/lacanne/realzdb:latest
container_name: postgis_initialized
restart: unless-stopped
env_file: .env
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
ports: ports:
- "5433:5432" - "5433:5432"
volumes: volumes:
- postgis_data:/var/lib/postgresql/data - postgis_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- realz-net
volumes: volumes:
postgis_data: postgis_data:
networks:
realz-net:

View File

@@ -2,66 +2,67 @@ package config
import ( import (
"fmt" "fmt"
"log"
"os"
"strings" "strings"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
// Config holds all configuration for the application. // Config représente la structure globale de la configuration de l'application.
type Config struct { type Config struct {
Database DatabaseConfig `mapstructure:"database"` Server ServerConfig
Server ServerConfig `mapstructure:"server"` Database DatabaseConfig
} }
// DatabaseConfig holds database connection configuration. // ServerConfig contient les paramètres de configuration du serveur HTTP.
type DatabaseConfig struct {
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
Dbname string `mapstructure:"dbname"`
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
Sslmode string `mapstructure:"sslmode"`
}
// ServerConfig holds server-specific configuration.
type ServerConfig struct { type ServerConfig struct {
Address string `mapstructure:"address"` Address string `mapstructure:"address"`
TrustedProxies []string `mapstructure:"trusted_proxies"` // Liste des IPs de proxies de confiance
} }
// Load reads configuration from file and environment variables. // DatabaseConfig contient les paramètres de configuration de la base de données.
func Load() (*Config, error) { type DatabaseConfig struct {
// 0. Configuration for environment variables Host string `mapstructure:"host"`
viper.SetEnvPrefix("APP") // Variables must start with "APP_" (e.g., APP_SERVER_ADDRESS) Port int `mapstructure:"port"`
User string `mapstructure:"user"`
Password string `mapstructure:"password"`
DBName string `mapstructure:"dbname"`
SSLMode string `mapstructure:"sslmode"`
}
// 1. Base configuration file // LoadConfig charge la configuration depuis un fichier et les variables d'environnement.
viper.SetConfigName("config") // config.yaml func LoadConfig(path string) (config Config, err error) {
viper.AddConfigPath(path)
viper.SetConfigName("config")
viper.SetConfigType("yaml") viper.SetConfigType("yaml")
viper.AddConfigPath(".")
// Read the base file. Don't stop if it's not found. viper.SetDefault("server.address", ":8080")
if err := viper.ReadInConfig(); err != nil { viper.SetDefault("server.trusted_proxies", "127.0.0.1, ::1")
viper.SetDefault("database.host", "localhost")
viper.SetDefault("database.port", 5432)
viper.SetDefault("database.user", "default_user")
viper.SetDefault("database.password", "")
viper.SetDefault("database.dbname", "default_db")
viper.SetDefault("database.sslmode", "disable")
// L'ordre ici est important
// 1. D'abord, on lit le fichier
err = viper.ReadInConfig()
if err != nil {
if _, ok := err.(viper.ConfigFileNotFoundError); !ok { if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
return nil, fmt.Errorf("error reading base config file config.yaml: %w", err) return
}
}
// 2. Override with environment-specific file (via APP_ENV)
if env := os.Getenv("APP_ENV"); env != "" {
viper.SetConfigName("config." + env) // e.g., config.prod.yaml
if err := viper.MergeInConfig(); err != nil {
log.Printf("Warning: could not load config file for environment '%s': %v", env, err)
} }
} }
// 2. Ensuite, on prépare Viper à lire les variables d'environnement
viper.SetEnvPrefix("APP")
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv() viper.AutomaticEnv() // Dit à Viper de chercher les variables d'environnement
var cfg Config // 3. Enfin, on déverse le résultat final dans la struct.
if err := viper.Unmarshal(&cfg); err != nil { // C'est à ce moment que la surcharge a lieu.
return nil, fmt.Errorf("unable to decode config into struct: %w", err) fmt.Printf("[%v]\n", config.Database.DBName)
}
return &cfg, nil err = viper.Unmarshal(&config)
return
} }

View File

@@ -16,8 +16,8 @@ type Service struct {
// NewService initializes and returns a new database service. // NewService initializes and returns a new database service.
func NewService(cfg *config.DatabaseConfig) (*Service, error) { func NewService(cfg *config.DatabaseConfig) (*Service, error) {
connStr := fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%d sslmode=%s", connStr := fmt.Sprintf("user=%s password=%s dbname=%s host=%s port=%d sslmode=%s",
cfg.User, cfg.Password, cfg.Dbname, cfg.User, cfg.Password, cfg.DBName,
cfg.Host, cfg.Port, cfg.Sslmode, cfg.Host, cfg.Port, cfg.SSLMode,
) )
db, err := sql.Open("postgres", connStr) db, err := sql.Open("postgres", connStr)