SurfSense/surfsense_web/contexts/SiteConfigContext.tsx
Ojārs Kapteinis b8bb74ec3e Add disable_registration toggle to site configuration
Implemented comprehensive registration control system that allows administrators to disable new user registrations through the admin panel. This provides better control over user access and supports closed registration scenarios.

Backend changes:
- Added disable_registration field to SiteConfiguration model (db.py)
- Created migration #39 to add disable_registration column with default false
- Updated SiteConfigurationBase, Update, and Read schemas
- Enhanced registration_allowed() dependency to check database toggle
- Returns 403 with clear message when registration is disabled

Frontend changes:
- Added disable_registration to SiteConfig interface and default config
- Updated LocalLoginForm to conditionally hide "Sign up" link when disabled
- Added RouteGuard support for "registration" route key
- Protected /register page with RouteGuard (shows 404 when disabled)
- Added "Registration Control" section to admin site-settings page

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 14:28:16 +02:00

115 lines
2.8 KiB
TypeScript

"use client";
import React, { createContext, useContext, useEffect, useState } from "react";
export interface SiteConfig {
// Header/Navbar toggles
show_pricing_link: boolean;
show_docs_link: boolean;
show_github_link: boolean;
show_sign_in: boolean;
// Homepage toggles
show_get_started_button: boolean;
show_talk_to_us_button: boolean;
// Footer toggles
show_pages_section: boolean;
show_legal_section: boolean;
show_register_section: boolean;
// Route disabling
disable_pricing_route: boolean;
disable_docs_route: boolean;
disable_contact_route: boolean;
disable_terms_route: boolean;
disable_privacy_route: boolean;
// Registration control
disable_registration: boolean;
// Custom text
custom_copyright: string | null;
}
const defaultConfig: SiteConfig = {
show_pricing_link: false,
show_docs_link: false,
show_github_link: false,
show_sign_in: true,
show_get_started_button: false,
show_talk_to_us_button: false,
show_pages_section: false,
show_legal_section: false,
show_register_section: false,
disable_pricing_route: true,
disable_docs_route: true,
disable_contact_route: true,
disable_terms_route: true,
disable_privacy_route: true,
disable_registration: false,
custom_copyright: "SurfSense 2025",
};
interface SiteConfigContextType {
config: SiteConfig;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
const SiteConfigContext = createContext<SiteConfigContextType>({
config: defaultConfig,
loading: true,
error: null,
refetch: async () => {},
});
export function SiteConfigProvider({ children }: { children: React.ReactNode }) {
const [config, setConfig] = useState<SiteConfig>(defaultConfig);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchConfig = async () => {
try {
setLoading(true);
setError(null);
const backendUrl =
process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL || "http://localhost:8000";
const response = await fetch(`${backendUrl}/api/v1/site-config/public`);
if (!response.ok) {
throw new Error(`Failed to fetch site configuration: ${response.statusText}`);
}
const data = await response.json();
setConfig(data);
} catch (err) {
const errorMessage = err instanceof Error ? err.message : "Unknown error occurred";
setError(errorMessage);
console.error("Error fetching site configuration:", err);
// Keep default config on error
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchConfig();
}, []);
return (
<SiteConfigContext.Provider value={{ config, loading, error, refetch: fetchConfig }}>
{children}
</SiteConfigContext.Provider>
);
}
export function useSiteConfig() {
const context = useContext(SiteConfigContext);
if (!context) {
throw new Error("useSiteConfig must be used within a SiteConfigProvider");
}
return context;
}