98 lines
2.3 KiB
Go
98 lines
2.3 KiB
Go
package module
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
|
||
"ops-assistant/internal/core/policy"
|
||
"ops-assistant/internal/core/runbook"
|
||
)
|
||
|
||
type CommandTemplate struct {
|
||
RunbookName string
|
||
Gate Gate
|
||
InputsFn func(text string, parts []string) (map[string]string, error)
|
||
MetaFn func(userID int64, confirmToken string, inputs map[string]string) runbook.RunMeta
|
||
DryRunMsg string
|
||
SuccessMsg func(jobID uint) string
|
||
}
|
||
|
||
type CommandSpec struct {
|
||
Prefixes []string
|
||
Template CommandTemplate
|
||
ErrPrefix string
|
||
ErrHint string
|
||
}
|
||
|
||
func ExecTemplate(runner *Runner, userID int64, raw string, tpl CommandTemplate) (uint, string, error) {
|
||
dryRun, confirmToken := policy.ParseCommonFlags(raw)
|
||
parts := strings.Fields(strings.TrimSpace(raw))
|
||
inputs := map[string]string{}
|
||
if tpl.InputsFn != nil {
|
||
out, err := tpl.InputsFn(raw, parts)
|
||
if err != nil {
|
||
return 0, "", err
|
||
}
|
||
inputs = out
|
||
}
|
||
meta := runbook.NewMeta()
|
||
if tpl.MetaFn != nil {
|
||
meta = tpl.MetaFn(userID, confirmToken, inputs)
|
||
}
|
||
if meta.RequestID == "" {
|
||
meta.RequestID = fmt.Sprintf("ops-u%d-%d", userID, time.Now().Unix())
|
||
}
|
||
req := Request{
|
||
RunbookName: tpl.RunbookName,
|
||
Inputs: inputs,
|
||
Meta: meta,
|
||
Gate: tpl.Gate,
|
||
DryRun: dryRun,
|
||
ConfirmToken: confirmToken,
|
||
}
|
||
jobID, out, err := runner.Run(raw, userID, req)
|
||
return jobID, out, err
|
||
}
|
||
|
||
func FormatDryRunMessage(tpl CommandTemplate) string {
|
||
if tpl.DryRunMsg != "" {
|
||
return tpl.DryRunMsg
|
||
}
|
||
return fmt.Sprintf("🧪 dry-run: 将执行 %s(未实际执行)", tpl.RunbookName)
|
||
}
|
||
|
||
func FormatSuccessMessage(tpl CommandTemplate, jobID uint) string {
|
||
if tpl.SuccessMsg != nil {
|
||
return tpl.SuccessMsg(jobID)
|
||
}
|
||
return fmt.Sprintf("✅ %s 已执行,job=%d", tpl.RunbookName, jobID)
|
||
}
|
||
|
||
func MatchAnyPrefix(text string, prefixes []string) bool {
|
||
text = strings.TrimSpace(text)
|
||
for _, p := range prefixes {
|
||
if strings.HasPrefix(text, p) {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func MatchCommand(text string, specs []CommandSpec) (CommandSpec, bool) {
|
||
for _, sp := range specs {
|
||
if MatchAnyPrefix(text, sp.Prefixes) {
|
||
return sp, true
|
||
}
|
||
}
|
||
return CommandSpec{}, false
|
||
}
|
||
|
||
func FormatExecError(sp CommandSpec, err error) string {
|
||
msg := sp.ErrPrefix + err.Error()
|
||
if sp.ErrHint != "" {
|
||
msg += "(示例:" + sp.ErrHint + ")"
|
||
}
|
||
return msg
|
||
}
|