feat(vault): 凭据库(密码+2FA 动态码)与 MASTER_KEY 密钥托管
credentials 表为登录凭据唯一事实源(站点×登录方式,含 oauth/2FA);账号密码读写重定向凭据层并幂等迁移历史数据;TOTP 按 RFC6238 零依赖自实现,绑定需当前动态码校验;Key Escrow 防 MASTER_KEY 遗失;前端新增凭据库视图与账号 2FA 联动。64 pytest + 16 浏览器端到端验证通过。
This commit is contained in:
+10
-2
@@ -5,12 +5,13 @@ const NAVS = [
|
||||
{ key: 'dashboard', label: '总览', icon: '📊' },
|
||||
{ key: 'assets', label: '资产', icon: '📦' },
|
||||
{ key: 'monitor', label: '监控中心', icon: '⏳' },
|
||||
{ key: 'vault', label: '凭据库', icon: '🔐' },
|
||||
{ key: 'providers', label: '平台', icon: '🏢' },
|
||||
{ key: 'settings', label: '设置', icon: '⚙️' },
|
||||
];
|
||||
const VIEW_MAP = {
|
||||
dashboard: 'dashboard-view', assets: 'assets-view', monitor: 'monitor-view',
|
||||
providers: 'providers-view', settings: 'settings-view',
|
||||
vault: 'vault-view', providers: 'providers-view', settings: 'settings-view',
|
||||
// 服务器监控为隐藏路由(不进导航,从资产页 VPS「监控」进入)
|
||||
servers: 'servers-view',
|
||||
};
|
||||
@@ -85,6 +86,10 @@ const AppRoot = {
|
||||
<provider-modal></provider-modal>
|
||||
<account-modal></account-modal>
|
||||
<accounts-view-modal></accounts-view-modal>
|
||||
<credential-modal></credential-modal>
|
||||
|
||||
<!-- 复制等操作的轻提示(底部导航上方,不遮内容) -->
|
||||
<div v-if="toast.show" class="fixed bottom-20 md:bottom-6 left-1/2 -translate-x-1/2 z-[80] px-4 py-2 rounded-full bg-slate-900/90 dark:bg-slate-100/90 text-white dark:text-slate-900 text-xs shadow-lg pointer-events-none whitespace-nowrap">{{ toast.text }}</div>
|
||||
</div>`,
|
||||
setup() {
|
||||
const appName = Api.CFG.appName;
|
||||
@@ -96,6 +101,7 @@ const AppRoot = {
|
||||
function go(key) { navigate(key); }
|
||||
function quickAdd() {
|
||||
if (store.view === 'providers') return openProviderCreate();
|
||||
if (store.view === 'vault') return openCredentialCreate();
|
||||
// 资产页:按当前 TAB 预设类型;监控中心/总览/设置:默认 VPS
|
||||
let preset = 'vps';
|
||||
if (store.view === 'assets') {
|
||||
@@ -112,7 +118,7 @@ const AppRoot = {
|
||||
if (searchTimer) clearTimeout(searchTimer);
|
||||
searchTimer = setTimeout(() => { loadAssets(); }, 300);
|
||||
}
|
||||
return { store, appName, appVersion, appCommit, navs, currentNav, viewComponent, go, quickAdd, toggleDark, reload: debouncedReload };
|
||||
return { store, toast, appName, appVersion, appCommit, navs, currentNav, viewComponent, go, quickAdd, toggleDark, reload: debouncedReload };
|
||||
},
|
||||
};
|
||||
|
||||
@@ -121,6 +127,7 @@ const app = Vue.createApp(AppRoot);
|
||||
app.component('dashboard-view', DashboardView);
|
||||
app.component('assets-view', AssetsView);
|
||||
app.component('monitor-view', MonitorView);
|
||||
app.component('vault-view', VaultView);
|
||||
app.component('providers-view', ProvidersView);
|
||||
app.component('servers-view', ServersView);
|
||||
app.component('settings-view', SettingsView);
|
||||
@@ -128,6 +135,7 @@ app.component('asset-modal', AssetModal);
|
||||
app.component('provider-modal', ProviderModal);
|
||||
app.component('account-modal', AccountModal);
|
||||
app.component('accounts-view-modal', AccountsViewModal);
|
||||
app.component('credential-modal', CredentialModal);
|
||||
applyDark();
|
||||
initRouter();
|
||||
loadAll();
|
||||
|
||||
Reference in New Issue
Block a user