Instances stay read-only until a licence is set. No licence is generated by the control plane, which keeps the signing key out of it entirely.
147 lines
4.4 KiB
Go
147 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")
|
|
|
|
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")
|
|
|
|
// Migrations 0001 to 0003 still speak the pre-rename shape (orgs, org_id),
|
|
// so they must run before 0004 renames everything underneath them.
|
|
if err := services.RunMigrations(); err != nil {
|
|
log.Fatalf("migration failed: %v", err)
|
|
}
|
|
// 0002 must precede 0003: 0003 can create a "default" org, which pushes
|
|
// 0002 into its ambiguous multi-org branch.
|
|
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)
|
|
}
|
|
|
|
// 0004 renames orgs to instances. It must run BEFORE the index builders:
|
|
// EnsureAuthIndexes creates instances.slug, which would create an empty
|
|
// instances collection and make 0004 refuse to rename onto it.
|
|
migCtx, migCancel := context.WithTimeout(context.Background(), 10*time.Minute)
|
|
migErr := services.MigrateOrgToInstance(migCtx, db.Database)
|
|
migCancel()
|
|
if migErr != nil {
|
|
log.Fatalf("instance rename migration failed: %v", migErr)
|
|
}
|
|
|
|
assertCtx, assertCancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
assertErr := services.AssertNoScopedCollectionMissed(assertCtx, db.Database)
|
|
assertCancel()
|
|
if assertErr != nil {
|
|
log.Fatalf("scoped collection check failed: %v", assertErr)
|
|
}
|
|
|
|
if err := services.EnsureAuthIndexes(); err != nil {
|
|
log.Fatalf("failed to ensure auth indexes: %v", err)
|
|
}
|
|
|
|
if err := services.EnsureSecretIndexes(); err != nil {
|
|
log.Printf("warning: failed to ensure secret indexes: %v", err)
|
|
}
|
|
|
|
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 instanceIDs, err := services.ListInstanceIDs(); err != nil {
|
|
log.Printf("warning: failed to list instances for default step seeding: %v", err)
|
|
} else {
|
|
for _, instanceID := range instanceIDs {
|
|
if created, updated, err := services.SeedDefaultSteps(instanceID); err != nil {
|
|
log.Printf("warning: failed to seed default steps for instance %s: %v", instanceID, err)
|
|
} else {
|
|
log.Printf("default steps seeded for instance %s: %d created, %d updated", instanceID, 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")
|
|
|
|
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)
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
if err := grpcserver.StartGRPC(9090); err != nil {
|
|
log.Fatalf("gRPC server error: %v", err)
|
|
}
|
|
}()
|
|
|
|
monitorsched.Start(context.Background())
|
|
|
|
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
|
|
}
|