chore: 初始提交 Fonu 1.0.2

- 公网反代:Nginx 状态卡片(版本 / 运行状态 / 配置校验 / 端口监听)
- 部署设置第一、二梯队(HTTPS 开关、安全头、限流、连接数、仅中国大陆 IP)
- 隧道检测兼容 frps v0.68 / v0.71(V2 与旧版端点)
- 恢复被清空的 internal/frp/store.go、internal/api/ddns_handler.go、web/src/views/DdnsView.vue
This commit is contained in:
2026-09-23 09:40:59 +08:00
commit 278f8267b7
333 changed files with 53562 additions and 0 deletions
+136
View File
@@ -0,0 +1,136 @@
import { computed, ref, watch } from 'vue'
import { darkTheme, type GlobalThemeOverrides } from 'naive-ui'
const themeKey = 'fonu-theme'
const themeMode = ref(localStorage.getItem(themeKey) ?? 'system')
function systemDark() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
const shared = {
primaryColor: '#10B981',
primaryColorHover: '#059669',
primaryColorPressed: '#047857',
primaryColorSuppl: '#34D399',
borderRadius: '8px',
fontFamily: 'var(--fonu-font)',
}
const lightOverrides: GlobalThemeOverrides = {
common: {
...shared,
bodyColor: '#F8FAFC',
cardColor: '#FFFFFF',
modalColor: '#FFFFFF',
popoverColor: '#FFFFFF',
borderColor: '#E5E7EB',
dividerColor: '#E5E7EB',
textColor1: '#111827',
textColor2: '#6B7280',
textColor3: '#9CA3AF',
hoverColor: 'rgba(16, 185, 129, 0.08)',
},
Card: { borderRadius: '12px', color: '#FFFFFF' },
Button: { borderRadiusMedium: '8px', heightMedium: '36px' },
Input: { borderRadius: '8px' },
DataTable: { borderRadius: '12px', thColor: '#F9FAFB', tdColor: '#FFFFFF' },
Tabs: { tabGapMediumLine: '32px', tabPaddingMediumLine: '12px 4px' },
Layout: { color: '#FFFFFF', siderColor: '#FFFFFF' },
Menu: {
itemColorActive: 'rgba(16, 185, 129, 0.12)',
itemTextColorActive: '#047857',
itemIconColorActive: '#10B981',
itemColorActiveHover: 'rgba(16, 185, 129, 0.16)',
},
Radio: {
buttonBorderColorActive: '#10B981',
buttonTextColorActive: '#047857',
buttonColorActive: 'rgba(16, 185, 129, 0.12)',
buttonBoxShadowHover: 'none',
buttonBoxShadowFocus: 'none',
},
}
const darkOverrides: GlobalThemeOverrides = {
common: {
...shared,
bodyColor: '#0F172A',
cardColor: '#1E293B',
modalColor: '#1E293B',
popoverColor: '#1E293B',
borderColor: '#334155',
dividerColor: '#334155',
textColor1: '#F1F5F9',
textColor2: '#94A3B8',
textColor3: '#64748B',
hoverColor: 'rgba(16, 185, 129, 0.12)',
},
Card: { borderRadius: '12px', color: '#1E293B', borderColor: '#334155' },
Button: { borderRadiusMedium: '8px', heightMedium: '36px' },
Input: { borderRadius: '8px', color: '#0F172A' },
DataTable: { borderRadius: '12px', thColor: '#0F172A', tdColor: '#1E293B', borderColor: '#334155' },
Tabs: { tabGapMediumLine: '32px', tabPaddingMediumLine: '12px 4px' },
Layout: { color: '#0F172A', siderColor: '#1E293B' },
Menu: {
itemColorActive: 'rgba(16, 185, 129, 0.18)',
itemTextColorActive: '#6EE7B7',
itemIconColorActive: '#34D399',
itemColorActiveHover: 'rgba(16, 185, 129, 0.24)',
},
Radio: {
buttonBorderColorActive: '#34D399',
buttonTextColorActive: '#6EE7B7',
buttonColorActive: 'rgba(16, 185, 129, 0.22)',
buttonBoxShadowHover: 'none',
buttonBoxShadowFocus: 'none',
},
}
function resolveDark(mode: string) {
if (mode === 'dark') return true
if (mode === 'light') return false
return systemDark()
}
function applyDocumentTheme(dark: boolean) {
document.documentElement.style.colorScheme = dark ? 'dark' : 'light'
document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light')
document.documentElement.classList.toggle('dark', dark)
}
export function useTheme() {
const isDark = computed(() => resolveDark(themeMode.value))
const naiveTheme = computed(() => (isDark.value ? darkTheme : null))
const themeOverrides = computed<GlobalThemeOverrides>(() =>
isDark.value ? darkOverrides : lightOverrides,
)
function setThemeMode(mode: string) {
themeMode.value = mode
}
function toggleTheme() {
themeMode.value = isDark.value ? 'light' : 'dark'
}
watch(
themeMode,
(value) => {
localStorage.setItem(themeKey, value)
applyDocumentTheme(resolveDark(value))
},
{ immediate: true },
)
if (typeof window !== 'undefined') {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
if (themeMode.value === 'system') {
applyDocumentTheme(systemDark())
}
})
}
return { themeMode, naiveTheme, themeOverrides, isDark, toggleTheme, setThemeMode }
}