mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-14 20:48:17 +08:00
178 lines
6.2 KiB
Go
178 lines
6.2 KiB
Go
package cli
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/matthewlu070111/smart-srun/core/internal/config"
|
|
"github.com/matthewlu070111/smart-srun/core/internal/daemon"
|
|
"github.com/matthewlu070111/smart-srun/core/internal/domain"
|
|
)
|
|
|
|
type configForm struct {
|
|
ctx context.Context
|
|
input formInput
|
|
err error
|
|
}
|
|
|
|
func (f *configForm) ask(label, current string, secret, keep bool, limit int) string {
|
|
if f.err != nil {
|
|
return ""
|
|
}
|
|
if current != "" && !secret {
|
|
label += " [" + strconv.Quote(current) + "]"
|
|
}
|
|
if keep {
|
|
label += "(留空保留)"
|
|
}
|
|
value, err := f.input.Read(f.ctx, label, secret, limit)
|
|
if err != nil {
|
|
f.err = err
|
|
return ""
|
|
}
|
|
if value == "" && keep {
|
|
return current
|
|
}
|
|
return value
|
|
}
|
|
func (f *configForm) secret(label string, editing bool) *string {
|
|
value := f.ask(label, "", true, editing, config.MaxSecretBytes)
|
|
if editing && value == "" {
|
|
return nil
|
|
}
|
|
return &value
|
|
}
|
|
|
|
func runInteractiveConfig(ctx context.Context, client onlineClient, args []string, stdin io.Reader, stdout, stderr *os.File) int {
|
|
if len(args) < 2 || len(args) > 3 || (args[0] != "account" && args[0] != "hotspot") || (args[1] != "add" && args[1] != "edit") || (len(args) == 3 && args[1] != "edit") {
|
|
return onlineError(stderr, domain.Errorf(domain.CodeInvalidArgument, "用法:config account|hotspot add|edit [ID] --interactive"))
|
|
}
|
|
input, err := newTerminalInput(stdin, stderr)
|
|
if err != nil {
|
|
return onlineError(stderr, err)
|
|
}
|
|
return interactiveConfig(ctx, client, args, input, stdout, stderr)
|
|
}
|
|
|
|
func interactiveConfig(ctx context.Context, client onlineClient, args []string, input formInput, stdout, stderr *os.File) int {
|
|
if err := client.ensure(ctx); err != nil {
|
|
return onlineError(stderr, err)
|
|
}
|
|
cfg, err := readOnlineConfig(ctx, client)
|
|
if err != nil {
|
|
return onlineError(stderr, err)
|
|
}
|
|
f := &configForm{ctx: ctx, input: input}
|
|
editing := args[1] == "edit"
|
|
verb := "新增"
|
|
if editing {
|
|
verb = "更新"
|
|
}
|
|
id := ""
|
|
if editing {
|
|
if len(args) == 3 {
|
|
id = args[2]
|
|
} else {
|
|
id = f.ask("要编辑的 ID", "", false, false, config.MaxIDBytes)
|
|
}
|
|
}
|
|
var method string
|
|
var params any
|
|
if args[0] == "account" {
|
|
account := domain.CampusAccount{AccessMode: domain.AccessModeWired, WiredIface: config.DefaultWiredIface, Encryption: "none"}
|
|
if editing {
|
|
var found bool
|
|
account, found = cfg.CampusAccountByID(id)
|
|
if !found {
|
|
if f.err != nil {
|
|
return onlineError(stderr, f.err)
|
|
}
|
|
return onlineError(stderr, domain.Errorf(domain.CodeNotFound, "没有这个校园账号"))
|
|
}
|
|
}
|
|
patch := config.CampusPatch{ID: id}
|
|
label := f.ask("账号名称", account.Label, false, editing, config.MaxLabelBytes)
|
|
user := f.ask("校园账号", account.UserID, false, editing, config.MaxUserIDBytes)
|
|
patch.Password = f.secret("校园密码", editing)
|
|
suffix := account.OperatorSuffix
|
|
if !editing || strings.EqualFold(f.ask("更改认证后缀?输入 y 更改,其余保留", "", false, false, 8), "y") {
|
|
suffix = f.ask("认证后缀(空行表示不加后缀)", "", false, false, config.MaxSuffixBytes)
|
|
}
|
|
mode := strings.TrimSpace(f.ask("接入方式 wired / wifi", string(account.AccessMode), false, true, 16))
|
|
patch.Label, patch.UserID, patch.OperatorSuffix, patch.AccessMode = &label, &user, &suffix, &mode
|
|
if mode == "wired" {
|
|
iface := f.ask("有线出口接口", account.WiredIface, false, true, config.MaxNameBytes)
|
|
if iface == "" {
|
|
iface = config.DefaultWiredIface
|
|
}
|
|
patch.WiredIface = &iface
|
|
if !editing {
|
|
enabled := true
|
|
patch.AuthEnabled = &enabled
|
|
}
|
|
} else if mode == "wifi" {
|
|
ssid := f.ask("Wi-Fi 名称", account.SSID, false, editing, 32)
|
|
radio := f.ask("无线电(留空自动选择)", account.Radio, false, editing, config.MaxNameBytes)
|
|
encryption := f.ask("加密方式 none / psk2 / sae", account.Encryption, false, true, config.MaxNameBytes)
|
|
patch.SSID, patch.Radio, patch.Encryption = &ssid, &radio, &encryption
|
|
if config.KeyRequired(encryption) {
|
|
patch.Key = f.secret("Wi-Fi 密码", editing)
|
|
} else {
|
|
empty := ""
|
|
patch.Key = &empty
|
|
}
|
|
} else if f.err == nil {
|
|
f.err = domain.Errorf(domain.CodeInvalidArgument, "接入方式只能是 wired 或 wifi")
|
|
}
|
|
baseURL := f.ask("认证地址(可稍后检测填写)", account.BaseURL, false, editing, config.MaxURLBytes)
|
|
acid := f.ask("AC_ID(可稍后检测填写)", account.ACID, false, editing, config.MaxNameBytes)
|
|
patch.BaseURL, patch.ACID = &baseURL, &acid
|
|
method, params = "campus.upsert", daemon.CampusUpsertParams{ExpectedRevision: &cfg.Revision, Account: &patch}
|
|
if f.err == nil {
|
|
fmt.Fprintf(stderr, "将%s校园账号 %q,接入方式 %s,认证后缀 %q。\n", verb, user, mode, suffix)
|
|
}
|
|
} else {
|
|
profile := domain.HotspotProfile{Encryption: "none"}
|
|
if editing {
|
|
var found bool
|
|
profile, found = cfg.HotspotByID(id)
|
|
if !found {
|
|
if f.err != nil {
|
|
return onlineError(stderr, f.err)
|
|
}
|
|
return onlineError(stderr, domain.Errorf(domain.CodeNotFound, "没有这个热点"))
|
|
}
|
|
}
|
|
label := f.ask("热点名称", profile.Label, false, editing, config.MaxLabelBytes)
|
|
ssid := f.ask("Wi-Fi 名称", profile.SSID, false, editing, 32)
|
|
radio := f.ask("无线电(留空自动选择)", profile.Radio, false, editing, config.MaxNameBytes)
|
|
encryption := f.ask("加密方式 none / psk2 / sae", profile.Encryption, false, true, config.MaxNameBytes)
|
|
patch := config.HotspotPatch{ID: id, Label: &label, SSID: &ssid, Radio: &radio, Encryption: &encryption}
|
|
if config.KeyRequired(encryption) {
|
|
patch.Key = f.secret("Wi-Fi 密码", editing)
|
|
} else {
|
|
empty := ""
|
|
patch.Key = &empty
|
|
}
|
|
method, params = "hotspot.upsert", daemon.HotspotUpsertParams{ExpectedRevision: &cfg.Revision, Profile: &patch}
|
|
if f.err == nil {
|
|
fmt.Fprintf(stderr, "将%s热点 %q,加密方式 %q。\n", verb, ssid, encryption)
|
|
}
|
|
}
|
|
if f.err != nil {
|
|
return onlineError(stderr, f.err)
|
|
}
|
|
confirmation := f.ask("保存以上配置?输入 y 保存,其余取消", "", false, false, 8)
|
|
if f.err != nil {
|
|
return onlineError(stderr, f.err)
|
|
}
|
|
if !strings.EqualFold(confirmation, "y") {
|
|
return onlineError(stderr, domain.Errorf(domain.CodeCancelled, "已取消,未保存配置"))
|
|
}
|
|
return printCall(ctx, client, method, params, stdout, stderr)
|
|
}
|