214 lines
6.0 KiB
Go
214 lines
6.0 KiB
Go
package svc
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"log"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
|
|
"aijianzhan/platform/internal/agentstore"
|
|
"aijianzhan/platform/internal/audit"
|
|
"aijianzhan/platform/internal/authx"
|
|
"aijianzhan/platform/internal/config"
|
|
"aijianzhan/platform/internal/crud"
|
|
"aijianzhan/platform/internal/dbsync"
|
|
"aijianzhan/platform/internal/invitestore"
|
|
"aijianzhan/platform/internal/license"
|
|
"aijianzhan/platform/internal/meta"
|
|
"aijianzhan/platform/internal/orgunitstore"
|
|
"aijianzhan/platform/internal/ratelimit"
|
|
"aijianzhan/platform/internal/rolestore"
|
|
"aijianzhan/platform/internal/schema"
|
|
"aijianzhan/platform/internal/smsstore"
|
|
"aijianzhan/platform/internal/storage"
|
|
"aijianzhan/platform/internal/tenantperm"
|
|
"aijianzhan/platform/internal/userstore"
|
|
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
type ServiceContext struct {
|
|
Config config.Config
|
|
Meta meta.Store
|
|
Schema schema.Runner
|
|
CRUD crud.Engine
|
|
Users userstore.Store
|
|
Agents agentstore.Store
|
|
Roles rolestore.Store
|
|
Invites invitestore.Store
|
|
OrgUnits orgunitstore.Store
|
|
Audit audit.Store
|
|
Objects storage.Store
|
|
Limiter *ratelimit.Limiter
|
|
DBPool *crud.DBPool
|
|
MemoryMode bool
|
|
DB *sql.DB
|
|
JWT authx.JWTConfig
|
|
DBSync *dbsync.Manager
|
|
TenantPerm tenantperm.Store
|
|
SMS *smsstore.Store
|
|
License *license.Manager
|
|
}
|
|
|
|
func NewServiceContext(c config.Config) *ServiceContext {
|
|
users := userstore.NewMemoryStore()
|
|
aud := audit.NewMemoryStore()
|
|
agents := agentstore.NewMemoryStore()
|
|
roles := rolestore.NewMemoryStore()
|
|
invites := invitestore.NewMemoryStore()
|
|
orgUnits := orgunitstore.NewMemoryStore()
|
|
pubBase := strings.TrimRight(c.PublicBaseURL, "/")
|
|
if pubBase == "" {
|
|
pubBase = "http://127.0.0.1:8180"
|
|
}
|
|
storageBase := c.Storage.PublicBase
|
|
if storageBase == "" {
|
|
storageBase = pubBase + "/api/v1/storage"
|
|
}
|
|
obj, err := storage.NewLocalStore(c.Storage.LocalRoot, storageBase)
|
|
if err != nil {
|
|
log.Printf("storage init: %v", err)
|
|
obj, _ = storage.NewLocalStore("./data/uploads", storageBase)
|
|
}
|
|
|
|
ctx := &ServiceContext{
|
|
Config: c,
|
|
Meta: meta.NewMemoryStore(),
|
|
Schema: schema.NoopRunner{},
|
|
CRUD: crud.NewMemoryEngine(),
|
|
Users: users,
|
|
Agents: agents,
|
|
Roles: roles,
|
|
Invites: invites,
|
|
OrgUnits: orgUnits,
|
|
Audit: aud,
|
|
TenantPerm: tenantperm.NewMemoryStore(),
|
|
Objects: obj,
|
|
Limiter: ratelimit.New(c.RateLimitPerMin),
|
|
MemoryMode: true,
|
|
SMS: smsstore.New(
|
|
time.Duration(c.SMS.CodeTTLSeconds)*time.Second,
|
|
time.Duration(c.SMS.ResendSeconds)*time.Second,
|
|
c.SMS.DevFixedCode,
|
|
),
|
|
JWT: authx.JWTConfig{
|
|
AccessSecret: c.Auth.AccessSecret,
|
|
AccessExpire: c.Auth.AccessExpire,
|
|
},
|
|
}
|
|
if ctx.JWT.AccessSecret == "" {
|
|
ctx.JWT.AccessSecret = "dev-only-change-me"
|
|
}
|
|
|
|
if c.DataSource != "" && !c.DryRun {
|
|
db, err := sql.Open("postgres", c.DataSource)
|
|
if err != nil {
|
|
log.Printf("postgres open failed, fallback memory: %v", err)
|
|
} else if err := db.Ping(); err != nil {
|
|
log.Printf("postgres ping failed, fallback memory: %v", err)
|
|
_ = db.Close()
|
|
} else {
|
|
db.SetMaxOpenConns(20)
|
|
db.SetMaxIdleConns(5)
|
|
migCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
|
|
defer cancel()
|
|
if err := meta.EnsureSchema(migCtx, db); err != nil {
|
|
log.Printf("meta migrate failed: %v", err)
|
|
ctx.DB = db
|
|
ctx.Schema = &schema.PostgresRunner{DB: db, AdminDSN: adminDSN(c.DataSource)}
|
|
pool := crud.NewDBPool(db, c.DataSource)
|
|
ctx.DBPool = pool
|
|
ctx.CRUD = crud.NewPostgresEngine(db, pool)
|
|
ctx.MemoryMode = false
|
|
} else {
|
|
ctx.DB = db
|
|
ctx.Schema = &schema.PostgresRunner{DB: db, AdminDSN: adminDSN(c.DataSource)}
|
|
pool := crud.NewDBPool(db, c.DataSource)
|
|
ctx.DBPool = pool
|
|
ctx.CRUD = crud.NewPostgresEngine(db, pool)
|
|
ctx.Meta = meta.NewPostgresStore(db)
|
|
ctx.Users = userstore.NewPostgresStore(db)
|
|
ctx.Agents = agentstore.NewPostgresStore(db)
|
|
ctx.Roles = rolestore.NewPostgresStore(db)
|
|
ctx.Invites = invitestore.NewPostgresStore(db)
|
|
ctx.OrgUnits = orgunitstore.NewPostgresStore(db)
|
|
ctx.Audit = audit.NewPostgresStore(db)
|
|
ctx.TenantPerm = tenantperm.NewPostgresStore(db)
|
|
ctx.MemoryMode = false
|
|
log.Printf("postgres engine + meta + users + agents + roles + invites + org_units + audit + tenant_perm enabled")
|
|
}
|
|
}
|
|
}
|
|
userstore.EnsureDemoUser(context.Background(), ctx.Users)
|
|
userstore.EnsurePlatformAdminUser(context.Background(), ctx.Users)
|
|
if ctx.Users != nil {
|
|
if err := ctx.Users.EnsureTenantSlugs(context.Background()); err != nil {
|
|
log.Printf("ensure tenant slugs: %v", err)
|
|
}
|
|
}
|
|
_ = ctx.Roles.EnsureDefaults(context.Background(), 1)
|
|
if ctx.TenantPerm != nil {
|
|
_ = ctx.TenantPerm.EnsureDefault(context.Background(), 1)
|
|
authx.EntitlementChecker = func(c context.Context, tenantID int64, perm string) bool {
|
|
ok, err := ctx.TenantPerm.Allows(c, tenantID, perm)
|
|
if err != nil {
|
|
log.Printf("entitlement check: %v", err)
|
|
return false
|
|
}
|
|
return ok
|
|
}
|
|
}
|
|
if c.DryRun {
|
|
ctx.Schema = schema.NoopRunner{}
|
|
}
|
|
|
|
// 跨库同步中间件(默认启用)
|
|
if c.DBSync.Enabled {
|
|
dir := c.DBSync.DataDir
|
|
if dir == "" {
|
|
dir = "./data/dbsync"
|
|
}
|
|
store, err := dbsync.NewFileStore(dir)
|
|
if err != nil {
|
|
log.Printf("dbsync store: %v", err)
|
|
} else {
|
|
ctx.DBSync = dbsync.NewManager(store)
|
|
log.Printf("dbsync middleware enabled (dir=%s)", dir)
|
|
}
|
|
}
|
|
|
|
licCfg := c.License
|
|
if licCfg.Enabled {
|
|
if strings.TrimSpace(licCfg.ControlSecret) == "" {
|
|
licCfg.ControlSecret = c.Auth.IssueSecret
|
|
}
|
|
if strings.TrimSpace(licCfg.SignSecret) == "" {
|
|
licCfg.SignSecret = licCfg.ControlSecret
|
|
}
|
|
}
|
|
lic, err := license.NewManager(licCfg)
|
|
if err != nil {
|
|
log.Printf("license manager: %v", err)
|
|
} else {
|
|
ctx.License = lic
|
|
if lic.Enabled() {
|
|
if ctx.DB != nil {
|
|
lic.AttachMirror(license.NewPGMirror(ctx.DB))
|
|
}
|
|
log.Printf("license lease enabled (leases=%s state=%s)", lic.Path(), lic.StatePath())
|
|
}
|
|
}
|
|
return ctx
|
|
}
|
|
|
|
func adminDSN(dsn string) string {
|
|
u, err := url.Parse(dsn)
|
|
if err != nil {
|
|
return dsn
|
|
}
|
|
u.Path = "/postgres"
|
|
return u.String()
|
|
}
|