Переглянути джерело

Merge pull request #899 from 0xJacky/feat/cli-reset-pwd

Feat/cli reset pwd
Jacky 2 місяців тому
батько
коміт
78052ea0f0

+ 7 - 0
api/user/user.go

@@ -3,6 +3,7 @@ package user
 import (
 	"github.com/0xJacky/Nginx-UI/internal/user"
 	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
 	"github.com/0xJacky/Nginx-UI/settings"
 	"github.com/gin-gonic/gin"
 	"github.com/uozi-tech/cosy"
@@ -40,6 +41,12 @@ func InitManageUserRouter(g *gin.RouterGroup) {
 			}
 		})
 		c.BeforeDecodeHook(encryptPassword)
+		c.ExecutedHook(func(ctx *cosy.Ctx[model.User]) {
+			if ctx.Payload["password"] != "" {
+				a := query.AuthToken
+				_, _ = a.Where(a.UserID.Eq(ctx.ID)).Delete()
+			}
+		})
 	})
 
 	c.DestroyHook(func(c *cosy.Ctx[model.User]) {

+ 3 - 1
app/src/components/OTPInput/OTPInput.vue

@@ -3,7 +3,9 @@ import VOtpInput from 'vue3-otp-input'
 
 const emit = defineEmits(['onComplete'])
 
-const data = defineModel<string>()
+const data = defineModel<string>({
+  default: '',
+})
 
 // eslint-disable-next-line vue/require-typed-ref
 const refOtp = ref()

+ 0 - 1
app/src/views/other/Login.vue

@@ -190,7 +190,6 @@ async function handlePasskeyLogin() {
               <AButton
                 v-if="has_casdoor"
                 block
-                html-type="submit"
                 :loading="loading"
                 class="mb-5"
                 @click="loginWithCasdoor"

+ 1 - 0
docs/.vitepress/config/en.ts

@@ -64,6 +64,7 @@ export const enConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           items: [
             { text: 'Nginx Proxy Example', link: '/guide/nginx-proxy-example' },
+            { text: 'Reset Password', link: '/guide/reset-password' },
             { text: 'License', link: '/guide/license' }
           ]
         }

+ 1 - 0
docs/.vitepress/config/zh_CN.ts

@@ -69,6 +69,7 @@ export const zhCNConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           items: [
             { text: 'Nginx 代理示例', link: '/zh_CN/guide/nginx-proxy-example' },
+            { text: '重置密码', link: '/zh_CN/guide/reset-password' },
             { text: '开源协议', link: '/zh_CN/guide/license' }
           ]
         }

+ 1 - 0
docs/.vitepress/config/zh_TW.ts

@@ -69,6 +69,7 @@ export const zhTWConfig: LocaleSpecificConfig<DefaultTheme.Config> = {
           collapsed: false,
           items: [
             { text: 'Nginx 代理示例', link: '/zh_TW/guide/nginx-proxy-example' },
+            { text: '重置密碼', link: '/zh_TW/guide/reset-password' },
             { text: '開源協議', link: '/zh_TW/guide/license' }
           ]
         }

+ 61 - 0
docs/guide/reset-password.md

@@ -0,0 +1,61 @@
+# Reset Initial User Password
+
+The `reset-password` command allows you to reset the initial administrator account's password to a randomly generated 12-character password that includes uppercase letters, lowercase letters, numbers, and special symbols.
+
+## Usage
+
+To reset the initial user's password, run:
+
+```bash
+nginx-ui reset-password --config=/path/to/app.ini
+```
+
+The command will:
+1. Generate a secure random password (12 characters)
+2. Reset the password for the initial user account (user ID 1)
+3. Output the new password in the application logs
+
+## Parameters
+
+- `--config`: (Required) Path to the Nginx UI configuration file
+
+## Example
+
+```bash
+# Reset the password using the default config file location
+nginx-ui reset-password --config=/path/to/app.ini
+
+# The output will include the generated password
+2025-03-03 03:24:41     INFO    user/reset_password.go:52       confPath: ../app.ini
+2025-03-03 03:24:41     INFO    user/reset_password.go:59       dbPath: ../database.db
+2025-03-03 03:24:41     INFO    user/reset_password.go:92       User: root, Password: X&K^(X0m(E&&
+```
+
+## Configuration File Location
+
+- If you installed Nginx UI using the Linux one-click installation script, the configuration file is located at:
+  ```
+  /usr/local/etc/nginx-ui/app.ini
+  ```
+
+  You can directly use the following command:
+  ```bash
+  nginx-ui reset-password --config /usr/local/etc/nginx-ui/app.ini
+  ```
+
+## Docker Usage
+
+If you're running Nginx UI in a Docker container, you need to use the `docker exec` command:
+
+```bash
+docker exec -it <nginx-ui-container> nginx-ui reset-password --config=/etc/nginx-ui/app.ini
+```
+
+Replace `<nginx-ui-container>` with your actual container name or ID.
+
+## Notes
+
+- This command is useful if you've forgotten the initial administrator password
+- The new password will be displayed in the logs, so be sure to copy it immediately
+- You must have access to the server's command line to use this feature
+- The database file must exist for this command to work 

+ 61 - 0
docs/zh_CN/guide/reset-password.md

@@ -0,0 +1,61 @@
+# 重置初始用户密码
+
+`reset-password` 命令允许您将初始管理员账户的密码重置为随机生成的12位密码,包含大写字母、小写字母、数字和特殊符号。
+
+## 使用方法
+
+要重置初始用户的密码,请运行:
+
+```bash
+nginx-ui reset-password --config=/path/to/app.ini
+```
+
+此命令将:
+1. 生成一个安全的随机密码(12个字符)
+2. 重置初始用户账户(用户ID 1)的密码
+3. 在应用程序日志中输出新密码
+
+## 参数
+
+- `--config`:(必填)Nginx UI 配置文件的路径
+
+## 示例
+
+```bash
+# 使用默认配置文件位置重置密码
+nginx-ui reset-password --config=/path/to/app.ini
+
+# 输出将包含生成的密码
+2025-03-03 03:24:41     INFO    user/reset_password.go:52       confPath: ../app.ini
+2025-03-03 03:24:41     INFO    user/reset_password.go:59       dbPath: ../database.db
+2025-03-03 03:24:41     INFO    user/reset_password.go:92       User: root, Password: X&K^(X0m(E&&
+```
+
+## 配置文件位置
+
+- 如果您使用 Linux 一键安装脚本安装的 Nginx UI,配置文件位于:
+  ```
+  /usr/local/etc/nginx-ui/app.ini
+  ```
+  
+  您可以直接使用以下命令:
+  ```bash
+  nginx-ui reset-password --config /usr/local/etc/nginx-ui/app.ini
+  ```
+
+## Docker 使用方法
+
+如果您在 Docker 容器中运行 Nginx UI,需要使用 `docker exec` 命令:
+
+```bash
+docker exec -it <nginx-ui-container> nginx-ui reset-password --config=/etc/nginx-ui/app.ini
+```
+
+请将 `<nginx-ui-container>` 替换为您实际的容器名称或 ID。
+
+## 注意事项
+
+- 如果您忘记了初始管理员密码,此命令很有用
+- 新密码将显示在日志中,请确保立即复制它
+- 您必须有权访问服务器的命令行才能使用此功能
+- 数据库文件必须存在才能使此命令正常工作 

+ 61 - 0
docs/zh_TW/guide/reset-password.md

@@ -0,0 +1,61 @@
+# 重置初始用戶密碼
+
+`reset-password` 命令允許您將初始管理員賬戶的密碼重置為隨機生成的12位密碼,包含大寫字母、小寫字母、數字和特殊符號。
+
+## 使用方法
+
+要重置初始用戶的密碼,請運行:
+
+```bash
+nginx-ui reset-password --config=/path/to/app.ini
+```
+
+此命令將:
+1. 生成一個安全的隨機密碼(12個字符)
+2. 重置初始用戶賬戶(用戶ID 1)的密碼
+3. 在應用程序日誌中輸出新密碼
+
+## 參數
+
+- `--config`:(必填)Nginx UI 配置文件的路徑
+
+## 示例
+
+```bash
+# 使用默認配置文件位置重置密碼
+nginx-ui reset-password --config=/path/to/app.ini
+
+# 輸出將包含生成的密碼
+2025-03-03 03:24:41     INFO    user/reset_password.go:52       confPath: ../app.ini
+2025-03-03 03:24:41     INFO    user/reset_password.go:59       dbPath: ../database.db
+2025-03-03 03:24:41     INFO    user/reset_password.go:92       User: root, Password: X&K^(X0m(E&&
+```
+
+## 配置文件位置
+
+- 如果您使用 Linux 一鍵安裝腳本安裝的 Nginx UI,配置文件位於:
+  ```
+  /usr/local/etc/nginx-ui/app.ini
+  ```
+  
+  您可以直接使用以下命令:
+  ```bash
+  nginx-ui reset-password --config /usr/local/etc/nginx-ui/app.ini
+  ```
+
+## Docker 使用方法
+
+如果您在 Docker 容器中運行 Nginx UI,需要使用 `docker exec` 命令:
+
+```bash
+docker exec -it <nginx-ui-container> nginx-ui reset-password --config=/etc/nginx-ui/app.ini
+```
+
+請將 `<nginx-ui-container>` 替換為您實際的容器名稱或 ID。
+
+## 注意事項
+
+- 如果您忘記了初始管理員密碼,此命令很有用
+- 新密碼將顯示在日誌中,請確保立即複製它
+- 您必須有權訪問服務器的命令行才能使用此功能
+- 數據庫文件必須存在才能使此命令正常工作 

+ 6 - 0
internal/cmd/main.go

@@ -5,6 +5,7 @@ import (
 	"log"
 	"os"
 
+	"github.com/0xJacky/Nginx-UI/internal/user"
 	"github.com/0xJacky/Nginx-UI/internal/version"
 	"github.com/urfave/cli/v3"
 )
@@ -24,6 +25,11 @@ func NewAppCmd() *cli.Command {
 					return nil
 				},
 			},
