mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-09-29 14:06:36 +08:00
143 lines
4.8 KiB
Go
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())
|
|
}
|