mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
fix(public): keep original GitHub links visible
(cherry picked from commit 7c93552697c9c35b77df35e117900ff8e9b62994)
This commit is contained in:
@@ -10,7 +10,7 @@ use crate::handlers::shared::{
|
|||||||
decrypt_catalog_secret_with_fallbacks, encrypt_catalog_secret_with_fallbacks,
|
decrypt_catalog_secret_with_fallbacks, encrypt_catalog_secret_with_fallbacks,
|
||||||
escape_admin_email_template_html, module_available_from_env, query_param_bool,
|
escape_admin_email_template_html, module_available_from_env, query_param_bool,
|
||||||
query_param_optional_bool, query_param_value, read_admin_email_template_payload,
|
query_param_optional_bool, query_param_value, read_admin_email_template_payload,
|
||||||
render_admin_email_template_html, system_config_bool, system_config_string,
|
render_admin_email_template_html, system_config_string,
|
||||||
unix_secs_to_rfc3339,
|
unix_secs_to_rfc3339,
|
||||||
};
|
};
|
||||||
use crate::{AppState, GatewayError};
|
use crate::{AppState, GatewayError};
|
||||||
@@ -239,17 +239,10 @@ pub(crate) async fn maybe_build_local_public_support_response(
|
|||||||
.flatten()
|
.flatten()
|
||||||
.and_then(|value| value.as_str().map(ToOwned::to_owned))
|
.and_then(|value| value.as_str().map(ToOwned::to_owned))
|
||||||
.unwrap_or_else(|| "AI Gateway".to_string());
|
.unwrap_or_else(|| "AI Gateway".to_string());
|
||||||
let show_github_link = state
|
|
||||||
.read_system_config_json_value("show_github_link")
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.flatten();
|
|
||||||
let show_github_link = system_config_bool(show_github_link.as_ref(), true);
|
|
||||||
return Some(
|
return Some(
|
||||||
Json(json!({
|
Json(json!({
|
||||||
"site_name": site_name,
|
"site_name": site_name,
|
||||||
"site_subtitle": site_subtitle,
|
"site_subtitle": site_subtitle,
|
||||||
"show_github_link": show_github_link,
|
|
||||||
}))
|
}))
|
||||||
.into_response(),
|
.into_response(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -2455,6 +2455,7 @@ async fn gateway_completes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
|||||||
.expect("account_state_recheck_error should be string when recheck is attempted");
|
.expect("account_state_recheck_error should be string when recheck is attempted");
|
||||||
assert!(
|
assert!(
|
||||||
account_state_recheck_error == "backend-api/me API 返回状态码 401"
|
account_state_recheck_error == "backend-api/me API 返回状态码 401"
|
||||||
|
|| account_state_recheck_error == "backend-api/me API 返回状态码 403"
|
||||||
|| account_state_recheck_error.starts_with("backend-api/me 请求执行失败:"),
|
|| account_state_recheck_error.starts_with("backend-api/me 请求执行失败:"),
|
||||||
"unexpected account_state_recheck_error: {account_state_recheck_error}"
|
"unexpected account_state_recheck_error: {account_state_recheck_error}"
|
||||||
);
|
);
|
||||||
@@ -4926,6 +4927,7 @@ async fn gateway_refreshes_admin_provider_oauth_key_locally_with_trusted_admin_p
|
|||||||
.expect("account_state_recheck_error should be string when attempted");
|
.expect("account_state_recheck_error should be string when attempted");
|
||||||
assert!(
|
assert!(
|
||||||
account_state_recheck_error == "backend-api/me API 返回状态码 401"
|
account_state_recheck_error == "backend-api/me API 返回状态码 401"
|
||||||
|
|| account_state_recheck_error == "backend-api/me API 返回状态码 403"
|
||||||
|| account_state_recheck_error.starts_with("backend-api/me 请求执行失败:"),
|
|| account_state_recheck_error.starts_with("backend-api/me 请求执行失败:"),
|
||||||
"unexpected account_state_recheck_error: {account_state_recheck_error}"
|
"unexpected account_state_recheck_error: {account_state_recheck_error}"
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -669,7 +669,6 @@ async fn gateway_handles_public_catalog_site_info_without_proxying_upstream() {
|
|||||||
vec![
|
vec![
|
||||||
("site_name".to_string(), json!("Aether Local")),
|
("site_name".to_string(), json!("Aether Local")),
|
||||||
("site_subtitle".to_string(), json!("Rust Only")),
|
("site_subtitle".to_string(), json!("Rust Only")),
|
||||||
("show_github_link".to_string(), json!(false)),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -686,7 +685,7 @@ async fn gateway_handles_public_catalog_site_info_without_proxying_upstream() {
|
|||||||
let payload: serde_json::Value = response.json().await.expect("json body should parse");
|
let payload: serde_json::Value = response.json().await.expect("json body should parse");
|
||||||
assert_eq!(payload["site_name"], "Aether Local");
|
assert_eq!(payload["site_name"], "Aether Local");
|
||||||
assert_eq!(payload["site_subtitle"], "Rust Only");
|
assert_eq!(payload["site_subtitle"], "Rust Only");
|
||||||
assert_eq!(payload["show_github_link"], false);
|
assert_eq!(payload.as_object().map(|object| object.len()), Some(2));
|
||||||
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
|
assert_eq!(*upstream_hits.lock().expect("mutex should lock"), 0);
|
||||||
|
|
||||||
gateway_handle.abort();
|
gateway_handle.abort();
|
||||||
|
|||||||
@@ -14,21 +14,19 @@ describe('useSiteInfo', () => {
|
|||||||
apiClientMocks.get.mockReset()
|
apiClientMocks.get.mockReset()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('loads github link display setting from public site info', async () => {
|
it('loads public site info', async () => {
|
||||||
apiClientMocks.get.mockResolvedValue({
|
apiClientMocks.get.mockResolvedValue({
|
||||||
data: {
|
data: {
|
||||||
site_name: 'Custom Aether',
|
site_name: 'Custom Aether',
|
||||||
site_subtitle: 'Gateway',
|
site_subtitle: 'Gateway',
|
||||||
show_github_link: false,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const { useSiteInfo } = await import('../useSiteInfo')
|
const { useSiteInfo } = await import('../useSiteInfo')
|
||||||
const { siteName, siteSubtitle, showGithubLink, refreshSiteInfo } = useSiteInfo()
|
const { siteName, siteSubtitle, refreshSiteInfo } = useSiteInfo()
|
||||||
await refreshSiteInfo()
|
await refreshSiteInfo()
|
||||||
|
|
||||||
expect(siteName.value).toBe('Custom Aether')
|
expect(siteName.value).toBe('Custom Aether')
|
||||||
expect(siteSubtitle.value).toBe('Gateway')
|
expect(siteSubtitle.value).toBe('Gateway')
|
||||||
expect(showGithubLink.value).toBe(false)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,13 +4,11 @@ import apiClient from '@/api/client'
|
|||||||
interface SiteInfo {
|
interface SiteInfo {
|
||||||
site_name: string
|
site_name: string
|
||||||
site_subtitle: string
|
site_subtitle: string
|
||||||
show_github_link?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 模块级缓存,所有组件共享同一份数据
|
// 模块级缓存,所有组件共享同一份数据
|
||||||
const siteName = ref('Aether')
|
const siteName = ref('Aether')
|
||||||
const siteSubtitle = ref('AI Gateway')
|
const siteSubtitle = ref('AI Gateway')
|
||||||
const showGithubLink = ref(true)
|
|
||||||
const loaded = ref(false)
|
const loaded = ref(false)
|
||||||
let fetchPromise: Promise<void> | null = null
|
let fetchPromise: Promise<void> | null = null
|
||||||
|
|
||||||
@@ -19,7 +17,6 @@ async function fetchSiteInfo() {
|
|||||||
const response = await apiClient.get<SiteInfo>('/api/public/site-info')
|
const response = await apiClient.get<SiteInfo>('/api/public/site-info')
|
||||||
siteName.value = response.data.site_name
|
siteName.value = response.data.site_name
|
||||||
siteSubtitle.value = response.data.site_subtitle
|
siteSubtitle.value = response.data.site_subtitle
|
||||||
showGithubLink.value = response.data.show_github_link !== false
|
|
||||||
loaded.value = true
|
loaded.value = true
|
||||||
} catch {
|
} catch {
|
||||||
// 加载失败时保持默认值,允许后续重试
|
// 加载失败时保持默认值,允许后续重试
|
||||||
@@ -38,7 +35,7 @@ export function useSiteInfo() {
|
|||||||
if (!loaded.value && !fetchPromise) {
|
if (!loaded.value && !fetchPromise) {
|
||||||
fetchPromise = fetchSiteInfo()
|
fetchPromise = fetchSiteInfo()
|
||||||
}
|
}
|
||||||
return { siteName, siteSubtitle, showGithubLink, refreshSiteInfo }
|
return { siteName, siteSubtitle, refreshSiteInfo }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 站点名称变化时同步更新 document.title
|
// 站点名称变化时同步更新 document.title
|
||||||
|
|||||||
@@ -325,7 +325,6 @@
|
|||||||
</button>
|
</button>
|
||||||
<!-- GitHub Link -->
|
<!-- GitHub Link -->
|
||||||
<a
|
<a
|
||||||
v-if="showGithubLink"
|
|
||||||
href="https://github.com/fawney19/Aether"
|
href="https://github.com/fawney19/Aether"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -413,7 +412,7 @@ const route = useRoute()
|
|||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const moduleStore = useModuleStore()
|
const moduleStore = useModuleStore()
|
||||||
const { themeMode, toggleDarkMode } = useDarkMode()
|
const { themeMode, toggleDarkMode } = useDarkMode()
|
||||||
const { siteName, siteSubtitle, showGithubLink } = useSiteInfo()
|
const { siteName, siteSubtitle } = useSiteInfo()
|
||||||
const isDemo = computed(() => isDemoMode())
|
const isDemo = computed(() => isDemoMode())
|
||||||
const isAdmin = computed(() => authStore.user?.role === 'admin')
|
const isAdmin = computed(() => authStore.user?.role === 'admin')
|
||||||
|
|
||||||
|
|||||||
@@ -14,13 +14,11 @@
|
|||||||
id="section-site-info"
|
id="section-site-info"
|
||||||
:site-name="systemConfig.site_name"
|
:site-name="systemConfig.site_name"
|
||||||
:site-subtitle="systemConfig.site_subtitle"
|
:site-subtitle="systemConfig.site_subtitle"
|
||||||
:show-github-link="systemConfig.show_github_link"
|
|
||||||
:loading="siteInfoLoading"
|
:loading="siteInfoLoading"
|
||||||
:has-changes="hasSiteInfoChanges"
|
:has-changes="hasSiteInfoChanges"
|
||||||
@save="saveSiteInfo"
|
@save="saveSiteInfo"
|
||||||
@update:site-name="systemConfig.site_name = $event"
|
@update:site-name="systemConfig.site_name = $event"
|
||||||
@update:site-subtitle="systemConfig.site_subtitle = $event"
|
@update:site-subtitle="systemConfig.site_subtitle = $event"
|
||||||
@update:show-github-link="systemConfig.show_github_link = $event"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 配置导出/导入 -->
|
<!-- 配置导出/导入 -->
|
||||||
|
|||||||
@@ -51,21 +51,6 @@
|
|||||||
显示在导航栏品牌名称下方
|
显示在导航栏品牌名称下方
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="md:col-span-2 flex items-center justify-between gap-4 rounded-lg border border-border/60 bg-muted/20 p-4">
|
|
||||||
<div>
|
|
||||||
<Label class="block text-sm font-medium">
|
|
||||||
GitHub 仓库入口
|
|
||||||
</Label>
|
|
||||||
<p class="mt-1 text-xs text-muted-foreground">
|
|
||||||
控制首页、指南页和控制台顶部的 GitHub 链接是否展示
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<Switch
|
|
||||||
:model-value="showGithubLink"
|
|
||||||
:disabled="loading"
|
|
||||||
@update:model-value="$emit('update:showGithubLink', $event)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</CardSection>
|
</CardSection>
|
||||||
</template>
|
</template>
|
||||||
@@ -74,13 +59,11 @@
|
|||||||
import Button from '@/components/ui/button.vue'
|
import Button from '@/components/ui/button.vue'
|
||||||
import Input from '@/components/ui/input.vue'
|
import Input from '@/components/ui/input.vue'
|
||||||
import Label from '@/components/ui/label.vue'
|
import Label from '@/components/ui/label.vue'
|
||||||
import Switch from '@/components/ui/switch.vue'
|
|
||||||
import { CardSection } from '@/components/layout'
|
import { CardSection } from '@/components/layout'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
siteName: string
|
siteName: string
|
||||||
siteSubtitle: string
|
siteSubtitle: string
|
||||||
showGithubLink: boolean
|
|
||||||
loading: boolean
|
loading: boolean
|
||||||
hasChanges: boolean
|
hasChanges: boolean
|
||||||
}>()
|
}>()
|
||||||
@@ -89,6 +72,5 @@ defineEmits<{
|
|||||||
save: []
|
save: []
|
||||||
'update:siteName': [value: string]
|
'update:siteName': [value: string]
|
||||||
'update:siteSubtitle': [value: string]
|
'update:siteSubtitle': [value: string]
|
||||||
'update:showGithubLink': [value: boolean]
|
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -35,23 +35,21 @@ vi.mock('@/components/ui/button.vue', () => ({
|
|||||||
|
|
||||||
const mountedApps: Array<{ app: App, root: HTMLElement }> = []
|
const mountedApps: Array<{ app: App, root: HTMLElement }> = []
|
||||||
|
|
||||||
function mountSection(onUpdateShowGithubLink = vi.fn()) {
|
function mountSection() {
|
||||||
const root = document.createElement('div')
|
const root = document.createElement('div')
|
||||||
document.body.appendChild(root)
|
document.body.appendChild(root)
|
||||||
const app = createApp(SiteInfoSection, {
|
const app = createApp(SiteInfoSection, {
|
||||||
siteName: 'Aether',
|
siteName: 'Aether',
|
||||||
siteSubtitle: 'AI Gateway',
|
siteSubtitle: 'AI Gateway',
|
||||||
showGithubLink: false,
|
|
||||||
loading: false,
|
loading: false,
|
||||||
hasChanges: true,
|
hasChanges: true,
|
||||||
onSave: vi.fn(),
|
onSave: vi.fn(),
|
||||||
'onUpdate:siteName': vi.fn(),
|
'onUpdate:siteName': vi.fn(),
|
||||||
'onUpdate:siteSubtitle': vi.fn(),
|
'onUpdate:siteSubtitle': vi.fn(),
|
||||||
'onUpdate:showGithubLink': onUpdateShowGithubLink,
|
|
||||||
})
|
})
|
||||||
app.mount(root)
|
app.mount(root)
|
||||||
mountedApps.push({ app, root })
|
mountedApps.push({ app, root })
|
||||||
return { root, onUpdateShowGithubLink }
|
return { root }
|
||||||
}
|
}
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -63,15 +61,11 @@ afterEach(() => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('SiteInfoSection', () => {
|
describe('SiteInfoSection', () => {
|
||||||
it('renders and emits the github link display switch', async () => {
|
it('renders site name and subtitle fields', async () => {
|
||||||
const { root, onUpdateShowGithubLink } = mountSection()
|
const { root } = mountSection()
|
||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
expect(root.textContent).toContain('GitHub 仓库入口')
|
expect(root.textContent).toContain('站点名称')
|
||||||
const switchButton = root.querySelector('[role="switch"]') as HTMLButtonElement | null
|
expect(root.textContent).toContain('站点副标题')
|
||||||
expect(switchButton?.getAttribute('aria-checked')).toBe('false')
|
|
||||||
|
|
||||||
switchButton?.click()
|
|
||||||
expect(onUpdateShowGithubLink).toHaveBeenCalledWith(true)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export interface SystemConfig {
|
|||||||
// 站点信息
|
// 站点信息
|
||||||
site_name: string
|
site_name: string
|
||||||
site_subtitle: string
|
site_subtitle: string
|
||||||
show_github_link: boolean
|
|
||||||
// 网络代理
|
// 网络代理
|
||||||
system_proxy_node_id: string | null
|
system_proxy_node_id: string | null
|
||||||
// 基础配置
|
// 基础配置
|
||||||
@@ -55,7 +54,6 @@ const CONFIG_KEYS = [
|
|||||||
// 站点信息
|
// 站点信息
|
||||||
'site_name',
|
'site_name',
|
||||||
'site_subtitle',
|
'site_subtitle',
|
||||||
'show_github_link',
|
|
||||||
// 网络代理
|
// 网络代理
|
||||||
'system_proxy_node_id',
|
'system_proxy_node_id',
|
||||||
// 基础配置
|
// 基础配置
|
||||||
@@ -102,7 +100,6 @@ function createDefaultConfig(): SystemConfig {
|
|||||||
// 站点信息
|
// 站点信息
|
||||||
site_name: 'Aether',
|
site_name: 'Aether',
|
||||||
site_subtitle: 'AI Gateway',
|
site_subtitle: 'AI Gateway',
|
||||||
show_github_link: true,
|
|
||||||
// 网络代理
|
// 网络代理
|
||||||
system_proxy_node_id: null,
|
system_proxy_node_id: null,
|
||||||
// 基础配置
|
// 基础配置
|
||||||
@@ -166,8 +163,7 @@ export function useSystemConfig() {
|
|||||||
if (!originalConfig.value) return false
|
if (!originalConfig.value) return false
|
||||||
return (
|
return (
|
||||||
systemConfig.value.site_name !== originalConfig.value.site_name ||
|
systemConfig.value.site_name !== originalConfig.value.site_name ||
|
||||||
systemConfig.value.site_subtitle !== originalConfig.value.site_subtitle ||
|
systemConfig.value.site_subtitle !== originalConfig.value.site_subtitle
|
||||||
systemConfig.value.show_github_link !== originalConfig.value.show_github_link
|
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -311,11 +307,6 @@ export function useSystemConfig() {
|
|||||||
value: systemConfig.value.site_subtitle,
|
value: systemConfig.value.site_subtitle,
|
||||||
description: '站点副标题',
|
description: '站点副标题',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: 'show_github_link',
|
|
||||||
value: systemConfig.value.show_github_link,
|
|
||||||
description: '是否显示 GitHub 仓库入口',
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
configItems.map((item) =>
|
configItems.map((item) =>
|
||||||
@@ -325,7 +316,6 @@ export function useSystemConfig() {
|
|||||||
if (originalConfig.value) {
|
if (originalConfig.value) {
|
||||||
originalConfig.value.site_name = systemConfig.value.site_name
|
originalConfig.value.site_name = systemConfig.value.site_name
|
||||||
originalConfig.value.site_subtitle = systemConfig.value.site_subtitle
|
originalConfig.value.site_subtitle = systemConfig.value.site_subtitle
|
||||||
originalConfig.value.show_github_link = systemConfig.value.show_github_link
|
|
||||||
}
|
}
|
||||||
await refreshSiteInfo()
|
await refreshSiteInfo()
|
||||||
success('站点信息已保存')
|
success('站点信息已保存')
|
||||||
|
|||||||
@@ -75,7 +75,6 @@
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
v-if="showGithubLink"
|
|
||||||
href="https://github.com/fawney19/Aether"
|
href="https://github.com/fawney19/Aether"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -90,10 +89,7 @@
|
|||||||
<!-- Desktop layout (>= md): Centered nav with balanced spacing -->
|
<!-- Desktop layout (>= md): Centered nav with balanced spacing -->
|
||||||
<div class="h-16 hidden md:flex items-center justify-between px-8">
|
<div class="h-16 hidden md:flex items-center justify-between px-8">
|
||||||
<!-- Left spacer for balance (matches right icons width) -->
|
<!-- Left spacer for balance (matches right icons width) -->
|
||||||
<div
|
<div class="w-[76px] shrink-0" />
|
||||||
class="shrink-0"
|
|
||||||
:class="showGithubLink ? 'w-[76px]' : 'w-9'"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Center: Logo + Nav + Login Button -->
|
<!-- Center: Logo + Nav + Login Button -->
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
@@ -190,7 +186,6 @@
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
v-if="showGithubLink"
|
|
||||||
href="https://github.com/fawney19/Aether"
|
href="https://github.com/fawney19/Aether"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -500,7 +495,7 @@ import {
|
|||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
const { isDark, themeMode, toggleDarkMode } = useDarkMode()
|
const { isDark, themeMode, toggleDarkMode } = useDarkMode()
|
||||||
const { copyToClipboard } = useClipboard()
|
const { copyToClipboard } = useClipboard()
|
||||||
const { siteName, siteSubtitle, showGithubLink } = useSiteInfo()
|
const { siteName, siteSubtitle } = useSiteInfo()
|
||||||
|
|
||||||
const dashboardPath = computed(() =>
|
const dashboardPath = computed(() =>
|
||||||
authStore.canAccessAdmin ? '/admin/dashboard' : '/dashboard'
|
authStore.canAccessAdmin ? '/admin/dashboard' : '/dashboard'
|
||||||
|
|||||||
@@ -261,7 +261,6 @@
|
|||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
<a
|
<a
|
||||||
v-if="showGithubLink"
|
|
||||||
href="https://github.com/fawney19/Aether"
|
href="https://github.com/fawney19/Aether"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
@@ -341,7 +340,7 @@ import { guideNavItems } from './guide-config'
|
|||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { themeMode, toggleDarkMode } = useDarkMode()
|
const { themeMode, toggleDarkMode } = useDarkMode()
|
||||||
const { siteName, siteSubtitle, showGithubLink } = useSiteInfo()
|
const { siteName, siteSubtitle } = useSiteInfo()
|
||||||
|
|
||||||
const mobileMenuOpen = ref(false)
|
const mobileMenuOpen = ref(false)
|
||||||
const baseUrl = ref(typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com')
|
const baseUrl = ref(typeof window !== 'undefined' ? window.location.origin : 'https://your-aether.com')
|
||||||
|
|||||||
@@ -12,12 +12,10 @@ import {
|
|||||||
Zap,
|
Zap,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { panelClasses } from './guide-config'
|
import { panelClasses } from './guide-config'
|
||||||
import { useSiteInfo } from '@/composables/useSiteInfo'
|
|
||||||
|
|
||||||
// 部署步骤数据
|
// 部署步骤数据
|
||||||
const activeDeployTab = ref(0)
|
const activeDeployTab = ref(0)
|
||||||
const copiedStep = ref<string | null>(null)
|
const copiedStep = ref<string | null>(null)
|
||||||
const { showGithubLink } = useSiteInfo()
|
|
||||||
|
|
||||||
const productionSteps = [
|
const productionSteps = [
|
||||||
{
|
{
|
||||||
@@ -399,7 +397,6 @@ function copyStep(stepId: string, code: string) {
|
|||||||
<h3>1. Aether-Proxy</h3>
|
<h3>1. Aether-Proxy</h3>
|
||||||
<p>Rust实现, 超小资源占有, 适合性能低的VPS直接使用。</p>
|
<p>Rust实现, 超小资源占有, 适合性能低的VPS直接使用。</p>
|
||||||
<a
|
<a
|
||||||
v-if="showGithubLink"
|
|
||||||
href="https://github.com/fawney19/Aether/tree/main/aether-proxy"
|
href="https://github.com/fawney19/Aether/tree/main/aether-proxy"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
|
|||||||
@@ -790,6 +790,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, computed, onBeforeUnmount, nextTick, watch, markRaw } from 'vue'
|
import { ref, onMounted, computed, onBeforeUnmount, nextTick, watch, markRaw } from 'vue'
|
||||||
|
import type { Component } from 'vue'
|
||||||
import { useAuthStore } from '@/stores/auth'
|
import { useAuthStore } from '@/stores/auth'
|
||||||
import { dashboardApi, type DashboardStat, type DailyStat, type ProviderSummary } from '@/api/dashboard'
|
import { dashboardApi, type DashboardStat, type DailyStat, type ProviderSummary } from '@/api/dashboard'
|
||||||
import { getDateRangeFromPeriod } from '@/features/usage/composables'
|
import { getDateRangeFromPeriod } from '@/features/usage/composables'
|
||||||
@@ -838,6 +839,10 @@ import type { ChartData, ChartOptions, ChartDataset, TooltipItem } from 'chart.j
|
|||||||
|
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
|
type DashboardStatCard = Omit<DashboardStat, 'icon'> & {
|
||||||
|
icon: Component
|
||||||
|
}
|
||||||
|
|
||||||
const statsPanelRef = ref<HTMLElement | null>(null)
|
const statsPanelRef = ref<HTMLElement | null>(null)
|
||||||
const announcementsHeight = ref<number | null>(null)
|
const announcementsHeight = ref<number | null>(null)
|
||||||
const announcementsTimelineRef = ref<HTMLElement | null>(null)
|
const announcementsTimelineRef = ref<HTMLElement | null>(null)
|
||||||
@@ -941,7 +946,7 @@ const getStatIconColor = (_index: number): string => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 统计数据
|
// 统计数据
|
||||||
const stats = ref<DashboardStat[]>([])
|
const stats = ref<DashboardStatCard[]>([])
|
||||||
const todayStats = ref<{
|
const todayStats = ref<{
|
||||||
requests: number
|
requests: number
|
||||||
tokens: number
|
tokens: number
|
||||||
@@ -1006,7 +1011,7 @@ const loadingAnnouncements = ref(false)
|
|||||||
const selectedAnnouncement = ref<Announcement | null>(null)
|
const selectedAnnouncement = ref<Announcement | null>(null)
|
||||||
const detailDialogOpen = ref(false)
|
const detailDialogOpen = ref(false)
|
||||||
|
|
||||||
const iconMap: Record<string, unknown> = {
|
const iconMap: Record<string, Component> = {
|
||||||
Users, Activity, TrendingUp, DollarSign, Key, Hash, Zap, Database
|
Users, Activity, TrendingUp, DollarSign, Key, Hash, Zap, Database
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user