+			{
+				Name:  "reset-password",
+				Usage: "Reset the initial user password",
+				Action: user.ResetInitUserPassword,
+			},
 		},
 		Flags: []cli.Flag{
 			&cli.StringFlag{

+ 97 - 0
internal/user/reset_password.go

@@ -0,0 +1,97 @@
+package user
+
+import (
+	"context"
+	"crypto/rand"
+	"math/big"
+	"os"
+	"path"
+
+	"github.com/0xJacky/Nginx-UI/model"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/0xJacky/Nginx-UI/settings"
+	"github.com/gin-gonic/gin"
+	"github.com/pkg/errors"
+	"github.com/uozi-tech/cosy"
+	sqlite "github.com/uozi-tech/cosy-driver-sqlite"
+	"github.com/uozi-tech/cosy/logger"
+	cSettings "github.com/uozi-tech/cosy/settings"
+	"github.com/urfave/cli/v3"
+	"golang.org/x/crypto/bcrypt"
+)
+
+var (
+	ErrConfigNotFound    = errors.New("config not found")
+	ErrDBFileNotFound    = errors.New("db file not found")
+	ErrInitUserNotExists = errors.New("init user not exists")
+)
+
+func generateRandomPassword(length int) (string, error) {
+	const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@#$%^&*()-_=+"
+	password := make([]byte, length)
+	charsetLength := big.NewInt(int64(len(charset)))
+
+	for i := 0; i < length; i++ {
+		randomIndex, err := rand.Int(rand.Reader, charsetLength)
+		if err != nil {
+			return "", err
+		}
+		password[i] = charset[randomIndex.Int64()]
+	}
+	return string(password), nil
+}
+
+func ResetInitUserPassword(ctx context.Context, command *cli.Command) error {
+	confPath := command.String("config")
+	settings.Init(confPath)
+
+	cSettings.ServerSettings.RunMode = gin.ReleaseMode
+
+	logger.Init(cSettings.ServerSettings.RunMode)
+
+	logger.Infof("confPath: %s", confPath)
+
+	if _, err := os.Stat(confPath); os.IsNotExist(err) {
+		return ErrConfigNotFound
+	}
+
+	dbPath := path.Join(path.Dir(confPath), settings.DatabaseSettings.Name+".db")
+	logger.Infof("dbPath: %s", dbPath)
+	// check if db file exists
+	if _, err := os.Stat(dbPath); os.IsNotExist(err) {
+		return ErrDBFileNotFound
+	}
+
+	db := cosy.InitDB(sqlite.Open(path.Dir(cSettings.ConfPath), settings.DatabaseSettings))
+	model.Use(db)
+	query.Init(db)
+
+	u := query.User
+	user, err := u.FirstByID(1)
+	if err != nil {
+		return ErrInitUserNotExists
+	}
+
+	pwd, err := generateRandomPassword(12)
+	if err != nil {
+		return err
+	}
+
+	pwdBytes, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
+	if err != nil {
+		return err
+	}
+
+	_, err = u.Where(u.ID.Eq(1)).Updates(&model.User{
+		Password: string(pwdBytes),
+	})
+	if err != nil {
+		return err
+	}
+
+	a := query.AuthToken
+	_, _ = a.Where(a.UserID.Eq(1)).Delete()
+
+	logger.Infof("User: %s, Password: %s", user.Name, pwd)
+	return nil
+}