You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
138 lines
5.3 KiB
138 lines
5.3 KiB
package db
|
|
|
|
import (
|
|
"encoding/json"
|
|
"encoding/xml"
|
|
"fmt"
|
|
"github.com/pelletier/go-toml/v2"
|
|
"gopkg.in/ini.v1"
|
|
"gopkg.in/yaml.v3"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// AppConfig 程序启动配置
|
|
type AppConfig struct {
|
|
Name string `json:"name,omitempty" yaml:"name,omitempty" toml:"name,multiline,omitempty" xml:"name,omitempty" ini:"name,omitempty"` // 应用名称
|
|
Description string `json:"description,omitempty" yaml:"description,omitempty" toml:"description,multiline,omitempty" xml:"description,omitempty" ini:"description,omitempty"` // 应用描述
|
|
Script string `json:"script,omitempty" yaml:"script,omitempty" toml:"script,multiline,omitempty" xml:"script,omitempty" ini:"script,omitempty"` // 执行脚本
|
|
Args []string `json:"args,omitempty" yaml:"args,omitempty" toml:"args,multiline,omitempty" xml:"args,omitempty" ini:"args,omitempty"` // 启动参数
|
|
Stdin string `json:"stdin,omitempty" yaml:"stdin,omitempty" toml:"stdin,multiline,omitempty" xml:"stdin,omitempty" ini:"stdin,omitempty"` // 标准输入数据
|
|
Cwd string `json:"Cwd,omitempty" yaml:"Cwd,omitempty" toml:"Cwd,multiline,omitempty" xml:"Cwd,omitempty" ini:"cwd,omitempty"` // 工作目录
|
|
Env map[string]string `json:"env,omitempty" yaml:"env,omitempty" toml:"env,multiline,omitempty" xml:"env,omitempty" ini:"env,omitempty"` // 自定义环境变量
|
|
Interpreter string `json:"interpreter,omitempty" yaml:"interpreter,omitempty" toml:"interpreter,multiline,omitempty" xml:"interpreter,omitempty" ini:"interpreter,omitempty"` // 脚本解释程序
|
|
InterpreterArgs []string `json:"interpreter_args,omitempty" yaml:"interpreter_args,omitempty" toml:"interpreter_args,multiline,omitempty" xml:"interpreter_args,omitempty" ini:"interpreterArgs,omitempty"` // 解释程序参数
|
|
RerunOnError *RerunOnError `json:"rerunOnError" yaml:"rerunOnError,omitempty" toml:"rerunOnError,multiline,omitempty" xml:"rerunOnError,omitempty" ini:"rerunOnError,omitempty"` // 错误重启策略
|
|
Custom map[string]any `json:"custom,omitempty" yaml:"custom,omitempty" toml:"custom,multiline,omitempty" xml:"custom,omitempty" ini:"custom,omitempty"` // 其它自定义参数
|
|
}
|
|
|
|
type Unmarshaller func(data []byte, v any) error
|
|
|
|
func ParseInput(input string) ([]*App, error) {
|
|
var configs []AppConfig
|
|
var err error
|
|
var app *App
|
|
|
|
if IsAppID(input) {
|
|
var id int
|
|
if id, err = strconv.Atoi(input); err != nil {
|
|
return nil, fmt.Errorf("invalid identifier")
|
|
}
|
|
app, err = FindApp(WithAppID(uint(id)))
|
|
} else if strings.HasSuffix(input, ".pmt.json") {
|
|
configs, err = Unmarshal(input, json.Unmarshal)
|
|
} else if strings.HasSuffix(input, ".pmt.toml") {
|
|
configs, err = Unmarshal(input, toml.Unmarshal)
|
|
} else if strings.HasSuffix(input, ".pmt.yaml") {
|
|
configs, err = Unmarshal(input, yaml.Unmarshal)
|
|
} else if strings.HasSuffix(input, ".pmt.xml") {
|
|
configs, err = Unmarshal(input, xml.Unmarshal)
|
|
} else if strings.HasSuffix(input, ".pmt.ini") {
|
|
configs, err = Unmarshal(input, func(data []byte, v any) error {
|
|
if info, err := ini.Load(data); err != nil {
|
|
return err
|
|
} else {
|
|
return info.MapTo(v)
|
|
}
|
|
})
|
|
} else if file, ex := os.Stat(input); ex == nil {
|
|
if file.IsDir() {
|
|
return nil, fmt.Errorf("unsupported directoty entry")
|
|
}
|
|
|
|
if app, err = scriptToApp(input); err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
app, err = FindApp(WithAppName(input))
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if app != nil {
|
|
return []*App{app}, nil
|
|
}
|
|
if len(configs) == 0 {
|
|
return nil, fmt.Errorf("not found")
|
|
}
|
|
var apps []*App
|
|
for _, cfg := range configs {
|
|
apps = append(apps, optionsToApp(&cfg))
|
|
}
|
|
return apps, err
|
|
}
|
|
|
|
func scriptToApp(script string) (*App, error) {
|
|
name := ToSnakeCase(script)
|
|
app, err := FindApp(WithAppName(name))
|
|
if err == nil {
|
|
if app.Script != script {
|
|
return nil, fmt.Errorf("应用 %s 已存在", script)
|
|
}
|
|
return app, nil
|
|
}
|
|
return &App{
|
|
PID: -1,
|
|
Name: name,
|
|
Script: script,
|
|
}, nil
|
|
}
|
|
|
|
func optionsToApp(opts *AppConfig) *App {
|
|
return &App{
|
|
PID: -1,
|
|
Name: opts.Name,
|
|
Description: opts.Description,
|
|
Script: opts.Script,
|
|
Command: nil,
|
|
Arguments: opts.Args,
|
|
Stdin: opts.Stdin,
|
|
Cwd: opts.Cwd,
|
|
Environments: opts.Env,
|
|
Interpreter: opts.Interpreter,
|
|
InterpreterArgs: opts.InterpreterArgs,
|
|
RerunOnError: opts.RerunOnError,
|
|
Options: opts.Custom,
|
|
}
|
|
}
|
|
|
|
// Unmarshal 读取配置文件
|
|
func Unmarshal(file string, unmarshal Unmarshaller) ([]AppConfig, error) {
|
|
bts, err := os.ReadFile(file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var config AppConfig
|
|
if err = unmarshal(bts, &config); err == nil {
|
|
return []AppConfig{config}, nil
|
|
}
|
|
|
|
var configs []AppConfig
|
|
if unmarshal(bts, &configs) == nil {
|
|
return configs, nil
|
|
}
|
|
|
|
return nil, err
|
|
}
|
|
|