Initial import of map-asset-gateway

This commit is contained in:
2026-04-14 13:10:10 +08:00
commit 1893ad3857
30 changed files with 6676 additions and 0 deletions
+108
View File
@@ -0,0 +1,108 @@
package config
import (
"fmt"
"os"
"path/filepath"
"strconv"
)
type Config struct {
Host string
Port int
APIBaseURL string
TilePublicBaseURL string
VectorPublicBaseURL string
SQLitePath string
BasemapScanRoot string
VectorScanRoot string
AutoScanOnStart bool
Env string
ServiceName string
ServiceVersion string
Commit string
}
func Load() (Config, error) {
if err := LoadEnvDefaults(); err != nil {
return Config{}, err
}
port, err := intEnv("API_PORT", 8910)
if err != nil {
return Config{}, fmt.Errorf("parse API_PORT: %w", err)
}
autoScanOnStart, err := boolEnv("AUTO_SCAN_ON_START", true)
if err != nil {
return Config{}, fmt.Errorf("parse AUTO_SCAN_ON_START: %w", err)
}
cfg := Config{
Host: stringEnv("API_HOST", "127.0.0.1"),
Port: port,
APIBaseURL: stringEnv("API_BASE_URL", ""),
TilePublicBaseURL: stringEnv("TILE_PUBLIC_BASE_URL", ""),
VectorPublicBaseURL: stringEnv("VECTOR_PUBLIC_BASE_URL", ""),
SQLitePath: stringEnv("SQLITE_PATH", filepath.Clean(filepath.Join("data", "map-asset-gateway.db"))),
BasemapScanRoot: stringEnv("BASEMAP_SCAN_ROOT", filepath.Clean(filepath.Join("data", "tiles"))),
VectorScanRoot: stringEnv("VECTOR_SCAN_ROOT", filepath.Clean(filepath.Join("data", "geojson"))),
AutoScanOnStart: autoScanOnStart,
Env: stringEnv("APP_ENV", "dev"),
ServiceName: stringEnv("SERVICE_NAME", "map-asset-gateway"),
ServiceVersion: stringEnv("SERVICE_VERSION", "0.2.0"),
Commit: stringEnv("SERVICE_COMMIT", "local"),
}
if cfg.APIBaseURL == "" {
cfg.APIBaseURL = deriveBaseURL(cfg.Host, cfg.Port)
}
if cfg.TilePublicBaseURL == "" {
cfg.TilePublicBaseURL = cfg.APIBaseURL
}
if cfg.VectorPublicBaseURL == "" {
cfg.VectorPublicBaseURL = cfg.APIBaseURL
}
return cfg, nil
}
func (c Config) ListenAddr() string {
return fmt.Sprintf("%s:%d", c.Host, c.Port)
}
func deriveBaseURL(host string, port int) string {
if host == "" || host == "0.0.0.0" {
host = "127.0.0.1"
}
return fmt.Sprintf("http://%s:%d", host, port)
}
func stringEnv(key, fallback string) string {
value, ok := os.LookupEnv(key)
if ok && value != "" {
return value
}
return fallback
}
func intEnv(key string, fallback int) (int, error) {
value, ok := os.LookupEnv(key)
if ok && value != "" {
parsed, err := strconv.Atoi(value)
if err != nil {
return 0, err
}
return parsed, nil
}
return fallback, nil
}
func boolEnv(key string, fallback bool) (bool, error) {
value, ok := os.LookupEnv(key)
if ok && value != "" {
parsed, err := strconv.ParseBool(value)
if err != nil {
return false, err
}
return parsed, nil
}
return fallback, nil
}
@@ -0,0 +1,73 @@
package config
import (
"bufio"
"fmt"
"os"
"path/filepath"
"strings"
)
func LoadEnvDefaults() error {
if explicit := strings.TrimSpace(os.Getenv("APP_ENV_FILE")); explicit != "" {
if err := loadEnvFile(explicit); err != nil {
return fmt.Errorf("load APP_ENV_FILE %s: %w", explicit, err)
}
return nil
}
for _, candidate := range []string{
filepath.Join("config", "service.env"),
filepath.Join("..", "..", "config", "service.env"),
} {
if _, err := os.Stat(candidate); err == nil {
if err := loadEnvFile(candidate); err != nil {
return fmt.Errorf("load env file %s: %w", candidate, err)
}
return nil
}
}
return nil
}
func loadEnvFile(path string) error {
file, err := os.Open(path)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
lineNo := 0
for scanner.Scan() {
lineNo++
line := strings.TrimSpace(scanner.Text())
if line == "" || strings.HasPrefix(line, "#") {
continue
}
if strings.HasPrefix(line, "export ") {
line = strings.TrimSpace(strings.TrimPrefix(line, "export "))
}
key, value, found := strings.Cut(line, "=")
if !found {
return fmt.Errorf("line %d: missing '='", lineNo)
}
key = strings.TrimSpace(key)
value = strings.TrimSpace(value)
value = strings.Trim(value, `"'`)
if key == "" {
return fmt.Errorf("line %d: empty key", lineNo)
}
if _, exists := os.LookupEnv(key); exists {
continue
}
if err := os.Setenv(key, value); err != nil {
return fmt.Errorf("line %d: set %s: %w", lineNo, key, err)
}
}
if err := scanner.Err(); err != nil {
return err
}
return nil
}