diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts index 287164c..bd20cb6 100644 --- a/app/api/auth/route.ts +++ b/app/api/auth/route.ts @@ -65,18 +65,25 @@ async function generateProfileId(password: string): Promise { return hashArray.slice(0, 8).map(b => b.toString(16).padStart(2, '0')).join(''); } -export async function GET() { - const hasAuth = !!(effectiveAdminPassword || ACCOUNTS); +function getPublicAuthConfig() { const runtimeFeatures = getRuntimeFeatures(); - return NextResponse.json({ - hasAuth, - hasPremiumAuth: !!PREMIUM_PASSWORD, + return { persistSession: PERSIST_SESSION, subscriptionSources: SUBSCRIPTION_SOURCES, iptvSources: runtimeFeatures.iptvEnabled ? IPTV_SOURCES : '', mergeSources: MERGE_SOURCES, danmakuApiUrl: DANMAKU_API_URL, + }; +} + +export async function GET() { + const hasAuth = !!(effectiveAdminPassword || ACCOUNTS); + + return NextResponse.json({ + hasAuth, + hasPremiumAuth: !!PREMIUM_PASSWORD, + ...getPublicAuthConfig(), }); } @@ -119,7 +126,7 @@ export async function POST(request: NextRequest) { name: '管理员', role: 'super_admin', profileId, - persistSession: PERSIST_SESSION, + ...getPublicAuthConfig(), }); } @@ -133,7 +140,7 @@ export async function POST(request: NextRequest) { name: account.name, role: account.role, profileId, - persistSession: PERSIST_SESSION, + ...getPublicAuthConfig(), customPermissions: account.customPermissions.length > 0 ? account.customPermissions : undefined, }); } diff --git a/app/premium/settings/hooks/usePremiumSettingsPage.ts b/app/premium/settings/hooks/usePremiumSettingsPage.ts index 294f039..656841e 100644 --- a/app/premium/settings/hooks/usePremiumSettingsPage.ts +++ b/app/premium/settings/hooks/usePremiumSettingsPage.ts @@ -36,26 +36,34 @@ export function usePremiumSettingsPage() { const [blockedCategories, setBlockedCategories] = useState([]); useEffect(() => { - // Sources come from main settings store - const settings = settingsStore.getSettings(); - setPremiumSources(settings.premiumSources || []); - setLocale(settings.locale); + const syncFromStores = () => { + const settings = settingsStore.getSettings(); + const modeSettings = premiumModeSettingsStore.getSettings(); - // Mode-specific settings come from premium mode settings store - const modeSettings = premiumModeSettingsStore.getSettings(); - setRealtimeLatency(modeSettings.realtimeLatency); - setSearchDisplayMode(modeSettings.searchDisplayMode); - setFullscreenType(modeSettings.fullscreenType); - setProxyMode(modeSettings.proxyMode); - setSeekStepSeconds(modeSettings.seekStepSeconds); - setRememberScrollPosition(modeSettings.rememberScrollPosition); - setDanmakuApiUrl(modeSettings.danmakuApiUrl); - setDanmakuOpacity(modeSettings.danmakuOpacity); - setDanmakuFontSize(modeSettings.danmakuFontSize); - setDanmakuDisplayArea(modeSettings.danmakuDisplayArea); + setPremiumSources(settings.premiumSources || []); + setLocale(settings.locale); + setBlockedCategories(settings.blockedCategories || []); - // blockedCategories is global - setBlockedCategories(settings.blockedCategories || []); + setRealtimeLatency(modeSettings.realtimeLatency); + setSearchDisplayMode(modeSettings.searchDisplayMode); + setFullscreenType(modeSettings.fullscreenType); + setProxyMode(modeSettings.proxyMode); + setSeekStepSeconds(modeSettings.seekStepSeconds); + setRememberScrollPosition(modeSettings.rememberScrollPosition); + setDanmakuApiUrl(modeSettings.danmakuApiUrl); + setDanmakuOpacity(modeSettings.danmakuOpacity); + setDanmakuFontSize(modeSettings.danmakuFontSize); + setDanmakuDisplayArea(modeSettings.danmakuDisplayArea); + }; + + syncFromStores(); + const unsubscribeSettings = settingsStore.subscribe(syncFromStores); + const unsubscribePremiumSettings = premiumModeSettingsStore.subscribe(syncFromStores); + + return () => { + unsubscribeSettings(); + unsubscribePremiumSettings(); + }; }, []); // --- Source management (uses main settingsStore) --- diff --git a/app/settings/hooks/useSettingsPage.ts b/app/settings/hooks/useSettingsPage.ts index 40a4b1e..5368be9 100644 --- a/app/settings/hooks/useSettingsPage.ts +++ b/app/settings/hooks/useSettingsPage.ts @@ -48,23 +48,28 @@ export function useSettingsPage() { const [blockedCategories, setBlockedCategories] = useState([]); useEffect(() => { - const settings = settingsStore.getSettings(); - setSources(settings.sources || []); - setSubscriptions(settings.subscriptions || []); - setSortBy(settings.sortBy); - setRealtimeLatency(settings.realtimeLatency); - setSearchDisplayMode(settings.searchDisplayMode); - setFullscreenType(settings.fullscreenType); - setProxyMode(settings.proxyMode); - setSeekStepSeconds(settings.seekStepSeconds); - setRememberScrollPosition(settings.rememberScrollPosition); - setLocale(settings.locale); - setVideoTogetherEnabled(settings.videoTogetherEnabled); - setDanmakuApiUrl(settings.danmakuApiUrl); - setDanmakuOpacity(settings.danmakuOpacity); - setDanmakuFontSize(settings.danmakuFontSize); - setDanmakuDisplayArea(settings.danmakuDisplayArea); - setBlockedCategories(settings.blockedCategories || []); + const syncFromStore = () => { + const settings = settingsStore.getSettings(); + setSources(settings.sources || []); + setSubscriptions(settings.subscriptions || []); + setSortBy(settings.sortBy); + setRealtimeLatency(settings.realtimeLatency); + setSearchDisplayMode(settings.searchDisplayMode); + setFullscreenType(settings.fullscreenType); + setProxyMode(settings.proxyMode); + setSeekStepSeconds(settings.seekStepSeconds); + setRememberScrollPosition(settings.rememberScrollPosition); + setLocale(settings.locale); + setVideoTogetherEnabled(settings.videoTogetherEnabled); + setDanmakuApiUrl(settings.danmakuApiUrl); + setDanmakuOpacity(settings.danmakuOpacity); + setDanmakuFontSize(settings.danmakuFontSize); + setDanmakuDisplayArea(settings.danmakuDisplayArea); + setBlockedCategories(settings.blockedCategories || []); + }; + + syncFromStore(); + return settingsStore.subscribe(syncFromStore); }, []); const handleSourcesChange = (newSources: VideoSource[]) => { diff --git a/components/PasswordGate.tsx b/components/PasswordGate.tsx index e5dcf84..f862836 100644 --- a/components/PasswordGate.tsx +++ b/components/PasswordGate.tsx @@ -65,6 +65,29 @@ function syncDanmakuApiUrl(rawValue: string) { } } +function applyRuntimeConfig(data: { + subscriptionSources?: string; + iptvSources?: string; + mergeSources?: string; + danmakuApiUrl?: string; +}) { + if (data.subscriptionSources) { + settingsStore.syncEnvSubscriptions(data.subscriptionSources); + } + + if (data.iptvSources) { + syncIPTVSources(data.iptvSources); + } + + if (data.mergeSources) { + syncMergeSources(data.mergeSources); + } + + if (data.danmakuApiUrl) { + syncDanmakuApiUrl(data.danmakuApiUrl); + } +} + export function PasswordGate({ children, hasAuth: initialHasAuth }: { children: React.ReactNode, hasAuth: boolean }) { // Enable background subscription syncing globally useSubscriptionSync(); @@ -102,25 +125,7 @@ export function PasswordGate({ children, hasAuth: initialHasAuth }: { children: if (mounted) { setHasAuth(data.hasAuth); setPersistSession(data.persistSession); - - // Sync subscriptions - if (data.subscriptionSources) { - settingsStore.syncEnvSubscriptions(data.subscriptionSources); - } - - // Sync IPTV sources from env - if (data.iptvSources) { - syncIPTVSources(data.iptvSources); - } - - // Sync merge sources setting from env - if (data.mergeSources) { - syncMergeSources(data.mergeSources); - } - - if (data.danmakuApiUrl) { - syncDanmakuApiUrl(data.danmakuApiUrl); - } + applyRuntimeConfig(data); // Re-evaluate lock status with confirmed server state const confirmLocked = data.hasAuth && !isAuthenticated; @@ -149,6 +154,7 @@ export function PasswordGate({ children, hasAuth: initialHasAuth }: { children: const data = await res.json(); if (data.valid) { + applyRuntimeConfig(data); setSession({ profileId: data.profileId, name: data.name,