mirror of
https://github.com/rustfs/console.git
synced 2026-09-19 01:47:44 +08:00
feat: custom server config
This commit is contained in:
+152
-31
@@ -1,14 +1,14 @@
|
||||
<script lang="ts" setup>
|
||||
await setPageLayout('plain')
|
||||
|
||||
import { Motion } from "motion-v"
|
||||
import { ref } from 'vue'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRouter } from 'vue-router'
|
||||
import AuroraBackground from '~/components/ui/aurora-background/AuroraBackground.vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { configManager } from '~/utils/config'
|
||||
|
||||
const { t } = useI18n()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
const method = ref('accessKeyAndSecretKey')
|
||||
const accessKeyAndSecretKey = ref({
|
||||
@@ -22,54 +22,123 @@ const sts = ref({
|
||||
sessionToken: '',
|
||||
})
|
||||
|
||||
// 服务端配置
|
||||
const serverConfig = ref({
|
||||
protocol: 'http',
|
||||
host: 'localhost',
|
||||
port: '9000',
|
||||
region: 'us-east-1'
|
||||
})
|
||||
|
||||
const message = useMessage()
|
||||
const auth = useAuth()
|
||||
|
||||
// 控制服务器配置折叠面板展开
|
||||
const expandedNames = ref<string[]>([])
|
||||
// 警告提示内容
|
||||
const configAlert = ref('')
|
||||
|
||||
// 检查是否需要默认展开服务器配置
|
||||
onMounted(() => {
|
||||
if (route.query.showConfig === '1') {
|
||||
expandedNames.value = ['server-config']
|
||||
configAlert.value = t('No valid server configuration detected, please check server configuration')
|
||||
}
|
||||
serverUrl.value = `${serverConfig.value.protocol}://${serverConfig.value.host}:${serverConfig.value.port}`
|
||||
})
|
||||
|
||||
const handleLogin = async () => {
|
||||
// 验证并解析URL
|
||||
validateAndParseUrl()
|
||||
if (urlValidationStatus.value === 'error') {
|
||||
expandedNames.value = ['server-config']
|
||||
return
|
||||
}
|
||||
|
||||
const credentials = method.value === 'accessKeyAndSecretKey' ? accessKeyAndSecretKey.value : sts.value
|
||||
|
||||
try {
|
||||
// 保存配置到 localStorage
|
||||
configManager.saveConfig({
|
||||
protocol: serverConfig.value.protocol,
|
||||
host: serverConfig.value.host,
|
||||
port: serverConfig.value.port,
|
||||
region: serverConfig.value.region
|
||||
})
|
||||
|
||||
message.success(t('Server configuration saved'))
|
||||
await auth.login(credentials)
|
||||
message.success(t('Login Success'))
|
||||
window.location.href = '/'
|
||||
} catch (error) {
|
||||
expandedNames.value = ['server-config']
|
||||
configAlert.value = t('Login failed, please check server configuration')
|
||||
message.error(t('Login Failed'))
|
||||
}
|
||||
}
|
||||
|
||||
const serverUrl = ref('')
|
||||
const urlValidationStatus = ref<'success' | 'warning' | 'error' | undefined>(undefined)
|
||||
const urlError = ref('')
|
||||
|
||||
const validateAndParseUrl = () => {
|
||||
const url = serverUrl.value.trim()
|
||||
|
||||
if (!url) {
|
||||
urlValidationStatus.value = 'error'
|
||||
urlError.value = t('Server URL is required')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const urlObj = new URL(url)
|
||||
|
||||
// 验证协议
|
||||
if (!['http:', 'https:'].includes(urlObj.protocol)) {
|
||||
urlValidationStatus.value = 'error'
|
||||
urlError.value = t('Only HTTP and HTTPS protocols are supported')
|
||||
return
|
||||
}
|
||||
|
||||
// 验证主机
|
||||
if (!urlObj.hostname) {
|
||||
urlValidationStatus.value = 'error'
|
||||
urlError.value = t('Invalid hostname')
|
||||
return
|
||||
}
|
||||
|
||||
// 解析并更新配置
|
||||
serverConfig.value.protocol = urlObj.protocol.replace(':', '')
|
||||
serverConfig.value.host = urlObj.hostname
|
||||
serverConfig.value.port = urlObj.port || (urlObj.protocol === 'https:' ? '443' : '80')
|
||||
|
||||
urlValidationStatus.value = 'success'
|
||||
urlError.value = ''
|
||||
} catch (error) {
|
||||
urlValidationStatus.value = 'error'
|
||||
urlError.value = t('Invalid URL format')
|
||||
}
|
||||
}
|
||||
|
||||
const onUrlInput = () => {
|
||||
// 清除之前的错误状态
|
||||
if (urlValidationStatus.value === 'error') {
|
||||
urlValidationStatus.value = undefined
|
||||
urlError.value = ''
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.bg {
|
||||
background-color: #191919;
|
||||
opacity: 1;
|
||||
background-image: linear-gradient(#090909 1.6px, transparent 1.6px),
|
||||
linear-gradient(to right, #090909 1.6px, #191919 1.6px);
|
||||
background-size: 32px 32px;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
<template>
|
||||
<div class="lg:p-20 flex flex-col items-center justify-center min-h-screen bg-gray-100 dark:bg-neutral-700">
|
||||
<AuroraBackground class="absolute inset-0 z-0">
|
||||
<Motion as="div" :initial="{ opacity: 0, y: 40, filter: 'blur(10px)' }" :in-view="{
|
||||
opacity: 1,
|
||||
y: 0,
|
||||
filter: 'blur(0px)',
|
||||
}" :transition="{
|
||||
delay: 0.3,
|
||||
duration: 0.8,
|
||||
ease: 'easeInOut',
|
||||
}" class="relative flex flex-col items-center justify-center gap-4 px-4">
|
||||
</Motion>
|
||||
</AuroraBackground>
|
||||
<div class="flex-1 flex w-full z-10 max-w-7xl lg:max-h-[75vh] shadow-lg rounded-lg overflow-hidden mx-auto dark:bg-neutral-800 dark:border-neutral-700">
|
||||
<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="hidden lg:block w-1/2">
|
||||
<auth-heros-ripple></auth-heros-ripple>
|
||||
<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="max-w-sm w-full p-4 sm:p-7">
|
||||
<img src="~/assets/logo.svg" class="max-w-28" alt="" />
|
||||
<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('Login to RustFS Console') }}</h1>
|
||||
<p class="mt-2 text-sm text-gray-600 dark:text-neutral-400">
|
||||
@@ -79,6 +148,58 @@ const handleLogin = async () => {
|
||||
</div>
|
||||
|
||||
<div class="mt-5 space-y-4">
|
||||
<!-- 服务器配置折叠面板 -->
|
||||
<div class="mb-2">
|
||||
<div class="transition-all duration-300 ease-in-out">
|
||||
<!-- 折叠时的卡片 -->
|
||||
<div v-if="!expandedNames.includes('server-config')" @click="expandedNames = ['server-config']" class="w-full cursor-pointer">
|
||||
<div
|
||||
class="rounded border border-gray-200 dark:border-neutral-700 bg-gray-50 dark:bg-neutral-800 px-4 py-2 flex items-center justify-between hover:bg-gray-100 dark:hover:bg-neutral-700 transition-colors">
|
||||
<div class="truncate">
|
||||
<span class="font-mono text-sm">{{ serverConfig.protocol }}://{{ serverConfig.host }}:{{ serverConfig.port }}</span>
|
||||
<span class="ml-2 text-xs text-gray-500">{{ serverConfig.region }}</span>
|
||||
</div>
|
||||
<Icon :size="18" name="ri:arrow-right-s-fill" class="text-gray-600 dark:text-gray-300 hover:text-blue-500 transition-all duration-200" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 展开时的内容 -->
|
||||
<div v-else class="border border-gray-200 dark:border-neutral-700 rounded-lg overflow-hidden animate-in fade-in slide-in-from-top-2 duration-300">
|
||||
<!-- 标题栏 -->
|
||||
<div @click="expandedNames = []"
|
||||
class="w-full cursor-pointer flex items-center justify-between px-4 py-3 bg-gray-50 dark:bg-neutral-800 hover:bg-gray-100 dark:hover:bg-neutral-700 transition-colors border-b border-gray-200 dark:border-neutral-700">
|
||||
<span class="flex items-center text-sm font-medium">
|
||||
<Icon :size="16" name="ri:settings-3-line" class="mr-2 text-gray-500" />
|
||||
{{ t('Server Configuration') }}
|
||||
</span>
|
||||
<Icon :size="18" name="ri:arrow-down-s-fill" class="text-gray-600 dark:text-gray-300 hover:text-blue-500 transition-all duration-200 transform" />
|
||||
</div>
|
||||
|
||||
<!-- 配置表单 -->
|
||||
<div class="p-4 bg-white dark:bg-neutral-900">
|
||||
<div v-if="configAlert"
|
||||
class="mb-4 px-3 py-2 rounded bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 border border-red-200 dark:border-red-800 text-sm">
|
||||
{{ configAlert }}
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">{{ t('Server URL') }}</label>
|
||||
<n-input v-model:value="serverUrl" size="medium" type="text" :placeholder="'http://localhost:9000'" :status="urlValidationStatus"
|
||||
@blur="validateAndParseUrl" @input="onUrlInput" />
|
||||
<div v-if="urlError" class="mt-1 text-xs text-red-500">{{ urlError }}</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium mb-2 text-gray-700 dark:text-gray-300">{{ t('Region') }}</label>
|
||||
<n-input v-model:value="serverConfig.region" size="medium" type="text" :placeholder="'us-east-1'" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-tabs type="segment" animated size="small" v-model:value="method">
|
||||
<n-tab-pane name="accessKeyAndSecretKey" :label="t('Key Login')" />
|
||||
<n-tab-pane name="sts" :label="t('STS Login')" />
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { configManager } from '~/utils/config'
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
// 服务端配置
|
||||
const serverConfig = ref({
|
||||
protocol: 'http',
|
||||
host: 'localhost',
|
||||
port: '9000',
|
||||
region: 'us-east-1'
|
||||
})
|
||||
|
||||
// 加载当前配置
|
||||
const loadCurrentConfig = () => {
|
||||
const currentConfig = configManager.loadConfig()
|
||||
if (currentConfig) {
|
||||
// 从 baseURL 解析配置
|
||||
const url = new URL(currentConfig.api.baseURL)
|
||||
serverConfig.value = {
|
||||
protocol: url.protocol.replace(':', ''),
|
||||
host: url.hostname,
|
||||
port: url.port || (url.protocol === 'https:' ? '443' : '80'),
|
||||
region: currentConfig.s3.region
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存配置
|
||||
const saveConfig = () => {
|
||||
configManager.saveConfig(serverConfig.value)
|
||||
message.success(t('Server configuration saved'))
|
||||
}
|
||||
|
||||
// 清除配置
|
||||
const clearConfig = () => {
|
||||
configManager.clearConfig()
|
||||
message.success('Configuration cleared')
|
||||
loadCurrentConfig()
|
||||
}
|
||||
|
||||
// 页面加载时读取当前配置
|
||||
onMounted(() => {
|
||||
loadCurrentConfig()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container mx-auto px-4 py-8">
|
||||
<div class="max-w-2xl mx-auto">
|
||||
<div class="bg-white dark:bg-neutral-800 rounded-lg shadow-lg p-6">
|
||||
<h1 class="text-2xl font-bold text-gray-800 dark:text-white mb-6">
|
||||
{{ t('Server Configuration') }}
|
||||
</h1>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ t('Protocol') }}
|
||||
</label>
|
||||
<n-select v-model:value="serverConfig.protocol" :options="[
|
||||
{ label: 'HTTP', value: 'http' },
|
||||
{ label: 'HTTPS', value: 'https' }
|
||||
]" />
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ t('Port') }}
|
||||
</label>
|
||||
<n-input v-model:value="serverConfig.port" type="text" :placeholder="'9000'" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ t('Host') }}
|
||||
</label>
|
||||
<n-input v-model:value="serverConfig.host" type="text" :placeholder="'localhost'" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ t('Region') }}
|
||||
</label>
|
||||
<n-input v-model:value="serverConfig.region" type="text" :placeholder="'us-east-1'" />
|
||||
</div>
|
||||
|
||||
<div class="bg-gray-50 dark:bg-neutral-700 p-4 rounded-lg">
|
||||
<h3 class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||||
{{ t('Generated Configuration') }}
|
||||
</h3>
|
||||
<div class="text-sm text-gray-600 dark:text-gray-400 space-y-1">
|
||||
<div><strong>API URL:</strong> {{ serverConfig.protocol }}://{{ serverConfig.host }}:{{ serverConfig.port }}/rustfs/admin/v3</div>
|
||||
<div><strong>S3 Endpoint:</strong> {{ serverConfig.protocol }}://{{ serverConfig.host }}:{{ serverConfig.port }}</div>
|
||||
<div><strong>Region:</strong> {{ serverConfig.region }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-4 pt-4">
|
||||
<n-button type="primary" @click="saveConfig">
|
||||
{{ t('Save Configuration') }}
|
||||
</n-button>
|
||||
<n-button @click="clearConfig">
|
||||
{{ t('Clear Configuration') }}
|
||||
</n-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user