mirror of
https://github.com/bufanyun/hotgo.git
synced 2025-02-03 10:58:44 +08:00
199 lines
5.0 KiB
Go
199 lines
5.0 KiB
Go
// Package addons
|
|
// @Link https://github.com/bufanyun/hotgo
|
|
// @Copyright Copyright (c) 2023 HotGo CLI
|
|
// @Author Ms <133814250@qq.com>
|
|
// @License https://github.com/bufanyun/hotgo/blob/master/LICENSE
|
|
package addons
|
|
|
|
import (
|
|
"context"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
"github.com/gogf/gf/v2/os/gfile"
|
|
"github.com/gogf/gf/v2/os/gres"
|
|
"github.com/gogf/gf/v2/os/gview"
|
|
"hotgo/internal/model/input/form"
|
|
"sort"
|
|
"sync"
|
|
)
|
|
|
|
// Skeleton 模块骨架
|
|
type Skeleton struct {
|
|
Label string `json:"label"` // 标识
|
|
Name string `json:"name"` // 名称
|
|
Group int `json:"group"` // 分组
|
|
Logo string `json:"logo"` // logo
|
|
Brief string `json:"brief"` // 简介
|
|
Description string `json:"description"` // 详细描述
|
|
Author string `json:"author"` // 作者
|
|
Version string `json:"version"` // 版本号
|
|
RootPath string `json:"rootPath"` // 根路径
|
|
View *gview.View `json:"view"` // 模板引擎
|
|
}
|
|
|
|
func (s *Skeleton) GetModule() Module {
|
|
return GetModule(s.Name)
|
|
}
|
|
|
|
// Module 插件模块
|
|
type Module interface {
|
|
Init(ctx context.Context) // 初始化
|
|
InitRouter(ctx context.Context, group *ghttp.RouterGroup) // 初始化并注册路由
|
|
Ctx() context.Context // 上下文
|
|
GetSkeleton() *Skeleton // 架子
|
|
Install(ctx context.Context) error // 安装模块
|
|
Upgrade(ctx context.Context) error // 更新模块
|
|
UnInstall(ctx context.Context) error // 卸载模块
|
|
}
|
|
|
|
var (
|
|
modules = make(map[string]Module, 0)
|
|
mLock sync.Mutex
|
|
)
|
|
|
|
// InitModules 初始化所有已注册模块
|
|
func InitModules(ctx context.Context) {
|
|
for _, module := range modules {
|
|
module.Init(ctx)
|
|
}
|
|
}
|
|
|
|
// RegisterModulesRouter 注册所有已安装模块路由
|
|
func RegisterModulesRouter(ctx context.Context, group *ghttp.RouterGroup) {
|
|
for _, module := range filterInstalled() {
|
|
module.InitRouter(ctx, group)
|
|
}
|
|
}
|
|
|
|
// RegisterModule 注册模块
|
|
func RegisterModule(m Module) Module {
|
|
mLock.Lock()
|
|
defer mLock.Unlock()
|
|
name := m.GetSkeleton().Name
|
|
_, ok := modules[name]
|
|
if ok {
|
|
panic("module repeat registration, name:" + name)
|
|
}
|
|
|
|
sk := m.GetSkeleton()
|
|
if sk == nil {
|
|
panic("module skeleton not initialized, name:" + name)
|
|
}
|
|
|
|
sk.RootPath = GetModulePath(name)
|
|
sk.View = NewView(m.Ctx(), name)
|
|
modules[name] = m
|
|
return m
|
|
}
|
|
|
|
// GetModule 获取指定名称模块
|
|
func GetModule(name string) Module {
|
|
mLock.Lock()
|
|
defer mLock.Unlock()
|
|
m, ok := modules[name]
|
|
if !ok {
|
|
panic("implement not found for interface " + name + ", forgot register?")
|
|
}
|
|
return m
|
|
}
|
|
|
|
// GetSkeletons 获取所有模块骨架
|
|
func GetSkeletons() (list []*Skeleton) {
|
|
var keys []string
|
|
for k := range modules {
|
|
keys = append(keys, k)
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
for _, v := range keys {
|
|
list = append(list, GetModule(v).GetSkeleton())
|
|
}
|
|
return list
|
|
}
|
|
|
|
// GetModuleRealPath 获取指定模块绝对路径
|
|
func GetModuleRealPath(name string) string {
|
|
path := gfile.RealPath(GetModulePath(name))
|
|
if path == "" {
|
|
panic("no path is found. please confirm that the path " + GetModulePath(name) + " exists?")
|
|
}
|
|
return path
|
|
}
|
|
|
|
// NewView 初始化一个插件的模板引擎
|
|
func NewView(ctx context.Context, name string) *gview.View {
|
|
view := gview.New()
|
|
|
|
if err := view.SetPath(ViewPath(name)); err != nil {
|
|
g.Log().Warningf(ctx, "NewView SetPath err:%+v", err)
|
|
return nil
|
|
}
|
|
|
|
// 默认和主模块使用一致的变量分隔符号
|
|
delimiters := g.Cfg().MustGet(ctx, "viewer.delimiters", []string{"@{", "}"}).Strings()
|
|
if len(delimiters) != 2 {
|
|
g.Log().Warning(ctx, "NewView delimiters config error")
|
|
return nil
|
|
}
|
|
view.SetDelimiters(delimiters[0], delimiters[1])
|
|
|
|
// 更多配置
|
|
// view.SetI18n()
|
|
// ...
|
|
return view
|
|
}
|
|
|
|
// AddStaticPath 设置插件静态目录映射
|
|
func AddStaticPath(ctx context.Context, server *ghttp.Server, p ...string) {
|
|
basePath := g.Cfg().MustGet(ctx, "server.serverRoot").String()
|
|
if len(p) > 0 {
|
|
basePath = p[0]
|
|
}
|
|
|
|
if basePath == "" {
|
|
return
|
|
}
|
|
|
|
for _, module := range filterInstalled() {
|
|
name := module.GetSkeleton().Name
|
|
prefix, path := StaticPath(name, basePath)
|
|
if !gres.Contains(path) {
|
|
if _, err := gfile.Search(path); err != nil {
|
|
g.Log().Warningf(ctx, `AddStaticPath failed: %v`, err)
|
|
continue
|
|
}
|
|
}
|
|
server.AddStaticPath(prefix, path)
|
|
}
|
|
}
|
|
|
|
// filterInstalled 过滤已安装模块
|
|
func filterInstalled() []Module {
|
|
var ms []Module
|
|
for _, module := range modules {
|
|
if IsInstall(module) {
|
|
ms = append(ms, module)
|
|
}
|
|
}
|
|
return ms
|
|
}
|
|
|
|
// ModuleSelect 获取插件模块选项
|
|
func ModuleSelect() form.Selects {
|
|
sks := GetSkeletons()
|
|
lst := make(form.Selects, 0)
|
|
if len(sks) == 0 {
|
|
return lst
|
|
}
|
|
|
|
for _, skeleton := range sks {
|
|
lst = append(lst, &form.Select{
|
|
Value: skeleton.Name,
|
|
Label: skeleton.Label,
|
|
Name: skeleton.Label,
|
|
})
|
|
}
|
|
return lst
|
|
}
|