fix:login page route

This commit is contained in:
马登山
2025-07-15 16:16:06 +08:00
parent 001d1e05e8
commit d49f10b379
3 changed files with 125 additions and 93 deletions
+34 -23
View File
@@ -1,6 +1,6 @@
import type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@aws-sdk/types";
import { getStsToken } from "~/lib/sts";
import type { SiteConfig } from "~/types/config";
import type { AwsCredentialIdentity, AwsCredentialIdentityProvider } from '@aws-sdk/types';
import { getStsToken } from '~/lib/sts';
import type { SiteConfig } from '~/types/config';
interface Credentials {
AccessKeyId?: string;
@@ -10,48 +10,59 @@ interface Credentials {
}
export function useAuth() {
const store = useLocalStorage('auth.credentials', {})
const store = useLocalStorage('auth.credentials', {});
const setCredentials = (credentials: Credentials) => {
store.value = credentials
}
store.value = credentials;
};
const getCredentials = () => {
if (!isValidCredentials(store.value)) {
return
return;
}
return store.value
}
return store.value;
};
const isExpired = (expiration: string) => expiration ? new Date(expiration) < new Date() : false
const isExpired = (expiration: string) =>
expiration ? new Date(expiration) < new Date() : false;
const isValidCredentials = (credentials: Credentials) => {
return !!credentials?.AccessKeyId && !!credentials?.SecretAccessKey && !!credentials?.SessionToken && credentials?.Expiration && !isExpired(credentials.Expiration)
}
return (
!!credentials?.AccessKeyId &&
!!credentials?.SecretAccessKey &&
!!credentials?.SessionToken &&
credentials?.Expiration &&
!isExpired(credentials.Expiration)
);
};
const login = async (
credentials: AwsCredentialIdentity | AwsCredentialIdentityProvider,
customConfig?: SiteConfig
) => {
const credentialsResponse = await getStsToken(credentials, 'arn:aws:iam::*:role/Admin', customConfig)
const credentialsResponse = await getStsToken(
credentials,
'arn:aws:iam::*:role/Admin',
customConfig
);
setCredentials({
...credentialsResponse,
Expiration: credentialsResponse.Expiration?.toISOString()
})
Expiration: credentialsResponse.Expiration?.toISOString(),
});
return credentialsResponse
}
return credentialsResponse;
};
const logout = () => {
store.value = {}
}
store.value = {};
};
const logoutAndRedirect = () => {
logout()
window.location.href = '/auth/login'
}
logout();
window.location.href = '/rustfs/console/auth/login';
};
return {
login,
@@ -59,5 +70,5 @@ export function useAuth() {
logoutAndRedirect,
credentials: ref<Credentials | undefined>(getCredentials()),
isAuthenticated: ref(isValidCredentials(store.value)),
}
};
}
+1 -1
View File
@@ -53,7 +53,7 @@ class ApiClient {
const message = useMessage();
// 清除登录信息
await useAuth().logout();
window.location.href = '/auth/login';
window.location.href = '/rustfs/console/auth/login';
return;
}
+90 -69
View File
@@ -1,106 +1,114 @@
<script lang="ts" setup>
await setPageLayout('plain')
await setPageLayout('plain');
import { onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
const { t } = useI18n()
const router = useRouter()
const message = useMessage()
const { t } = useI18n();
const router = useRouter();
const message = useMessage();
const serverHost = ref('')
const isValid = ref(false)
const serverHost = ref('');
const isValid = ref(false);
const validateAndSave = async () => {
try {
if (serverHost.value) {
// 更宽松的URL验证
let urlToValidate = serverHost.value.trim()
let urlToValidate = serverHost.value.trim();
// 如果没有协议,自动添加https://
if (!urlToValidate.match(/^https?:\/\//)) {
urlToValidate = 'https://' + urlToValidate
urlToValidate = 'https://' + urlToValidate;
}
// 验证URL格式
const url = new URL(urlToValidate)
console.log('Valid URL:', url.href) // 调试信息
const url = new URL(urlToValidate);
console.log('Valid URL:', url.href); // 调试信息
// 保存原始输入(如果用户没输入协议,就保存添加了协议的版本)
const urlToSave = serverHost.value.match(/^https?:\/\//) ? serverHost.value : urlToValidate
localStorage.setItem('rustfs-server-host', urlToSave)
const urlToSave = serverHost.value.match(/^https?:\/\//) ? serverHost.value : urlToValidate;
localStorage.setItem('rustfs-server-host', urlToSave);
// 如果我们自动添加了协议,更新输入框显示
if (!serverHost.value.match(/^https?:\/\//)) {
serverHost.value = urlToValidate
serverHost.value = urlToValidate;
}
} else {
// 如果为空,清除localStorage,使用默认值
localStorage.removeItem('rustfs-server-host')
localStorage.removeItem('rustfs-server-host');
}
// 清除配置缓存
const { configManager } = await import('~/utils/config')
configManager.clearCache()
message.success(t('Server configuration saved successfully'))
const { configManager } = await import('~/utils/config');
configManager.clearCache();
message.success(t('Server configuration saved successfully'));
// 延迟后刷新页面并跳转,确保配置完全生效
setTimeout(() => {
window.location.href = '/auth/login'
}, 200)
window.location.href = '/rustfs/console/auth/login';
}, 200);
} catch (error) {
console.error('URL validation error:', error) // 调试信息
message.error(t('Invalid server address format') + ': ' + (error as Error).message)
console.error('URL validation error:', error); // 调试信息
message.error(t('Invalid server address format') + ': ' + (error as Error).message);
}
}
};
const resetToCurrentHost = async () => {
// 清除localStorage中的配置,让系统回到默认状态(使用当前host)
localStorage.removeItem('rustfs-server-host')
localStorage.removeItem('rustfs-server-host');
// 清除配置缓存
const { configManager } = await import('~/utils/config')
configManager.clearCache()
const { configManager } = await import('~/utils/config');
configManager.clearCache();
// 清空输入框,表示使用默认配置
serverHost.value = ''
message.success(t('Reset to default successfully'))
serverHost.value = '';
message.success(t('Reset to default successfully'));
// 延迟后跳转到登录页面,确保配置完全生效
setTimeout(() => {
window.location.href = '/auth/login'
}, 200)
}
window.location.href = '/rustfs/console/auth/login';
}, 200);
};
const skipConfig = () => {
router.push('/auth/login')
}
router.push('/auth/login');
};
onMounted(() => {
// 检查是否已有配置
const saved = localStorage.getItem('rustfs-server-host')
const saved = localStorage.getItem('rustfs-server-host');
if (saved) {
serverHost.value = saved
isValid.value = true
serverHost.value = saved;
isValid.value = true;
}
})
});
</script>
<template>
<div class="lg:p-20 flex flex-col items-center justify-center min-h-screen bg-gray-100 dark:bg-neutral-800">
<div
class="lg:p-20 flex flex-col items-center justify-center min-h-screen bg-gray-100 dark:bg-neutral-800"
>
<img src="~/assets/backgrounds/scillate.svg" class="absolute inset-0 z-0 opacity-45" alt="" />
<div class="flex-1 flex w-full z-10 max-w-7xl lg:max-h-[85vh] shadow-lg rounded-lg overflow-hidden mx-auto dark:bg-neutral-800 dark:border-neutral-700">
<div
class="flex-1 flex w-full z-10 max-w-7xl lg:max-h-[85vh] shadow-lg rounded-lg overflow-hidden mx-auto dark:bg-neutral-800 dark:border-neutral-700"
>
<div class="hidden lg:block w-1/2">
<auth-heros-static></auth-heros-static>
</div>
<div class="w-full lg:w-1/2 flex flex-col justify-center items-center bg-white dark:bg-neutral-900 dark:border-neutral-700 relative">
<div
class="w-full lg:w-1/2 flex flex-col justify-center items-center bg-white dark:bg-neutral-900 dark:border-neutral-700 relative"
>
<div class="max-w-sm w-full p-4 sm:p-7">
<img src="https://rustfs.com/rustfs.logo.svg" class="max-w-28" alt="" />
<div class="py-6">
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white">{{ t('Server Configuration') }}</h1>
<h1 class="block text-2xl font-bold text-gray-800 dark:text-white">
{{ t('Server Configuration') }}
</h1>
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400">
{{ t('Please configure your RustFS server address') }}
</p>
@@ -111,14 +119,16 @@ onMounted(() => {
<form @submit.prevent="validateAndSave" autocomplete="off">
<div class="grid gap-y-6">
<div>
<label for="serverHost" class="block text-sm mb-2 dark:text-white">{{ t('Server Address') }}</label>
<label for="serverHost" class="block text-sm mb-2 dark:text-white">{{
t('Server Address')
}}</label>
<div class="text-xs text-gray-500 mb-2">
{{ t('Leave empty to use current host as default') }}
</div>
<n-input
v-model:value="serverHost"
type="text"
:placeholder="t('Please enter server address (e.g., http://localhost:9000)')"
<n-input
v-model:value="serverHost"
type="text"
:placeholder="t('Please enter server address (e.g., http://localhost:9000)')"
/>
<div class="text-xs text-gray-500 mt-1">
{{ t('Example: http://localhost:9000 or https://your-domain.com') }}
@@ -126,18 +136,26 @@ onMounted(() => {
</div>
<div class="flex gap-3">
<button type="submit"
class="flex-1 py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none">
<button
type="submit"
class="flex-1 py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-transparent bg-blue-600 text-white hover:bg-blue-700 focus:outline-none focus:bg-blue-700 disabled:opacity-50 disabled:pointer-events-none"
>
{{ t('Save Configuration') }}
</button>
<button type="button" @click="resetToCurrentHost"
class="py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-800">
<button
type="button"
@click="resetToCurrentHost"
class="py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-800"
>
{{ t('Reset') }}
</button>
<button type="button" @click="skipConfig"
class="py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-800">
<button
type="button"
@click="skipConfig"
class="py-3 px-4 inline-flex justify-center items-center gap-x-2 text-sm font-medium rounded-lg border border-gray-200 bg-white text-gray-800 shadow-sm hover:bg-gray-50 disabled:opacity-50 disabled:pointer-events-none dark:bg-neutral-900 dark:border-neutral-700 dark:text-white dark:hover:bg-neutral-800"
>
{{ t('Skip') }}
</button>
</div>
@@ -148,7 +166,10 @@ onMounted(() => {
<div class="my-8">
<p class="text-sm text-gray-600 dark:text-neutral-400">
{{ t('Need help?') }} <NuxtLink to="https://docs.rustfs.com" class="text-blue-600 hover:underline">{{ t('View Documentation') }}</NuxtLink>
{{ t('Need help?') }}
<NuxtLink to="https://docs.rustfs.com" class="text-blue-600 hover:underline">{{
t('View Documentation')
}}</NuxtLink>
</p>
</div>
@@ -164,4 +185,4 @@ onMounted(() => {
</div>
</div>
</div>
</template>
</template>