Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions api/user/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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]) {
Expand Down
4 changes: 3 additions & 1 deletion app/src/components/OTPInput/OTPInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
1 change: 0 additions & 1 deletion app/src/views/other/Login.vue
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,6 @@ async function handlePasskeyLogin() {
<AButton
v-if="has_casdoor"
block
html-type="submit"
:loading="loading"
class="mb-5"
@click="loginWithCasdoor"
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
]
}
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
]
}
Expand Down
1 change: 1 addition & 0 deletions docs/.vitepress/config/zh_TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
]
}
Expand Down
61 changes: 61 additions & 0 deletions docs/guide/reset-password.md
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions docs/zh_CN/guide/reset-password.md
Original file line number Diff line number Diff line change
@@ -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 changes: 61 additions & 0 deletions docs/zh_TW/guide/reset-password.md
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions internal/cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand All @@ -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{
Expand Down
97 changes: 97 additions & 0 deletions internal/user/reset_password.go
Original file line number Diff line number Diff line change
@@ -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
}