perf: 强化强制修改默认密码逻辑
This commit is contained in:
@@ -43,6 +43,7 @@ class AstrBotConfig(dict):
|
||||
"""不存在时载入默认配置"""
|
||||
with open(config_path, "w", encoding="utf-8-sig") as f:
|
||||
json.dump(default_config, f, indent=4, ensure_ascii=False)
|
||||
object.__setattr__(self, "first_deploy", True) # 标记第一次部署
|
||||
|
||||
with open(config_path, "r", encoding="utf-8-sig") as f:
|
||||
conf_str = f.read()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import jwt
|
||||
import datetime
|
||||
import asyncio
|
||||
from .route import Route, Response, RouteContext
|
||||
from quart import request
|
||||
from astrbot.core import WEBUI_SK, DEMO_MODE
|
||||
@@ -41,6 +42,7 @@ class AuthRoute(Route):
|
||||
.__dict__
|
||||
)
|
||||
else:
|
||||
await asyncio.sleep(3)
|
||||
return Response().error("用户名或密码错误").__dict__
|
||||
|
||||
async def edit_account(self):
|
||||
|
||||
@@ -46,11 +46,27 @@ class StatRoute(Route):
|
||||
h, m = divmod(m, 60)
|
||||
return f"{h}小时{m}分{s}秒"
|
||||
|
||||
def is_default_cred(self):
|
||||
username = self.config["dashboard"]["username"]
|
||||
password = self.config["dashboard"]["password"]
|
||||
return (
|
||||
username == "astrbot"
|
||||
and password == "77b90590a8945a7d36c963981a307dc9"
|
||||
and not DEMO_MODE
|
||||
)
|
||||
|
||||
async def get_version(self):
|
||||
return Response().ok({
|
||||
"version": VERSION,
|
||||
"dashboard_version": await get_dashboard_version(),
|
||||
}).__dict__
|
||||
return (
|
||||
Response()
|
||||
.ok(
|
||||
{
|
||||
"version": VERSION,
|
||||
"dashboard_version": await get_dashboard_version(),
|
||||
"change_pwd_hint": self.is_default_cred(),
|
||||
}
|
||||
)
|
||||
.__dict__
|
||||
)
|
||||
|
||||
async def get_start_time(self):
|
||||
return Response().ok({"start_time": self.core_lifecycle.start_time}).__dict__
|
||||
|
||||
@@ -5,18 +5,25 @@
|
||||
<img width="110" src="@/assets/images/astrbot_logo_mini.webp" alt="AstrBot Logo">
|
||||
</div>
|
||||
<div class="logo-text">
|
||||
<h2 class="text-secondary">AstrBot 仪表盘</h2>
|
||||
<h2 class="text-secondary">{{ title }}</h2>
|
||||
<!-- 父子组件传递css变量可能会出错,暂时使用十六进制颜色值 -->
|
||||
<h4 :style="{color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#000000aa' : '#ffffffcc'}"
|
||||
class="hint-text">登录以继续</h4>
|
||||
class="hint-text">{{ subtitle }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
// No props or other logic needed for this simple component
|
||||
import {useCustomizerStore} from "@/stores/customizer";
|
||||
import { useCustomizerStore } from "@/stores/customizer";
|
||||
|
||||
const props = withDefaults(defineProps<{
|
||||
title?: string;
|
||||
subtitle?: string;
|
||||
}>(), {
|
||||
title: 'AstrBot 仪表盘',
|
||||
subtitle: '欢迎使用'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -68,5 +75,4 @@ import {useCustomizerStore} from "@/stores/customizer";
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import {ref} from 'vue';
|
||||
import {ref, computed} from 'vue';
|
||||
import {useCustomizerStore} from '@/stores/customizer';
|
||||
import axios from 'axios';
|
||||
import Logo from '@/components/shared/Logo.vue';
|
||||
import {md5} from 'js-md5';
|
||||
import {useAuthStore} from '@/stores/auth';
|
||||
import {useCommonStore} from '@/stores/common';
|
||||
@@ -24,7 +25,7 @@ let dashboardHasNewVersion = ref(false);
|
||||
let dashboardCurrentVersion = ref('');
|
||||
let version = ref('');
|
||||
let releases = ref([]);
|
||||
let devCommits = ref([]); // 新增的 ref
|
||||
let devCommits = ref([]);
|
||||
|
||||
let installLoading = ref(false);
|
||||
|
||||
@@ -38,12 +39,38 @@ let releasesHeader = [
|
||||
{title: '操作', key: 'switch'}
|
||||
];
|
||||
|
||||
// Form validation
|
||||
const formValid = ref(true);
|
||||
const passwordRules = [
|
||||
(v: string) => !!v || '请输入密码',
|
||||
(v: string) => v.length >= 8 || '密码长度至少 8 位'
|
||||
];
|
||||
const usernameRules = [
|
||||
(v: string) => !v || v.length >= 3 || '用户名长度至少3位'
|
||||
];
|
||||
|
||||
// 显示密码相关
|
||||
const showPassword = ref(false);
|
||||
const showNewPassword = ref(false);
|
||||
|
||||
// 账户修改状态
|
||||
const accountEditStatus = ref({
|
||||
loading: false,
|
||||
success: false,
|
||||
error: false,
|
||||
message: ''
|
||||
});
|
||||
|
||||
const open = (link: string) => {
|
||||
window.open(link, '_blank');
|
||||
};
|
||||
|
||||
// 账户修改
|
||||
function accountEdit() {
|
||||
accountEditStatus.value.loading = true;
|
||||
accountEditStatus.value.error = false;
|
||||
accountEditStatus.value.success = false;
|
||||
|
||||
// md5加密
|
||||
// @ts-ignore
|
||||
if (password.value != '') {
|
||||
@@ -59,23 +86,29 @@ function accountEdit() {
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.status == 'error') {
|
||||
status.value = res.data.message;
|
||||
accountEditStatus.value.error = true;
|
||||
accountEditStatus.value.message = res.data.message;
|
||||
password.value = '';
|
||||
newPassword.value = '';
|
||||
return;
|
||||
}
|
||||
dialog.value = !dialog.value;
|
||||
status.value = res.data.message;
|
||||
accountEditStatus.value.success = true;
|
||||
accountEditStatus.value.message = res.data.message;
|
||||
setTimeout(() => {
|
||||
dialog.value = !dialog.value;
|
||||
const authStore = useAuthStore();
|
||||
authStore.logout();
|
||||
}, 1000);
|
||||
}, 2000);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
status.value = err
|
||||
accountEditStatus.value.error = true;
|
||||
accountEditStatus.value.message = typeof err === 'string' ? err : '修改失败,请重试';
|
||||
password.value = '';
|
||||
newPassword.value = '';
|
||||
})
|
||||
.finally(() => {
|
||||
accountEditStatus.value.loading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,6 +117,14 @@ function getVersion() {
|
||||
.then((res) => {
|
||||
botCurrVersion.value = "v" + res.data.data.version;
|
||||
dashboardCurrentVersion.value = res.data.data?.dashboard_version;
|
||||
let change_pwd_hint = res.data.data?.change_pwd_hint;
|
||||
if (change_pwd_hint) {
|
||||
dialog.value = true;
|
||||
accountWarning.value = true;
|
||||
localStorage.setItem('change_pwd_hint', 'true');
|
||||
} else {
|
||||
localStorage.removeItem('change_pwd_hint');
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
@@ -119,8 +160,6 @@ function checkUpdate() {
|
||||
function getReleases() {
|
||||
axios.get('/api/update/releases')
|
||||
.then((res) => {
|
||||
// releases.value = res.data.data;
|
||||
// 更新 published_at 的时间为本地时间
|
||||
releases.value = res.data.data.map((item: any) => {
|
||||
item.published_at = new Date(item.published_at).toLocaleString();
|
||||
return item;
|
||||
@@ -202,13 +241,6 @@ const commonStore = useCommonStore();
|
||||
commonStore.createEventSource(); // log
|
||||
commonStore.getStartTime();
|
||||
|
||||
|
||||
if (localStorage.getItem('change_pwd_hint') != null && localStorage.getItem('change_pwd_hint') == 'true') {
|
||||
dialog.value = true;
|
||||
accountWarning.value = true;
|
||||
localStorage.removeItem('change_pwd_hint');
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -390,48 +422,118 @@ if (localStorage.getItem('change_pwd_hint') != null && localStorage.getItem('cha
|
||||
</v-card>
|
||||
</v-dialog>
|
||||
|
||||
<v-dialog v-model="dialog" persistent width="700">
|
||||
<v-dialog v-model="dialog" persistent max-width="500">
|
||||
<template v-slot:activator="{ props }">
|
||||
<v-btn size="small" class="text-primary mr-4" color="var(--v-theme-surface)" variant="flat" rounded="sm" v-bind="props">
|
||||
<v-icon class="mr-1">mdi-account</v-icon>
|
||||
账户
|
||||
</v-btn>
|
||||
</template>
|
||||
<v-card>
|
||||
<v-card-title>
|
||||
<span class="text-h5">账户</span>
|
||||
</v-card-title>
|
||||
<v-card-text>
|
||||
<v-container>
|
||||
<v-row>
|
||||
<v-col cols="12">
|
||||
<v-card class="account-dialog">
|
||||
<v-card-text class="py-6">
|
||||
<div class="d-flex flex-column align-center mb-6">
|
||||
<logo title="AstrBot 仪表盘" subtitle="修改账户"></logo>
|
||||
</div>
|
||||
<v-alert
|
||||
v-if="accountWarning"
|
||||
type="warning"
|
||||
variant="tonal"
|
||||
border="start"
|
||||
class="mb-4"
|
||||
>
|
||||
<strong>安全提醒:</strong> 请修改默认密码以确保账户安全
|
||||
</v-alert>
|
||||
|
||||
<v-alert v-if="accountWarning" color="warning" style="margin-bottom: 16px;">
|
||||
<div>为了安全,请务必修改默认密码。</div>
|
||||
</v-alert>
|
||||
<v-alert
|
||||
v-if="accountEditStatus.success"
|
||||
type="success"
|
||||
variant="tonal"
|
||||
border="start"
|
||||
class="mb-4"
|
||||
>
|
||||
{{ accountEditStatus.message }}
|
||||
</v-alert>
|
||||
|
||||
<v-text-field label="原密码(*)" type="password" v-model="password" required
|
||||
variant="outlined"/>
|
||||
<v-alert
|
||||
v-if="accountEditStatus.error"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
border="start"
|
||||
class="mb-4"
|
||||
>
|
||||
{{ accountEditStatus.message }}
|
||||
</v-alert>
|
||||
|
||||
<v-text-field label="新密码(*)" type="password" v-model="newPassword" required
|
||||
variant="outlined"/>
|
||||
<v-form v-model="formValid" @submit.prevent="accountEdit">
|
||||
<v-text-field
|
||||
v-model="password"
|
||||
:append-inner-icon="showPassword ? 'mdi-eye-off' : 'mdi-eye'"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
label="当前密码"
|
||||
variant="outlined"
|
||||
required
|
||||
clearable
|
||||
@click:append-inner="showPassword = !showPassword"
|
||||
prepend-inner-icon="mdi-lock-outline"
|
||||
hide-details="auto"
|
||||
class="mb-4"
|
||||
></v-text-field>
|
||||
|
||||
<v-text-field label="新用户名(可选)" v-model="newUsername" variant="outlined"/>
|
||||
<v-text-field
|
||||
v-model="newPassword"
|
||||
:append-inner-icon="showNewPassword ? 'mdi-eye-off' : 'mdi-eye'"
|
||||
:type="showNewPassword ? 'text' : 'password'"
|
||||
:rules="passwordRules"
|
||||
label="新密码"
|
||||
variant="outlined"
|
||||
required
|
||||
clearable
|
||||
@click:append-inner="showNewPassword = !showNewPassword"
|
||||
prepend-inner-icon="mdi-lock-plus-outline"
|
||||
hint="密码长度至少 8 位"
|
||||
persistent-hint
|
||||
class="mb-4"
|
||||
></v-text-field>
|
||||
|
||||
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
<small>默认用户名和密码是 astrbot。</small>
|
||||
<br>
|
||||
<small>{{ status }}</small>
|
||||
<v-text-field
|
||||
v-model="newUsername"
|
||||
:rules="usernameRules"
|
||||
label="新用户名 (可选)"
|
||||
variant="outlined"
|
||||
clearable
|
||||
prepend-inner-icon="mdi-account-edit-outline"
|
||||
hint="留空表示不修改用户名"
|
||||
persistent-hint
|
||||
class="mb-3"
|
||||
></v-text-field>
|
||||
</v-form>
|
||||
|
||||
<div class="text-caption text-medium-emphasis mt-2">
|
||||
默认用户名和密码均为 astrbot
|
||||
</div>
|
||||
</v-card-text>
|
||||
<v-card-actions>
|
||||
|
||||
<v-divider></v-divider>
|
||||
|
||||
<v-card-actions class="pa-4">
|
||||
<v-spacer></v-spacer>
|
||||
<v-btn v-if="!accountWarning" color="blue-darken-1" variant="text" @click="dialog = false">
|
||||
关闭
|
||||
<v-btn
|
||||
v-if="!accountWarning"
|
||||
variant="tonal"
|
||||
color="secondary"
|
||||
@click="dialog = false"
|
||||
:disabled="accountEditStatus.loading"
|
||||
>
|
||||
取消
|
||||
</v-btn>
|
||||
<v-btn color="blue-darken-1" variant="text" @click="accountEdit">
|
||||
提交
|
||||
<v-btn
|
||||
color="primary"
|
||||
@click="accountEdit"
|
||||
:loading="accountEditStatus.loading"
|
||||
:disabled="!formValid"
|
||||
prepend-icon="mdi-content-save"
|
||||
>
|
||||
保存修改
|
||||
</v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
@@ -457,4 +559,27 @@ if (localStorage.getItem('change_pwd_hint') != null && localStorage.getItem('cha
|
||||
margin-top: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.account-dialog .v-card-text {
|
||||
padding-top: 24px;
|
||||
padding-bottom: 24px;
|
||||
}
|
||||
|
||||
.account-dialog .v-alert {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.account-dialog .v-btn {
|
||||
text-transform: none;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.account-dialog .v-avatar {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.account-dialog .v-avatar:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
</style>
|
||||
@@ -64,16 +64,12 @@ async function validate(values: any, { setErrors }: any) {
|
||||
prepend-inner-icon="mdi-lock"
|
||||
:disabled="loading"
|
||||
></v-text-field>
|
||||
|
||||
<v-label :style="{color: useCustomizerStore().uiTheme === 'PurpleTheme' ? '#000000aa' : '#ffffffcc'}" class="mt-1 mb-5">
|
||||
<small>默认用户名和密码为 astrbot</small>
|
||||
</v-label>
|
||||
|
||||
<v-btn
|
||||
color="secondary"
|
||||
:loading="isSubmitting || loading"
|
||||
block
|
||||
class="login-btn"
|
||||
class="login-btn mt-8"
|
||||
variant="flat"
|
||||
size="large"
|
||||
:disabled="valid"
|
||||
|
||||
Reference in New Issue
Block a user