The settings collection was a single global document, so every org shared one SMTP config, alert config, retention policy and ESO read token. GetSecretGroupDecryptedAny then flattened every org's secrets for a group into one map, meaning any tenant's token read every tenant's secrets. - settings gains org_id; GetSettings/SaveSettings/RotateSecretsReadToken/ GetWorkflowLogRetentionDays all take orgID - VerifySecretsReadToken replaced by ResolveSecretsReadToken, which resolves the org from the presented token's hash; the ESO endpoint derives its org from the token rather than a session, since it is called machine-to-machine - GetSecretGroupDecryptedAny deleted in favour of the org-scoped variant - settings and token-rotation routes now require owner/admin - offline sweep and log retention resolve org per server / per run - migration 0002 stamps the legacy settings doc with the default org Note: /api/settings now 403s for members; the web settings page needs a matching role check.
123 lines
3.4 KiB
Go
123 lines
3.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 err := db.Connect(mongoURI, dbName); err != nil {
|
|
log.Fatalf("failed to connect to MongoDB: %v", err)
|
|
}
|
|
log.Println("connected to MongoDB")
|
|
|
|
if err := services.EnsureAuthIndexes(); err != nil {
|
|
log.Printf("warning: 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.
|
|
if err := services.MigrateSettingsOrg(); err != nil {
|
|
log.Fatalf("settings org migration failed: %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.Printf("warning: 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
|
|
}
|