mirror of
https://github.com/rustfs/console.git
synced 2026-10-29 18:44:56 +08:00
feat: custom server config
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 696 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 707 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 689 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" viewBox="0 0 1422 800"><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="oooscillate-grad"><stop stop-color="hsl(206, 75%, 49%)" stop-opacity="1" offset="0%"></stop><stop stop-color="hsl(331, 90%, 56%)" stop-opacity="1" offset="100%"></stop></linearGradient></defs><g stroke-width="6" stroke="hsl(205, 69%, 50%)" fill="none" stroke-linecap="round"><path d="M 0 630 Q 355.5 -100 711 400 Q 1066.5 900 1422 630" opacity="0.56"></path><path d="M 0 588 Q 355.5 -100 711 400 Q 1066.5 900 1422 588" opacity="0.38"></path><path d="M 0 546 Q 355.5 -100 711 400 Q 1066.5 900 1422 546" opacity="0.42"></path><path d="M 0 504 Q 355.5 -100 711 400 Q 1066.5 900 1422 504" opacity="0.84"></path><path d="M 0 462 Q 355.5 -100 711 400 Q 1066.5 900 1422 462" opacity="0.35"></path><path d="M 0 420 Q 355.5 -100 711 400 Q 1066.5 900 1422 420" opacity="0.84"></path><path d="M 0 378 Q 355.5 -100 711 400 Q 1066.5 900 1422 378" opacity="0.98"></path><path d="M 0 336 Q 355.5 -100 711 400 Q 1066.5 900 1422 336" opacity="0.53"></path><path d="M 0 294 Q 355.5 -100 711 400 Q 1066.5 900 1422 294" opacity="0.64"></path><path d="M 0 252 Q 355.5 -100 711 400 Q 1066.5 900 1422 252" opacity="0.40"></path><path d="M 0 210 Q 355.5 -100 711 400 Q 1066.5 900 1422 210" opacity="0.78"></path><path d="M 0 168 Q 355.5 -100 711 400 Q 1066.5 900 1422 168" opacity="0.19"></path><path d="M 0 126 Q 355.5 -100 711 400 Q 1066.5 900 1422 126" opacity="0.55"></path><path d="M 0 84 Q 355.5 -100 711 400 Q 1066.5 900 1422 84" opacity="0.26"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 13 KiB |
Vendored
+1
@@ -40,6 +40,7 @@ declare module 'vue' {
|
||||
NGrid: typeof import('naive-ui')['NGrid']
|
||||
NH3: typeof import('naive-ui')['NH3']
|
||||
NH4: typeof import('naive-ui')['NH4']
|
||||
NIcon: typeof import('naive-ui')['NIcon']
|
||||
NInput: typeof import('naive-ui')['NInput']
|
||||
NInputGroup: typeof import('naive-ui')['NInputGroup']
|
||||
NInputGroupLabel: typeof import('naive-ui')['NInputGroupLabel']
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<div class="w-full bg-gray-50 dark:bg-black h-full p-16 flex flex-col justify-center gap-8 overflow-hidden relative border-r dark:border-none">
|
||||
<div class="max-w-7xl flex flex-col z-10">
|
||||
<img src="~/assets/logo.svg" class="max-w-28" alt="" />
|
||||
<div class="text-4xl my-6 font-semibold !text-primary px-0">
|
||||
<span :class="{ 'pr-1': locale !== 'zh' }">{{ t('Rust-based') }} </span>
|
||||
<FlipWords :words="[t('High Performance'), t('Infinite Scaling'), t('Secure & Reliable'), t('Multi-Cloud Storage'), t('S3 Compatible')]" :duration="3000"
|
||||
class="text-4xl font-semibold !text-primary px-0" />
|
||||
<div class="text-muted-foreground mt-2">
|
||||
{{ t('Reliable distributed file system') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a href="https://www.rustfs.com" class="z-10 text-primary-500 inline-flex w-max items-center gap-2 leading-none p-2 px-5 border rounded-full border-blue-500">
|
||||
<span>{{ t('Visit website') }}</span>
|
||||
<Icon name="ri:arrow-right-long-fill" class="mr-2" />
|
||||
</a>
|
||||
<div class="h-full inset-0 absolute z-0">
|
||||
<img src="~/assets/backgrounds/ttten.svg" class="absolute h-full w-full inset-0 z-0 opacity-45 object-cover" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import FlipWords from '~/components/ui/flip-words/FlipWords.vue'
|
||||
|
||||
const { t, locale } = useI18n()
|
||||
</script>
|
||||
@@ -73,6 +73,11 @@ export default [
|
||||
key: "divider-2",
|
||||
type: "divider",
|
||||
},
|
||||
{
|
||||
label: "Settings",
|
||||
to: "/settings",
|
||||
icon: "ri:settings-3-line",
|
||||
},
|
||||
{
|
||||
label: "License",
|
||||
// to: 'https://github.com/rustfs/rustfs?tab=Apache-2.0-1-ov-file#readme',
|
||||
|
||||
+18
-1
@@ -133,6 +133,20 @@
|
||||
"Welcome Back": "Welcome Back",
|
||||
"Login Problems": "Login Problems",
|
||||
"Bucket Not Empty": "Bucket Not Empty",
|
||||
"Server Configuration": "Server Configuration",
|
||||
"Server URL": "Server URL",
|
||||
"Enter complete server URL (e.g., http://localhost:9000)": "Enter complete server URL (e.g., http://localhost:9000)",
|
||||
"Server URL is required": "Server URL is required",
|
||||
"Only HTTP and HTTPS protocols are supported": "Only HTTP and HTTPS protocols are supported",
|
||||
"Invalid hostname": "Invalid hostname",
|
||||
"Invalid URL format": "Invalid URL format",
|
||||
"Protocol": "Protocol",
|
||||
"Host": "Host",
|
||||
"Port": "Port",
|
||||
"Server configuration saved": "Server configuration saved",
|
||||
"Save Configuration": "Save Configuration",
|
||||
"Clear Configuration": "Clear Configuration",
|
||||
"Generated Configuration": "Generated Configuration",
|
||||
"Upload File": "Upload File",
|
||||
"Close": "Close",
|
||||
"Select File": "Select File",
|
||||
@@ -343,5 +357,8 @@
|
||||
"Days After": "Days After",
|
||||
"Expire": "Expire",
|
||||
"On": "On",
|
||||
"Off": "Off"
|
||||
"Off": "Off",
|
||||
"No valid server configuration detected, please check server configuration": "No valid server configuration detected, please check server configuration",
|
||||
"Login failed, please check server configuration": "Login failed, please check server configuration",
|
||||
"Click to modify": "Click to modify"
|
||||
}
|
||||
|
||||
+18
-1
@@ -154,6 +154,20 @@
|
||||
"Login Failed": "登录失败",
|
||||
"Login Problems": "登录遇到问题?",
|
||||
"Get Help": "获取帮助",
|
||||
"Server Configuration": "服务器配置",
|
||||
"Server URL": "服务器地址",
|
||||
"Enter complete server URL (e.g., http://localhost:9000)": "请输入完整的服务器地址(例如:http://localhost:9000)",
|
||||
"Server URL is required": "服务器地址不能为空",
|
||||
"Only HTTP and HTTPS protocols are supported": "仅支持 HTTP 和 HTTPS 协议",
|
||||
"Invalid hostname": "无效的主机名",
|
||||
"Invalid URL format": "无效的URL格式",
|
||||
"Protocol": "协议",
|
||||
"Host": "主机",
|
||||
"Port": "端口",
|
||||
"Server configuration saved": "服务器配置已保存",
|
||||
"Save Configuration": "保存配置",
|
||||
"Clear Configuration": "清除配置",
|
||||
"Generated Configuration": "生成的配置",
|
||||
"Create Bucket": "创建存储桶",
|
||||
"Creation Date": "创建时间",
|
||||
"Bucket Not Empty": "当前桶不为空,请先删除桶内容",
|
||||
@@ -534,5 +548,8 @@
|
||||
"Please enter valid days": "请输入天数",
|
||||
"Expire": "过期",
|
||||
"On": "开启",
|
||||
"Off": "关闭"
|
||||
"Off": "关闭",
|
||||
"No valid server configuration detected, please check server configuration": "未检测到有效服务器配置,请检查服务器配置",
|
||||
"Login failed, please check server configuration": "登录失败,请检查服务器配置",
|
||||
"Click to modify": "点击修改"
|
||||
}
|
||||
|
||||
+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>
|
||||
+50
-13
@@ -1,29 +1,66 @@
|
||||
import { configManager } from '~/utils/config'
|
||||
|
||||
export default defineNuxtPlugin({
|
||||
name: 'load-site-config',
|
||||
async setup(nuxtApp) {
|
||||
let finalConfig: any
|
||||
|
||||
// 客户端:尝试从 public/config.json 动态加载配置
|
||||
// 优先级:localStorage > public/config.json > runtimeConfig
|
||||
try {
|
||||
const response = await fetch('/config.json')
|
||||
const remoteConfig = await response.json()
|
||||
// 获取license
|
||||
// 判断是否是开发环境
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
const licenseResponse = await fetch('/license')
|
||||
const licenseConfig = await licenseResponse.json()
|
||||
remoteConfig.license = licenseConfig
|
||||
// 1. 首先尝试从 localStorage 读取配置
|
||||
const localStorageConfig = configManager.loadConfig()
|
||||
if (localStorageConfig) {
|
||||
finalConfig = { ...useRuntimeConfig().public, ...localStorageConfig }
|
||||
console.log('Configuration loaded from localStorage')
|
||||
}
|
||||
|
||||
// 2. 如果 localStorage 没有配置,尝试从 public/config.json 加载
|
||||
if (!finalConfig) {
|
||||
const response = await fetch('/config.json')
|
||||
const remoteConfig = await response.json()
|
||||
|
||||
// 获取license
|
||||
// 判断是否是开发环境
|
||||
if (process.env.NODE_ENV !== 'development') {
|
||||
const licenseResponse = await fetch('/license')
|
||||
const licenseConfig = await licenseResponse.json()
|
||||
remoteConfig.license = licenseConfig
|
||||
}
|
||||
|
||||
// 合并 public/runtimeConfig 与 remote 配置,remote 的优先
|
||||
finalConfig = { ...useRuntimeConfig().public, ...remoteConfig }
|
||||
console.log('Configuration loaded from config.json')
|
||||
}
|
||||
|
||||
// 3. 如果都没有,使用 runtimeConfig
|
||||
if (!finalConfig) {
|
||||
finalConfig = useRuntimeConfig().public
|
||||
console.log('Configuration loaded from runtimeConfig')
|
||||
}
|
||||
|
||||
|
||||
// 合并 public/runtimeConfig 与 remote 配置,remote 的优先
|
||||
finalConfig = { ...useRuntimeConfig().public, ...remoteConfig }
|
||||
} catch (error) {
|
||||
console.error('Failed to load configuration:', error)
|
||||
|
||||
// 如果配置加载失败,重定向到登录页而不是抛出错误
|
||||
if (process.client) {
|
||||
const currentRoute = useRoute()
|
||||
if (currentRoute.path !== '/auth/login') {
|
||||
await navigateTo('/auth/login')
|
||||
}
|
||||
}
|
||||
|
||||
// 使用默认配置,避免应用崩溃
|
||||
finalConfig = useRuntimeConfig().public
|
||||
}
|
||||
|
||||
if (!finalConfig.api.baseURL) {
|
||||
throw new Error('API URL is not defined in the configuration.')
|
||||
// 如果仍然没有 baseURL,重定向到登录页
|
||||
if (process.client) {
|
||||
const currentRoute = useRoute()
|
||||
if (currentRoute.path !== '/auth/login') {
|
||||
await navigateTo('/auth/login?showConfig=1')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
export interface ServerConfig {
|
||||
protocol: string
|
||||
host: string
|
||||
port: string
|
||||
region: string
|
||||
}
|
||||
|
||||
export interface RustFSConfig {
|
||||
api: {
|
||||
baseURL: string
|
||||
}
|
||||
s3: {
|
||||
endpoint: string
|
||||
region: string
|
||||
}
|
||||
}
|
||||
|
||||
const CONFIG_KEY = 'rustfs-config'
|
||||
|
||||
export const configManager = {
|
||||
// 保存配置到 localStorage
|
||||
saveConfig(serverConfig: ServerConfig): void {
|
||||
if (process.client) {
|
||||
const config: RustFSConfig = {
|
||||
api: {
|
||||
baseURL: `${serverConfig.protocol}://${serverConfig.host}:${serverConfig.port}/rustfs/admin/v3`
|
||||
},
|
||||
s3: {
|
||||
endpoint: `${serverConfig.protocol}://${serverConfig.host}:${serverConfig.port}`,
|
||||
region: serverConfig.region
|
||||
}
|
||||
}
|
||||
|
||||
localStorage.setItem(CONFIG_KEY, JSON.stringify(config))
|
||||
}
|
||||
},
|
||||
|
||||
// 从 localStorage 读取配置
|
||||
loadConfig(): RustFSConfig | null {
|
||||
if (process.client) {
|
||||
try {
|
||||
const configStr = localStorage.getItem(CONFIG_KEY)
|
||||
if (configStr) {
|
||||
return JSON.parse(configStr)
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse localStorage config:', error)
|
||||
}
|
||||
}
|
||||
return null
|
||||
},
|
||||
|
||||
// 清除配置
|
||||
clearConfig(): void {
|
||||
if (process.client) {
|
||||
localStorage.removeItem(CONFIG_KEY)
|
||||
}
|
||||
},
|
||||
|
||||
// 检查是否有有效配置
|
||||
hasValidConfig(): boolean {
|
||||
const config = this.loadConfig()
|
||||
return !!(config?.api?.baseURL)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user