Files
OpenFlare/backend/openflare/plugins/server/plugin.go
T

143 lines
4.8 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
// Package server 装载 OpenFlare 控制面业务路由:站点/区域、Cloudflare 接入、Pages 部署、
// WAF、节点与回源管理以及边缘协议接口。平台路径(user/admin/cap/health)由 Wavelet
// 域插件注册,本插件只挂 OpenFlare 自己的业务。
package server
import (
"Wavelet/core"
"Wavelet/core/contracts"
"Wavelet/openflare/plugins/server/domain/observability/chwriter"
ofrouter "Wavelet/openflare/plugins/server/httpapi"
ofgeoip "Wavelet/openflare/plugins/server/kernel/geoip"
"Wavelet/openflare/plugins/server/kernel/ofevents"
"Wavelet/openflare/plugins/server/kernel/ofupload"
"Wavelet/openflare/plugins/server/kernel/publicconfig"
"Wavelet/openflare/plugins/server/kernel/repository"
"Wavelet/openflare/plugins/server/kernel/repository/logstore"
"Wavelet/openflare/plugins/server/kernel/runtimeconfig"
oftask "Wavelet/openflare/plugins/server/kernel/task"
"Wavelet/openflare/plugins/server/migrate"
"Wavelet/pkg/logger"
"Wavelet/plugins/infra/database"
"context"
"embed"
"reflect"
_ "Wavelet/docs"
"Wavelet/openflare/plugins/server/kernel/credential"
adminservice "Wavelet/plugins/domain/admin/service"
"net/http"
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)
//go:embed migrate/postgres/*.sql migrate/sqlite/*.sql
var serverMigrations embed.FS
// Plugin 实现 core.Plugin,是 OpenFlare 控制面的装载入口。
type Plugin struct{}
// New 创建 server 插件。
func New() *Plugin { return &Plugin{} }
// Name 返回插件标识。
func (p *Plugin) Name() string { return "server" }
// Inject waits for platform services so Apply runs after Wavelet domain plugins.
func (p *Plugin) Inject() []reflect.Type {
return []reflect.Type{
reflect.TypeFor[contracts.DBService](),
reflect.TypeFor[contracts.CacheService](),
reflect.TypeFor[contracts.UserService](),
reflect.TypeFor[contracts.AuthService](),
reflect.TypeFor[contracts.TaskService](),
reflect.TypeFor[contracts.StorageService](),
}
}
// Apply 声明 OpenFlare 业务 HTTP 路由树、公共配置、推送事件与异步任务。
func (p *Plugin) Apply(ctx *core.Context) error {
var chCfg database.ClickHouseConfig
_ = ctx.Config().Bind("clickhouse", &chCfg)
runtimeconfig.Set(runtimeconfig.Snapshot{
SessionSecret: ctx.Config().String("app.session_secret", ""),
DatabaseEnabled: ctx.Config().Bool("database.enabled", false),
ClickHouse: chCfg,
})
credential.SetSessionSecret(runtimeconfig.SessionSecret())
ctx.Migrations().Register("server", serverMigrations)
if err := migrate.UpClickHouse(); err != nil {
return err
}
if ts, err := core.Inject[contracts.TaskService](ctx); err == nil && ts != nil {
oftask.SetService(ts)
} else {
core.When[contracts.TaskService](ctx, oftask.SetService)
}
if user, err := core.Inject[contracts.UserService](ctx); err == nil && user != nil {
repository.SetUserService(user)
} else {
core.When[contracts.UserService](ctx, repository.SetUserService)
}
if storage, err := core.Inject[contracts.StorageService](ctx); err == nil && storage != nil {
ofupload.SetStorage(storage)
} else {
core.When[contracts.StorageService](ctx, ofupload.SetStorage)
}
core.Provide[contracts.PublicConfigProvider](ctx, publicconfig.New(ctx))
if pr, err := core.Inject[contracts.PushRegistry](ctx); err == nil {
for _, meta := range ofevents.All() {
pr.RegisterBuiltInEvent(meta)
}
_ = pr.SyncEvents(ctx.GoContext())
}
registerOpenFlareTasks(ctx)
bindLogstore(ctx)
if err := ofgeoip.EnsureRuntimeProvider(ctx.GoContext()); err != nil {
logger.ErrorF(ctx.GoContext(), "[server] init GeoIP provider failed: %v", err)
}
chwriter.Init(ctx.GoContext())
ctx.OnDispose(func() error {
return chwriter.Stop(context.Background())
})
var auth contracts.AuthService
if err := core.Using[contracts.AuthService](ctx, func(s contracts.AuthService) { auth = s }); err != nil {
return err
}
repository.SetAuthService(auth)
ofrouter.RegisterV1Routes(ctx.Router().Group("/api/v1"), auth)
ofrouter.RegisterRoutes(ctx.Router().Group("/api/v1"), auth)
ctx.Router().GET("/robots.txt", func(c *gin.Context) {
c.Data(http.StatusOK, "text/plain; charset=utf-8", []byte(adminservice.RobotsTxtBody(c.Request.Context())))
})
env := ctx.Config().String("app.env", "production")
if env != "production" && env != "prod" {
ctx.Router().GET("/api/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
}
return nil
}
func bindLogstore(ctx *core.Context) {
logstore.SetConfigReader(func(goCtx context.Context, key string) (string, error) {
cfg, err := repository.GetSystemConfigByKey(goCtx, key)
if err != nil {
return "", err
}
return cfg.Value, nil
})
logstore.Init(ctx.GoContext())
}