feat(server): auth indexes + default-org backfill migration
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/mrhid6/vantage/server/internal/db"
|
||||
"github.com/mrhid6/vantage/server/internal/models"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo"
|
||||
"go.mongodb.org/mongo-driver/v2/mongo/options"
|
||||
)
|
||||
|
||||
var scopedCollections = []string{
|
||||
"servers", "keys", "assignments", "secrets",
|
||||
"workflows", "workflow_steps", "workflow_runs",
|
||||
"audit", "monitors", "channels",
|
||||
}
|
||||
|
||||
// EnsureAuthIndexes creates unique indexes for the new auth collections.
|
||||
func EnsureAuthIndexes() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if _, err := db.Col("users").Indexes().CreateOne(ctx, mongo.IndexModel{
|
||||
Keys: bson.D{{Key: "email", Value: 1}},
|
||||
Options: options.Index().SetUnique(true),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Col("orgs").Indexes().CreateOne(ctx, mongo.IndexModel{
|
||||
Keys: bson.D{{Key: "slug", Value: 1}},
|
||||
Options: options.Index().SetUnique(true),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := db.Col("org_oidc").Indexes().CreateOne(ctx, mongo.IndexModel{
|
||||
Keys: bson.D{{Key: "org_id", Value: 1}},
|
||||
Options: options.Index().SetUnique(true),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunMigrations backfills a default org onto pre-existing documents. Idempotent
|
||||
// via a marker in the migrations collection.
|
||||
func RunMigrations() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
const marker = "0001_default_org_backfill"
|
||||
if n, _ := db.Col("migrations").CountDocuments(ctx, bson.M{"_id": marker}); n > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Only backfill if there is legacy data lacking org_id.
|
||||
needs := false
|
||||
for _, col := range scopedCollections {
|
||||
n, _ := db.Col(col).CountDocuments(ctx, bson.M{"org_id": bson.M{"$exists": false}})
|
||||
if n > 0 {
|
||||
needs = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if needs {
|
||||
var org models.Org
|
||||
err := db.Col("orgs").FindOne(ctx, bson.M{"slug": "default"}).Decode(&org)
|
||||
if err != nil {
|
||||
org = models.Org{OrgID: uuid.NewString(), Name: "Default", Slug: "default", CreatedAt: time.Now()}
|
||||
if _, err := db.Col("orgs").InsertOne(ctx, org); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, col := range scopedCollections {
|
||||
if _, err := db.Col(col).UpdateMany(ctx,
|
||||
bson.M{"org_id": bson.M{"$exists": false}},
|
||||
bson.M{"$set": bson.M{"org_id": org.OrgID}},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_, err := db.Col("migrations").InsertOne(ctx, bson.M{"_id": marker, "applied_at": time.Now()})
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user