Files
vantage/server/cmd/main.go
T
mrhid6 9ffae221ac
Server Deploy / deploy (push) Successful in 59s
fix: Fixed server install scripts
2026-07-22 12:59:05 +01:00

142 lines
4.4 KiB
Go

package main
import (
"context"
"log"
"os"
"time"
"github.com/gin-gonic/gin"
"github.com/mrhid6/vantage/server/internal/api"
"github.com/mrhid6/vantage/server/internal/auth"
"github.com/mrhid6/vantage/server/internal/db"
grpcserver "github.com/mrhid6/vantage/server/internal/grpc"
"github.com/mrhid6/vantage/server/internal/monitorsched"
"github.com/mrhid6/vantage/server/internal/services"
)
func main() {
mongoURI := getEnv("MONGO_URI", "mongodb://localhost:27017")
dbName := getEnv("MONGO_DB", "vantage")
// Agents dial gRPC directly, so there is no sane default: falling back to the
// public web host would hand every new agent a config pointing at a port that
// does not speak gRPC. Fail loudly at boot instead of at install time.
if os.Getenv("GRPC_HOST") == "" {
log.Fatal("GRPC_HOST is required (host:port agents dial for gRPC)")
}
if err := db.Connect(mongoURI, dbName); err != nil {
log.Fatalf("failed to connect to MongoDB: %v", err)
}
log.Println("connected to MongoDB")
// The unique indexes are a security property: GetUserByEmail does an
// unscoped FindOne, so duplicate (or blank) emails let the OIDC callback's
// cross-org guard compare against an arbitrary user, and duplicate org slugs
// make host-based org resolution pick one at random.
if err := services.EnsureAuthIndexes(); err != nil {
log.Fatalf("failed to ensure auth indexes: %v", err)
}
if err := services.RunMigrations(); err != nil {
log.Fatalf("migration failed: %v", err)
}
// Must run before the unique settings indexes are built, and before 0003:
// 0003 can create a "default" org, which would push 0002 into its ambiguous
// multi-org branch and leave the settings doc unstamped.
if err := services.MigrateSettingsOrg(); err != nil {
log.Fatalf("settings org migration failed: %v", err)
}
if err := services.MigrateMissedOrgScopes(); err != nil {
log.Fatalf("missed org scope migration failed: %v", err)
}
if err := services.EnsureSecretIndexes(); err != nil {
log.Printf("warning: failed to ensure secret indexes: %v", err)
}
// The unique indexes are a security property: duplicate settings docs make
// GetSettings return an arbitrary one, and duplicate ESO token hashes make
// ResolveSecretsReadToken pick an arbitrary org.
if err := services.EnsureSettingsIndexes(); err != nil {
log.Fatalf("failed to ensure settings indexes: %v", err)
}
if err := services.EnsureWorkflowIndexes(); err != nil {
log.Printf("warning: failed to ensure workflow indexes: %v", err)
}
if orgIDs, err := services.ListOrgIDs(); err != nil {
log.Printf("warning: failed to list orgs for default step seeding: %v", err)
} else {
for _, orgID := range orgIDs {
if created, updated, err := services.SeedDefaultSteps(orgID); err != nil {
log.Printf("warning: failed to seed default steps for org %s: %v", orgID, err)
} else {
log.Printf("default steps seeded for org %s: %d created, %d updated", orgID, created, updated)
}
}
}
services.StartLogSweeper()
redisAddr := getEnv("REDIS_ADDR", "localhost:6379")
if err := auth.InitRedis(redisAddr); err != nil {
log.Fatalf("failed to connect to Redis: %v", err)
}
log.Println("connected to Redis")
// Background goroutine to mark offline servers
go func() {
ticker := time.NewTicker(2 * time.Minute)
defer ticker.Stop()
for range ticker.C {
if err := services.MarkOfflineServers(); err != nil {
log.Printf("mark offline error: %v", err)
}
}
}()
// Start gRPC server
go func() {
if err := grpcserver.StartGRPC(9090); err != nil {
log.Fatalf("gRPC server error: %v", err)
}
}()
// Start the server-side monitor scheduler.
monitorsched.Start(context.Background())
// Start REST server
r := gin.New()
r.Use(gin.Recovery())
r.Use(gin.LoggerWithConfig(gin.LoggerConfig{SkipPaths: []string{"/api/console/tunnel"}}))
r.Use(corsMiddleware())
api.RegisterRoutes(r)
log.Println("REST server listening on :8080")
if err := r.Run(":8080"); err != nil {
log.Fatalf("REST server error: %v", err)
}
}
func corsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}