|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- 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/publock"
- "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"
- var client_key_file = "client_key.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_key_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_key_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,
- }
- cfg.PubLock = publock.Config{
- LeaseExpiredSeconds: 5,
- }
-
- 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
- }
|