feat: add local fallback for S3 writes
This commit is contained in:
@@ -22,8 +22,10 @@ async function submit() {
|
||||
const resp = await login(email.value.trim(), password.value)
|
||||
auth.setAuth(resp.token, resp.user)
|
||||
|
||||
const redirect = typeof route.query.redirect === 'string' ? route.query.redirect : '/dashboard'
|
||||
await router.push(redirect)
|
||||
const defaultRoute = resp.user.role === 'admin' ? '/admin' : '/dashboard'
|
||||
const requestedRoute = typeof route.query.redirect === 'string' ? route.query.redirect : null
|
||||
const redirect = requestedRoute?.startsWith('/') && !requestedRoute.startsWith('//') ? requestedRoute : defaultRoute
|
||||
await router.replace(redirect)
|
||||
} catch (err) {
|
||||
if (err instanceof ApiError) {
|
||||
error.value = `[${err.code}] ${err.message}`
|
||||
|
||||
@@ -20,6 +20,8 @@ const loading = ref(true)
|
||||
const error = ref<string | null>(null)
|
||||
const message = ref<string | null>(null)
|
||||
const activeBackend = ref<'local' | 's3'>('local')
|
||||
const localObjectCount = ref(0)
|
||||
const localStoredBytes = ref(0)
|
||||
const endpoints = ref<AdminStorageEndpoint[]>([])
|
||||
const editingId = ref<string | null>(null)
|
||||
const formOpen = ref(false)
|
||||
@@ -59,6 +61,8 @@ async function loadEndpoints(clearError = true) {
|
||||
const response = await listStorageEndpoints(auth.token)
|
||||
endpoints.value = response.endpoints
|
||||
activeBackend.value = response.active_backend
|
||||
localObjectCount.value = response.local_object_count
|
||||
localStoredBytes.value = response.local_stored_bytes
|
||||
} catch (err) {
|
||||
error.value = errorText(err, '加载存储配置失败')
|
||||
} finally {
|
||||
@@ -207,7 +211,7 @@ onMounted(loadEndpoints)
|
||||
<p class="text-xs font-semibold uppercase tracking-[0.22em] text-cyan-300">Storage control plane</p>
|
||||
<h2 class="mt-3 text-2xl font-semibold">对象存储</h2>
|
||||
<p class="mt-2 max-w-2xl text-sm leading-6 text-slate-300">
|
||||
应用完成鉴权后返回短期签名地址,图片与批量 ZIP 的下载流量直接由高带宽 S3 节点承担。每个对象固定绑定写入端点,后续切换或扩容不会影响旧文件。
|
||||
新对象优先写入活动 S3;连接或写入失败时自动保存到应用服务器本地。每个对象固定记录实际写入后端,恢复 S3 后的新文件会自动重新走对象存储。
|
||||
</p>
|
||||
</div>
|
||||
<div class="rounded-xl border border-white/10 bg-white/5 p-4">
|
||||
@@ -217,12 +221,16 @@ onMounted(loadEndpoints)
|
||||
class="rounded-full px-3 py-1 text-xs font-semibold"
|
||||
:class="activeBackend === 's3' ? 'bg-emerald-400/15 text-emerald-300' : 'bg-amber-400/15 text-amber-200'"
|
||||
>
|
||||
{{ activeBackend === 's3' ? 'S3 已启用' : '本地回退' }}
|
||||
{{ activeBackend === 's3' ? 'S3 优先' : '仅本地' }}
|
||||
</span>
|
||||
</div>
|
||||
<p class="mt-3 text-xs leading-5 text-slate-400">
|
||||
推荐内部 Endpoint 走两台服务器之间的 WireGuard 地址;公网 Endpoint 使用带 TLS 的下载域名。签名默认 5 分钟有效。
|
||||
S3 故障只影响当次写入,下载与到期清理会按对象记录的真实后端执行。签名默认 5 分钟有效。
|
||||
</p>
|
||||
<div class="mt-3 flex items-center justify-between border-t border-white/10 pt-3 text-xs">
|
||||
<span class="text-slate-400">本地对象</span>
|
||||
<span class="font-medium text-slate-200">{{ localObjectCount }} 个 · {{ formatBytes(localStoredBytes) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -238,7 +246,7 @@ onMounted(loadEndpoints)
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h3 class="font-semibold text-slate-900">存储端点</h3>
|
||||
<p class="mt-1 text-sm text-slate-500">同一时间只有一个端点接收新对象,停用端点继续服务其历史对象。</p>
|
||||
<p class="mt-1 text-sm text-slate-500">同一时间只有一个首选 S3 端点;写入失败自动回退本地,停用端点继续服务其历史对象。</p>
|
||||
</div>
|
||||
<button
|
||||
class="rounded-lg bg-slate-900 px-4 py-2 text-sm font-medium text-white hover:bg-slate-800"
|
||||
|
||||
Reference in New Issue
Block a user