新增client init命令
This commit is contained in:
parent
03a3701b47
commit
948f4ce3cc
|
@ -0,0 +1,366 @@
|
|||
package cmdline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/chzyer/readline"
|
||||
"github.com/spf13/cobra"
|
||||
"gitlink.org.cn/cloudream/common/pkgs/logger"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/accesstoken"
|
||||
clicfg "gitlink.org.cn/cloudream/jcs-pub/client/internal/config"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/db"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/downloader"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/http"
|
||||
mntcfg "gitlink.org.cn/cloudream/jcs-pub/client/internal/mount/config"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/client/internal/ticktock"
|
||||
corrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/coordinator"
|
||||
hubrpc "gitlink.org.cn/cloudream/jcs-pub/common/pkgs/rpc/hub"
|
||||
"gitlink.org.cn/cloudream/jcs-pub/common/pkgs/sysevent"
|
||||
)
|
||||
|
||||
var confs_file = path.Join("confs", "client.config.json")
|
||||
var cli_certs_path = path.Join("confs", "cli_certs")
|
||||
var pub_certs_path = path.Join("confs", "pub_certs")
|
||||
var ca_key_file = "ca_key.pem"
|
||||
var ca_cert_file = "ca_cert.pem"
|
||||
var client_cert_file = "client_cert.pem"
|
||||
|
||||
func init() {
|
||||
cmd := cobra.Command{
|
||||
Use: "init",
|
||||
Short: "initialize client configuration",
|
||||
Run: func(c *cobra.Command, args []string) {
|
||||
init2()
|
||||
},
|
||||
}
|
||||
RootCmd.AddCommand(&cmd)
|
||||
}
|
||||
|
||||
func getPath() (exePath string, dirPath string, err error) {
|
||||
exePath, err = os.Executable()
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("获取执行路径失败: %w", err)
|
||||
}
|
||||
dirPath = filepath.Dir(exePath)
|
||||
return exePath, dirPath, nil
|
||||
}
|
||||
|
||||
func init2() error {
|
||||
rl, err := readline.New("> ")
|
||||
if err != nil {
|
||||
fmt.Printf("初始化命令行失败: %v\n", err)
|
||||
return err
|
||||
}
|
||||
defer rl.Close()
|
||||
|
||||
// 1. 检查配置文件是否存在
|
||||
_, dirPath, err := getPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
configFilePath := filepath.Join(dirPath, confs_file)
|
||||
cliCertsPath := filepath.Join(dirPath, cli_certs_path)
|
||||
pubCertsPath := filepath.Join(dirPath, pub_certs_path)
|
||||
|
||||
_, err = os.Stat(configFilePath)
|
||||
if err == nil {
|
||||
fmt.Println("\033[33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m")
|
||||
fmt.Println("\033[33m⚠ 配置文件已存在!重新初始化会覆盖原配置文件\033[0m")
|
||||
fmt.Println("\033[33m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\033[0m")
|
||||
checkLoop:
|
||||
for {
|
||||
// 2. 配置文件存在,询问是否覆盖
|
||||
rl.SetPrompt("\033[36m是否继续?(y/n): \033[0m")
|
||||
overwrite, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(overwrite)) {
|
||||
case "y", "yes":
|
||||
break checkLoop
|
||||
|
||||
case "n", "no":
|
||||
fmt.Printf("\033[32m已保留原有配置文件,退出初始化\033[0m\n")
|
||||
return nil
|
||||
|
||||
default:
|
||||
fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cfg clicfg.Config
|
||||
|
||||
mysqlLoop:
|
||||
for {
|
||||
// 3. 询问数据库配置
|
||||
rl.SetPrompt("\033[36m请输入Mysql连接地址(例如127.0.0.1:3306): \033[0m")
|
||||
dbAddress, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rl.SetPrompt("\033[36m请输入Mysql用户名(例如root): \033[0m")
|
||||
dbAccount, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbPasswordBytes, err := rl.ReadPassword("\033[36m请输入Mysql密码(例如123456): \033[0m")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dbPassword := string(dbPasswordBytes)
|
||||
|
||||
rl.SetPrompt("\033[36m请输入Mysql数据库名称(例如cloudream): \033[0m")
|
||||
dbName, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.DB = db.Config{
|
||||
Address: dbAddress,
|
||||
Account: dbAccount,
|
||||
Password: dbPassword,
|
||||
DatabaseName: dbName,
|
||||
}
|
||||
|
||||
rl.SetPrompt("\033[36m是否测试数据库连接?(y/n): \033[0m")
|
||||
needTest, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(needTest)) {
|
||||
case "y", "yes":
|
||||
// 4. 测试数据库连接
|
||||
fmt.Printf("\033[33m正在测试数据库连接...\033[0m\n")
|
||||
testDB, err := sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s)/%s",
|
||||
dbAccount,
|
||||
dbPassword,
|
||||
dbAddress,
|
||||
dbName))
|
||||
if err != nil {
|
||||
fmt.Printf("\033[31m连接创建失败: %v\033[0m\n", err)
|
||||
testDB.Close()
|
||||
} else {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
err = testDB.PingContext(ctx)
|
||||
if err != nil {
|
||||
fmt.Printf("\033[31m数据库连接测试失败: %v\033[0m\n", err)
|
||||
testDB.Close()
|
||||
} else {
|
||||
fmt.Printf("\033[32m✓ 数据库连接测试成功!\033[0m\n\n")
|
||||
testDB.Close()
|
||||
break mysqlLoop
|
||||
}
|
||||
}
|
||||
|
||||
rl.SetPrompt("\033[36m连接测试失败,是否重新输入配置?(y/n): \033[0m")
|
||||
retry, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if strings.ToLower(retry) != "y" {
|
||||
fmt.Printf("\033[33m警告: 数据库连接尚未验证,将使用当前配置,如需后续更改,请查看配置文件client.config.json\033[0m\n")
|
||||
break mysqlLoop
|
||||
}
|
||||
|
||||
case "n", "no":
|
||||
fmt.Printf("\033[33m警告: 数据库连接未测试,将直接使用当前配置,如需后续更改,请查看配置文件client.config.json\033[0m\n")
|
||||
break mysqlLoop
|
||||
|
||||
default:
|
||||
fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 询问证书配置
|
||||
rl.SetPrompt("\033[36m请输入HTTP API监听地址(例如127.0.0.1:3306): \033[0m")
|
||||
listen, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. 生成证书
|
||||
err = os.MkdirAll(cliCertsPath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keyFilePath := filepath.Join(cliCertsPath, ca_key_file)
|
||||
certFilePath := filepath.Join(cliCertsPath, ca_cert_file)
|
||||
certRoot(cliCertsPath)
|
||||
certServer(certFilePath, keyFilePath, cliCertsPath)
|
||||
certClient(certFilePath, keyFilePath, cliCertsPath)
|
||||
|
||||
cfg.HTTP = &http.ConfigJSON{
|
||||
Enabled: true,
|
||||
Listen: listen,
|
||||
UserSpaceID: 0,
|
||||
RootCA: path.Join(cli_certs_path, "ca_cert.pem"),
|
||||
ServerCert: path.Join(cli_certs_path, "server_cert.pem"),
|
||||
ServerKey: path.Join(cli_certs_path, "server_key.pem"),
|
||||
ClientCerts: []string{path.Join(cli_certs_path, "client_cert.pem")},
|
||||
MaxBodySize: 5242880,
|
||||
}
|
||||
|
||||
cloudLoop:
|
||||
for {
|
||||
// 7. 填写云际基础设施配置
|
||||
rl.SetPrompt("\033[36m是否连接云际存储基础设施?(y/n): \033[0m")
|
||||
isConnect, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(isConnect)) {
|
||||
case "y", "yes":
|
||||
rl.SetPrompt("\033[36m请输入Coordinator地址: \033[0m")
|
||||
coorAddress, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.CoordinatorRPC = corrpc.PoolConfigJSON{
|
||||
Address: coorAddress,
|
||||
RootCA: path.Join(".", pub_certs_path, ca_cert_file),
|
||||
ClientCert: path.Join(".", pub_certs_path, client_cert_file),
|
||||
ClientKey: path.Join(".", pub_certs_path, client_cert_file),
|
||||
}
|
||||
|
||||
cfg.HubRPC = hubrpc.PoolConfigJSON{
|
||||
RootCA: path.Join(".", pub_certs_path, ca_cert_file),
|
||||
ClientCert: path.Join(".", pub_certs_path, client_cert_file),
|
||||
ClientKey: path.Join(".", pub_certs_path, client_cert_file),
|
||||
}
|
||||
|
||||
rl.SetPrompt("\033[36m请输入账户名(Account): \033[0m")
|
||||
account, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
passwordBytes, err := rl.ReadPassword("\033[36m请输入密码(Password): \033[0m")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
password := string(passwordBytes)
|
||||
|
||||
cfg.AccessToken = &accesstoken.Config{
|
||||
Account: account,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
fmt.Printf("\033[33m注意:请将JCS-pub证书文件放置于 %s 目录下\033[0m\n", pubCertsPath)
|
||||
err = os.MkdirAll(pubCertsPath, 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
break cloudLoop
|
||||
|
||||
case "n", "no":
|
||||
cfg.CoordinatorRPC.Address = "127.0.0.1:5009"
|
||||
cfg.AccessToken = &accesstoken.Config{}
|
||||
break cloudLoop
|
||||
|
||||
default:
|
||||
fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
|
||||
}
|
||||
}
|
||||
|
||||
// 8. 生成配置文件
|
||||
err = saveConfig(&cfg, configFilePath)
|
||||
if err != nil {
|
||||
fmt.Printf("\033[31m保存配置文件失败: %v\033[0m\n", err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("\033[32m配置文件已生成: %s\033[0m\n", configFilePath)
|
||||
|
||||
dbLoop:
|
||||
for {
|
||||
// 9. 询问是否生成库表结构
|
||||
rl.SetPrompt("\033[36m是否生成数据库表?(y/n): \033[0m")
|
||||
isCreate, err := rl.Readline()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(isCreate)) {
|
||||
case "y", "yes":
|
||||
// 10. 创建库表结构
|
||||
migrate(configFilePath)
|
||||
break dbLoop
|
||||
|
||||
case "n", "no":
|
||||
fmt.Println("\033[33m请自行创建数据库表,如需更改配置,请查看配置文件client.config.json \033[0m")
|
||||
break dbLoop
|
||||
|
||||
default:
|
||||
fmt.Println("\033[31m无效输入!请输入 y/n 或 yes/no \033[0m")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveConfig(cfg *clicfg.Config, configPath string) error {
|
||||
cfg.Logger = logger.Config{
|
||||
Level: "info",
|
||||
Output: "file",
|
||||
OutputFileName: "client.log",
|
||||
OutputDirectory: path.Join(".", "logs"),
|
||||
}
|
||||
cfg.SysEvent = sysevent.Config{
|
||||
Enabled: false,
|
||||
Address: "127.0.0.1:5672",
|
||||
Account: "cloudream",
|
||||
Password: "123456",
|
||||
VHost: "/",
|
||||
Exchange: "SysEvent",
|
||||
Queue: "SysEvent",
|
||||
}
|
||||
cfg.Connectivity.TestInterval = 300
|
||||
cfg.Downloader = downloader.Config{
|
||||
MaxStripCacheCount: 100,
|
||||
ECStripPrefetchCount: 1,
|
||||
}
|
||||
cfg.DownloadStrategy.HighLatencyHubMs = 35
|
||||
cfg.TickTock = ticktock.Config{
|
||||
ECFileSizeThreshold: 5242880,
|
||||
AccessStatHistoryWeight: 0.8,
|
||||
}
|
||||
cfg.Mount = &mntcfg.Config{
|
||||
Enabled: false,
|
||||
AttrTimeout: time.Second * 10,
|
||||
UploadPendingTime: time.Second * 30,
|
||||
CacheActiveTime: time.Minute * 1,
|
||||
CacheExpireTime: time.Minute * 1,
|
||||
ScanDataDirInterval: time.Minute * 10,
|
||||
}
|
||||
|
||||
configData, err := json.MarshalIndent(cfg, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("序列化配置失败: %w", err)
|
||||
}
|
||||
|
||||
configData = append(configData, '\n')
|
||||
|
||||
err = os.WriteFile(configPath, configData, 0644)
|
||||
if err != nil {
|
||||
return fmt.Errorf("写入配置文件失败: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
Loading…
Reference in New Issue