refactor!: 迁移至 samber/do 依赖注入与三层架构

依赖注入:
- 移除 google/wire(含 wire.go/wire_gen.go 与全部 ProviderSet),
  改用 samber/do v2 单注入器 + 双入口惰性构建
- 贡献模型替代命令式注册:路由 routes:、命令 commands:、
  任务 jobs: 前缀经 internal/registry 收集与校验
- 构造函数统一 func NewXxx(i do.Injector) (T, error)

三层架构:
- 补全用例层:每个 biz.XxxRepo 配 XxxUsecase,
  service/command/job 表现层只依赖用例,不再直接引用仓储

路由与文档:
- route/http.go 按域拆为声明式 Endpoint 贡献,
  Endpoint 承载登录白名单与端点限流语义
- 调试模式下提供 OpenAPI 3.1 文档:/openapi.json 与 /docs(Scalar),
  从 validate 标签生成

目录与依赖:
- internal/http/{middleware,request,rule} 拍平至 internal/*
- CLI 命令拆至 internal/command
- 日志轮转 timberjack 换为 libtnb/logrotate
- 升级 validator/cron/sessions/gormstore/sqlite/securecookie
- 数据库迁移保持 gormigrate 不变

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
耗子
2026-07-09 21:54:22 +08:00
parent 6f0593fbf8
commit 443516a2cf
331 changed files with 5526 additions and 2852 deletions
+59
View File
@@ -0,0 +1,59 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// AppCommand 应用管理命令组
func AppCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "app",
Usage: t.Get("Application management"),
Commands: []*cli.Command{
{
Name: "install",
Usage: t.Get("Install application"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).AppInstall(ctx, cmd)
},
},
{
Name: "uninstall",
Usage: t.Get("Uninstall application"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).AppUnInstall(ctx, cmd)
},
},
{
Name: "update",
Usage: t.Get("Update application"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).AppUpdate(ctx, cmd)
},
},
{
Name: "write",
Usage: t.Get("Add panel application mark (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).AppWrite(ctx, cmd)
},
},
{
Name: "remove",
Usage: t.Get("Remove panel application mark (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).AppRemove(ctx, cmd)
},
},
},
}, nil
}
+128
View File
@@ -0,0 +1,128 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// BackupCommand 数据备份命令组
func BackupCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "backup",
Usage: t.Get("Data backup"),
Commands: []*cli.Command{
{
Name: "website",
Usage: t.Get("Backup website"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: t.Get("Website name"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BackupWebsite(ctx, cmd)
},
},
{
Name: "database",
Usage: t.Get("Backup database"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: t.Get("Database type"),
Required: true,
},
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: t.Get("Database name"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BackupDatabase(ctx, cmd)
},
},
{
Name: "path",
Usage: t.Get("Backup directory"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "path",
Aliases: []string{"p"},
Usage: t.Get("Directory path"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BackupPath(ctx, cmd)
},
},
{
Name: "panel",
Usage: t.Get("Backup panel"),
Flags: []cli.Flag{},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BackupPanel(ctx, cmd)
},
},
{
Name: "clear",
Usage: t.Get("Clear backups"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: t.Get("Backup type"),
Required: true,
},
&cli.StringFlag{
Name: "file",
Aliases: []string{"f"},
Usage: t.Get("Backup file"),
Required: true,
},
&cli.UintFlag{
Name: "keep",
Aliases: []string{"k"},
Usage: t.Get("Number of backups to keep"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BackupClear(ctx, cmd)
},
},
},
}, nil
}
+30
View File
@@ -0,0 +1,30 @@
package command
import (
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/registry"
)
const Prefix = "commands:"
var Package = do.Package(
do.LazyNamed(Prefix+"status", StatusCommand), do.LazyNamed(Prefix+"restart", RestartCommand),
do.LazyNamed(Prefix+"stop", StopCommand), do.LazyNamed(Prefix+"start", StartCommand),
do.LazyNamed(Prefix+"update", UpdateCommand), do.LazyNamed(Prefix+"sync", SyncCommand),
do.LazyNamed(Prefix+"fix", FixCommand), do.LazyNamed(Prefix+"info", InfoCommand),
do.LazyNamed(Prefix+"port", PortCommand), do.LazyNamed(Prefix+"sync-time", SyncTimeCommand),
do.LazyNamed(Prefix+"clear-task", ClearTaskCommand), do.LazyNamed(Prefix+"init", InitCommand),
do.LazyNamed(Prefix+"user", UserCommand), do.LazyNamed(Prefix+"https", HttpsCommand),
do.LazyNamed(Prefix+"entrance", EntranceCommand), do.LazyNamed(Prefix+"bind-domain", BindDomainCommand),
do.LazyNamed(Prefix+"bind-ip", BindIPCommand), do.LazyNamed(Prefix+"bind-ua", BindUACommand),
do.LazyNamed(Prefix+"website", WebsiteCommand), do.LazyNamed(Prefix+"database", DatabaseCommand),
do.LazyNamed(Prefix+"backup", BackupCommand), do.LazyNamed(Prefix+"cutoff", CutoffCommand),
do.LazyNamed(Prefix+"app", AppCommand), do.LazyNamed(Prefix+"setting", SettingCommand),
)
// Commands 收集全部命令贡献。
func Commands(i do.Injector) ([]*cli.Command, error) {
return registry.Collect[*cli.Command](i, Prefix)
}
+94
View File
@@ -0,0 +1,94 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// CutoffCommand 日志切割命令组
func CutoffCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "cutoff",
Usage: t.Get("Log rotation"),
Commands: []*cli.Command{
{
Name: "website",
Usage: t.Get("Website"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: t.Get("Website name"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).CutoffWebsite(ctx, cmd)
},
},
{
Name: "container",
Usage: t.Get("Container"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: t.Get("Container name"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).CutoffContainer(ctx, cmd)
},
},
{
Name: "clear",
Usage: t.Get("Clear rotated logs"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "type",
Aliases: []string{"t"},
Usage: t.Get("Rotation type"),
Required: true,
},
&cli.StringFlag{
Name: "name",
Aliases: []string{"n"},
Usage: t.Get("Target name"),
Required: true,
},
&cli.UintFlag{
Name: "keep",
Aliases: []string{"k"},
Usage: t.Get("Number of logs to keep"),
Required: true,
},
&cli.UintFlag{
Name: "storage",
Aliases: []string{"s"},
Usage: t.Get("Storage ID (local storage if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).CutoffClear(ctx, cmd)
},
},
},
}, nil
}
+78
View File
@@ -0,0 +1,78 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// DatabaseCommand 数据库管理命令组
func DatabaseCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "database",
Usage: t.Get("Database management"),
Commands: []*cli.Command{
{
Name: "add-server",
Usage: t.Get("Add database server"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "type",
Usage: t.Get("Server type"),
Required: true,
},
&cli.StringFlag{
Name: "name",
Usage: t.Get("Server name"),
Required: true,
},
&cli.StringFlag{
Name: "host",
Usage: t.Get("Server address"),
Required: true,
},
&cli.UintFlag{
Name: "port",
Usage: t.Get("Server port"),
Required: true,
},
&cli.StringFlag{
Name: "username",
Usage: t.Get("Server username"),
},
&cli.StringFlag{
Name: "password",
Usage: t.Get("Server password"),
},
&cli.StringFlag{
Name: "remark",
Usage: t.Get("Server remark"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).DatabaseAddServer(ctx, cmd)
},
},
{
Name: "delete-server",
Usage: t.Get("Delete database server"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: t.Get("Server name"),
Aliases: []string{"n"},
Required: true,
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).DatabaseDeleteServer(ctx, cmd)
},
},
},
}, nil
}
+90
View File
@@ -0,0 +1,90 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// EntranceCommand 访问入口管理命令组
func EntranceCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "entrance",
Usage: t.Get("Operate AcePanel access entrance"),
Commands: []*cli.Command{
{
Name: "on",
Usage: t.Get("Enable access entrance"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).EntranceOn(ctx, cmd)
},
},
{
Name: "off",
Usage: t.Get("Disable access entrance"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).EntranceOff(ctx, cmd)
},
},
},
}, nil
}
// BindDomainCommand 域名绑定管理命令组
func BindDomainCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "bind-domain",
Usage: t.Get("Operate AcePanel domain binding"),
Commands: []*cli.Command{
{
Name: "off",
Usage: t.Get("Disable domain binding"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BindDomainOff(ctx, cmd)
},
},
},
}, nil
}
// BindIPCommand IP 绑定管理命令组
func BindIPCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "bind-ip",
Usage: t.Get("Operate AcePanel IP binding"),
Commands: []*cli.Command{
{
Name: "off",
Usage: t.Get("Disable IP binding"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BindIPOff(ctx, cmd)
},
},
},
}, nil
}
// BindUACommand UA 绑定管理命令组
func BindUACommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "bind-ua",
Usage: t.Get("Operate AcePanel UA binding"),
Commands: []*cli.Command{
{
Name: "off",
Usage: t.Get("Disable UA binding"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).BindUAOff(ctx, cmd)
},
},
},
}, nil
}
+43
View File
@@ -0,0 +1,43 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// HttpsCommand 面板 HTTPS 管理命令组
func HttpsCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "https",
Usage: t.Get("Operate AcePanel HTTPS"),
Commands: []*cli.Command{
{
Name: "on",
Usage: t.Get("Enable HTTPS"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).HTTPSOn(ctx, cmd)
},
},
{
Name: "off",
Usage: t.Get("Disable HTTPS"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).HTTPSOff(ctx, cmd)
},
},
{
Name: "generate",
Usage: t.Get("Obtain a free certificate or generate a self-signed certificate"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).HTTPSGenerate(ctx, cmd)
},
},
},
}, nil
}
+116
View File
@@ -0,0 +1,116 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// UpdateCommand 更新面板
func UpdateCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "update",
Usage: t.Get("Update AcePanel to the latest version"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Update(ctx, cmd)
},
}, nil
}
// SyncCommand 同步云端缓存数据
func SyncCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "sync",
Usage: t.Get("Sync AcePanel cached data with cloud"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Sync(ctx, cmd)
},
}, nil
}
// FixCommand 修复升级问题
func FixCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "fix",
Usage: t.Get("Fix AcePanel upgrade issues"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Fix(ctx, cmd)
},
}, nil
}
// InfoCommand 输出面板基础信息
func InfoCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "info",
Usage: t.Get("Output AcePanel basic information"),
Flags: []cli.Flag{
&cli.BoolFlag{
Name: "force",
Aliases: []string{"f"},
Usage: t.Get("Force reset password"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Info(ctx, cmd)
},
}, nil
}
// PortCommand 修改监听端口
func PortCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "port",
Usage: t.Get("Change the AcePanel listening port"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Port(ctx, cmd)
},
}, nil
}
// SyncTimeCommand 通过 NTP 同步系统时间
func SyncTimeCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "sync-time",
Usage: t.Get("Sync server time with NTP"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).SyncTime(ctx, cmd)
},
}, nil
}
// ClearTaskCommand 清理卡住的任务队列
func ClearTaskCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "clear-task",
Usage: t.Get("Clear all tasks in the task queue if they are stuck (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).ClearTask(ctx, cmd)
},
}, nil
}
// InitCommand 初始化面板
func InitCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "init",
Usage: t.Get("Initialize AcePanel (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Init(ctx, cmd)
},
}, nil
}
+59
View File
@@ -0,0 +1,59 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// StatusCommand 查询服务状态
func StatusCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "status",
Usage: t.Get("Get AcePanel service status"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Status(ctx, cmd)
},
}, nil
}
// RestartCommand 重启服务
func RestartCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "restart",
Usage: t.Get("Restart AcePanel service"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Restart(ctx, cmd)
},
}, nil
}
// StopCommand 停止服务
func StopCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "stop",
Usage: t.Get("Stop AcePanel service"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Stop(ctx, cmd)
},
}, nil
}
// StartCommand 启动服务
func StartCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "start",
Usage: t.Get("Start AcePanel service"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).Start(ctx, cmd)
},
}, nil
}
+47
View File
@@ -0,0 +1,47 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// SettingCommand 面板设置管理命令组
func SettingCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "setting",
Usage: t.Get("Setting management"),
Hidden: true,
Commands: []*cli.Command{
{
Name: "get",
Usage: t.Get("Get panel setting (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).GetSetting(ctx, cmd)
},
},
{
Name: "write",
Usage: t.Get("Write panel setting (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).WriteSetting(ctx, cmd)
},
},
{
Name: "remove",
Usage: t.Get("Remove panel setting (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).RemoveSetting(ctx, cmd)
},
},
},
}, nil
}
+57
View File
@@ -0,0 +1,57 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// UserCommand 用户管理命令组
func UserCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "user",
Usage: t.Get("Operate AcePanel users"),
Commands: []*cli.Command{
{
Name: "list",
Usage: t.Get("List all users"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).UserList(ctx, cmd)
},
},
{
Name: "username",
Usage: t.Get("Change a user's username"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).UserName(ctx, cmd)
},
},
{
Name: "password",
Usage: t.Get("Change a user's password"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).UserPassword(ctx, cmd)
},
},
{
Name: "2fa",
Usage: t.Get("Toggle two-factor authentication for a user"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).UserTwoFA(ctx, cmd)
},
},
{
Name: "passkey",
Usage: t.Get("Clear all passkeys for a user"),
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).UserPasskey(ctx, cmd)
},
},
},
}, nil
}
+95
View File
@@ -0,0 +1,95 @@
package command
import (
"context"
"github.com/leonelquinteros/gotext"
"github.com/samber/do/v2"
"github.com/urfave/cli/v3"
"github.com/acepanel/panel/v3/internal/service"
)
// WebsiteCommand 网站管理命令组
func WebsiteCommand(i do.Injector) (*cli.Command, error) {
t := do.MustInvoke[*gotext.Locale](i)
return &cli.Command{
Name: "website",
Usage: t.Get("Website management"),
Commands: []*cli.Command{
{
Name: "create",
Usage: t.Get("Create new website"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: t.Get("Website name"),
Aliases: []string{"n"},
Required: true,
},
&cli.StringSliceFlag{
Name: "domains",
Usage: t.Get("List of domains associated with the website"),
Aliases: []string{"d"},
Required: true,
},
&cli.StringSliceFlag{
Name: "listens",
Usage: t.Get("List of listening addresses associated with the website"),
Aliases: []string{"l"},
Required: true,
},
&cli.StringFlag{
Name: "path",
Usage: t.Get("Path where the website is hosted (default path if not filled)"),
},
&cli.UintFlag{
Name: "php",
Usage: t.Get("PHP version used by the website (not used if not filled)"),
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).WebsiteCreate(ctx, cmd)
},
},
{
Name: "remove",
Usage: t.Get("Remove website"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: t.Get("Website name"),
Aliases: []string{"n"},
Required: true,
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).WebsiteRemove(ctx, cmd)
},
},
{
Name: "delete",
Usage: t.Get("Delete website (including website directory, database with the same name)"),
Flags: []cli.Flag{
&cli.StringFlag{
Name: "name",
Usage: t.Get("Website name"),
Aliases: []string{"n"},
Required: true,
},
},
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).WebsiteDelete(ctx, cmd)
},
},
{
Name: "write",
Usage: t.Get("Write website data (use only under guidance)"),
Hidden: true,
Action: func(ctx context.Context, cmd *cli.Command) error {
return do.MustInvoke[*service.CliService](i).WebsiteWrite(ctx, cmd)
},
},
},
}, nil
}