chore: initial commit of ai site platform
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
213
platform/internal/svc/servicecontext.go
Normal file
213
platform/internal/svc/servicecontext.go
Normal file
@@ -0,0 +1,213 @@
|
||||
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()
|
||||
}
|
||||
Reference in New Issue
Block a user