mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-20 01:50:02 +00:00
84 lines
2.9 KiB
Go
84 lines
2.9 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package config
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
|
|
"github.com/MeshCore-Beacon/beacon-server/internal/scopestore"
|
|
)
|
|
|
|
// Seeder is the database interface required to seed config data on startup.
|
|
type Seeder interface {
|
|
UpsertIATA(ctx context.Context, iata string) error
|
|
UpsertIATADetails(ctx context.Context, iata string, name string, lat, lng *float64) error
|
|
UpsertIATABorder(ctx context.Context, iata string, border json.RawMessage) error
|
|
UpsertRegion(ctx context.Context, slug, name, description string, displayOrder int, centerLat, centerLng *float64, zoomLevel *int) (int32, error)
|
|
SetRegionIATAs(ctx context.Context, regionID int32, iatas []string) error
|
|
UpsertTransportScope(ctx context.Context, name, displayName string, transportKey, keyFingerprint []byte) error
|
|
SetChannelConfigScopes(ctx context.Context, fingerprints [][]byte, regions []string) error
|
|
}
|
|
|
|
// Seed applies config-defined regions, IATA overrides to the database.
|
|
// It is safe to call on every startup — all operations are upserts.
|
|
func Seed(ctx context.Context, cfg *Config, db Seeder) error {
|
|
slog.Info(fmt.Sprintf("config: seeding %d IATAs, %d regions, %d scopes", len(cfg.IATAs), len(cfg.Regions), len(cfg.Scopes)), "component", "config")
|
|
// IATA overrides
|
|
for iata, details := range cfg.IATAs {
|
|
if err := db.UpsertIATADetails(ctx, iata, details.Name, details.Lat, details.Lng); err != nil {
|
|
return err
|
|
}
|
|
if details.BorderFile != "" {
|
|
raw, err := os.ReadFile(details.BorderFile)
|
|
if err != nil {
|
|
return fmt.Errorf("iata %s: reading border file %s: %w", iata, details.BorderFile, err)
|
|
}
|
|
border, err := ValidateBorder(raw)
|
|
if err != nil {
|
|
return fmt.Errorf("iata %s: invalid border in %s: %w", iata, details.BorderFile, err)
|
|
}
|
|
if err := db.UpsertIATABorder(ctx, iata, border); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
// Regions
|
|
for _, r := range cfg.Regions {
|
|
id, err := db.UpsertRegion(ctx, r.Slug, r.Name, r.Description, r.DisplayOrder, r.CenterLat, r.CenterLng, r.ZoomLevel)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, iata := range r.IATAs {
|
|
if err := db.UpsertIATA(ctx, iata); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// Config owns the member list, including when it takes over an imported slug.
|
|
if err := db.SetRegionIATAs(ctx, id, r.IATAs); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
// Channel region placement
|
|
var fingerprints [][]byte
|
|
var regions []string
|
|
for _, scope := range cfg.ChannelScopes() {
|
|
fingerprints, regions = append(fingerprints, scope.Fingerprint), append(regions, scope.Region)
|
|
}
|
|
if err := db.SetChannelConfigScopes(ctx, fingerprints, regions); err != nil {
|
|
return err
|
|
}
|
|
// Transport Codes
|
|
for _, s := range cfg.Scopes {
|
|
entry := scopestore.FromName(s.Name)
|
|
if err := db.UpsertTransportScope(ctx, entry.Name, "", entry.TransportKey, entry.KeyFingerprint); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|