feat: add system configuration API endpoint and frontend hook for dynamic settings

This commit is contained in:
2025-12-25 23:41:00 +01:00
parent 529457c9a3
commit 4cb398869d
6 changed files with 64 additions and 8 deletions

View File

@@ -0,0 +1,15 @@
import { useQuery } from "@tanstack/react-query";
import { api } from "@/lib/api";
export interface ConfigResponse {
allow_registration: boolean;
}
export function useConfig() {
return useQuery<ConfigResponse>({
queryKey: ["config"],
queryFn: () => api.get("/config"),
staleTime: Infinity, // Config rarely changes
});
}

View File

@@ -6,6 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Link } from "react-router-dom";
import { useLogin } from "@/hooks/use-auth";
import { useConfig } from "@/hooks/useConfig";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
@@ -22,6 +23,7 @@ type LoginFormValues = z.infer<typeof loginSchema>;
export default function LoginPage() {
const { mutate: login, isPending } = useLogin();
const { data: config } = useConfig();
const form = useForm<LoginFormValues>({
resolver: zodResolver(loginSchema),
@@ -95,12 +97,14 @@ export default function LoginPage() {
</Form>
</CardContent>
<CardFooter className="flex justify-center">
<p className="text-sm text-gray-500 dark:text-gray-400">
Don't have an account?{" "}
<Link to="/register" className="font-semibold text-primary hover:underline">
Sign up
</Link>
</p>
{config?.allow_registration !== false && (
<p className="text-sm text-gray-500 dark:text-gray-400">
Don't have an account?{" "}
<Link to="/register" className="font-semibold text-primary hover:underline">
Sign up
</Link>
</p>
)}
</CardFooter>
</Card>
<SettingsDialog open={settingsOpen} onOpenChange={setSettingsOpen} dataManagementEnabled={false} />

View File

@@ -1,11 +1,12 @@
import { useState } from "react";
import { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { Settings } from "lucide-react";
import { SettingsDialog } from "@/components/settings-dialog";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Link } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom";
import { useRegister } from "@/hooks/use-auth";
import { useConfig } from "@/hooks/useConfig";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
@@ -26,6 +27,19 @@ type RegisterFormValues = z.infer<typeof registerSchema>;
export default function RegisterPage() {
const { mutate: register, isPending } = useRegister();
const { data: config, isLoading: isConfigLoading } = useConfig();
const navigate = useNavigate();
useEffect(() => {
if (!isConfigLoading && config?.allow_registration === false) {
toast.error("Registration is currently disabled");
navigate("/login");
}
}, [config, isConfigLoading, navigate]);
if (isConfigLoading || config?.allow_registration === false) {
return null; // Or a loading spinner
}
const form = useForm<RegisterFormValues>({
resolver: zodResolver(registerSchema),

View File

@@ -166,3 +166,9 @@ impl From<notes_domain::NoteVersion> for NoteVersionResponse {
}
}
}
/// System configuration response
#[derive(Debug, Serialize)]
pub struct ConfigResponse {
pub allow_registration: bool,
}

View File

@@ -0,0 +1,14 @@
//! Configuration routes
use axum::{Json, extract::State};
use crate::dto::ConfigResponse;
use crate::error::ApiResult;
use crate::state::AppState;
/// Get system configuration
pub async fn get_config(State(state): State<AppState>) -> ApiResult<Json<ConfigResponse>> {
Ok(Json(ConfigResponse {
allow_registration: state.config.allow_registration,
}))
}

View File

@@ -1,6 +1,7 @@
//! Route definitions and module structure
pub mod auth;
pub mod config;
pub mod import_export;
pub mod notes;
pub mod tags;
@@ -40,4 +41,6 @@ pub fn api_v1_router() -> Router<AppState> {
"/tags/{id}",
delete(tags::delete_tag).patch(tags::rename_tag),
)
// System Config
.route("/config", get(config::get_config))
}