فهرست منبع

refactor: major code formatting and structure improvements

Adam 3 ماه پیش
والد
کامیت
3075104b7d
9فایلهای تغییر یافته به همراه583 افزوده شده و 572 حذف شده
  1. 10 45
      Caddyfile
  2. 66 61
      backend/server.js
  3. 1 1
      backend/themes.json
  4. 4 1
      public/posts/20251025-hey-there.md
  5. 1 1
      src/App.jsx
  6. 1 1
      src/components/AdminDashboard.jsx
  7. 1 1
      src/components/PostEditor.jsx
  8. 136 127
      src/contexts/AuthContext.jsx
  9. 363 334
      src/contexts/ThemeContext.jsx

+ 10 - 45
Caddyfile

@@ -1,55 +1,20 @@
 goonblog.thevakhovske.eu.org {
-    # Enable automatic HTTPS
-    # Caddy will automatically get and renew SSL certificates
+    root * /var/www/gooneral-wheelchair/dist
+    encode gzip zstd
+    file_server
+
+    # Handle routes for stuffs
+    rewrite /posts* /index.html
+    rewrite /login* /index.html
+    rewrite /admin* /index.html
 
-    # Serve the React frontend (static files)
-    root * dist
-    
-    # Try to serve static files first, then fallback to index.html for SPA routing
-    try_files {path} /index.html
-    
     # API routes - proxy to backend
     handle /api/* {
         reverse_proxy localhost:3001
     }
-    
+
     # Health check endpoint
     handle /health {
         reverse_proxy localhost:3001
     }
-    
-    # Serve static files with proper headers
-    header /assets/* {
-        Cache-Control "public, max-age=31536000, immutable"
-    }
-    
-    # Security headers
-    header {
-        # Enable HSTS
-        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
-        
-        # Prevent clickjacking
-        X-Frame-Options "DENY"
-        
-        # Prevent MIME type sniffing
-        X-Content-Type-Options "nosniff"
-        
-        # XSS protection
-        X-XSS-Protection "1; mode=block"
-        
-        # Referrer policy
-        Referrer-Policy "strict-origin-when-cross-origin"
-        
-        # Content Security Policy (adjust as needed)
-        Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:;"
-    }
-    
-    # Gzip compression
-    encode gzip
-    
-    # Logging
-    log {
-        output file /var/log/caddy/gooneral-wheelchair.log
-        format json
-    }
-}
+}

+ 66 - 61
backend/server.js

@@ -131,73 +131,79 @@ function generateFilename(title) {
 // Authentication Routes
 
 // POST /api/auth/login - Login
-app.post('/api/auth/login', async (req, res) => {
-  try {
-    const { username, password } = req.body;
-    
-    if (!username || !password) {
-      return res.status(400).json({ error: 'Username and password are required' });
-    }
-    
-    const user = await authenticateUser(username, password);
-    if (!user) {
-      return res.status(401).json({ error: 'Invalid username or password' });
-    }
-    
-    // Store user in session
-    req.session.user = user;
-    console.log('Login successful - Session ID:', req.sessionID);
-    console.log('Login successful - Stored user:', req.session.user);
-    
-    // Manually save the session to ensure it's persisted
-    req.session.save((err) => {
-      if (err) {
-        console.error('Session save error:', err);
-        return res.status(500).json({ error: 'Failed to save session' });
-      }
-      
-      console.log('Session saved successfully');
-      res.json({ 
-        success: true, 
-        user: {
-          username: user.username,
-          role: user.role
+app.post("/api/auth/login", async (req, res) => {
+    try {
+        const { username, password } = req.body;
+
+        if (!username || !password) {
+            return res
+                .status(400)
+                .json({ error: "Username and password are required" });
         }
-      });
-    });
-  } catch (error) {
-    console.error('Login error:', error);
-    res.status(500).json({ error: 'Login failed' });
-  }
+
+        const user = await authenticateUser(username, password);
+        if (!user) {
+            return res
+                .status(401)
+                .json({ error: "Invalid username or password" });
+        }
+
+        // Store user in session
+        req.session.user = user;
+        console.log("Login successful - Session ID:", req.sessionID);
+        console.log("Login successful - Stored user:", req.session.user);
+
+        // Manually save the session to ensure it's persisted
+        req.session.save((err) => {
+            if (err) {
+                console.error("Session save error:", err);
+                return res
+                    .status(500)
+                    .json({ error: "Failed to save session" });
+            }
+
+            console.log("Session saved successfully");
+            res.json({
+                success: true,
+                user: {
+                    username: user.username,
+                    role: user.role,
+                },
+            });
+        });
+    } catch (error) {
+        console.error("Login error:", error);
+        res.status(500).json({ error: "Login failed" });
+    }
 });
 
 // POST /api/auth/logout - Logout
-app.post('/api/auth/logout', (req, res) => {
-  req.session.destroy((err) => {
-    if (err) {
-      return res.status(500).json({ error: 'Logout failed' });
-    }
-    res.clearCookie('gooneral-session'); // Use the same name as configured
-    res.json({ success: true, message: 'Logged out successfully' });
-  });
+app.post("/api/auth/logout", (req, res) => {
+    req.session.destroy((err) => {
+        if (err) {
+            return res.status(500).json({ error: "Logout failed" });
+        }
+        res.clearCookie("gooneral-session"); // Use the same name as configured
+        res.json({ success: true, message: "Logged out successfully" });
+    });
 });
 
 // GET /api/auth/me - Get current user
-app.get('/api/auth/me', isAuthenticated, (req, res) => {
-  console.log('Auth check - Session ID:', req.sessionID);
-  console.log('Auth check - Session user:', req.session?.user);
-  console.log('Auth check - Is authenticated:', req.isAuthenticated);
-  
-  if (req.isAuthenticated) {
-    res.json({ 
-      user: {
-        username: req.user.username,
-        role: req.user.role
-      }
-    });
-  } else {
-    res.json({ user: null });
-  }
+app.get("/api/auth/me", isAuthenticated, (req, res) => {
+    console.log("Auth check - Session ID:", req.sessionID);
+    console.log("Auth check - Session user:", req.session?.user);
+    console.log("Auth check - Is authenticated:", req.isAuthenticated);
+
+    if (req.isAuthenticated) {
+        res.json({
+            user: {
+                username: req.user.username,
+                role: req.user.role,
+            },
+        });
+    } else {
+        res.json({ user: null });
+    }
 });
 
 // POST /api/auth/change-password - Change password
@@ -252,7 +258,6 @@ app.get("/api/posts", async (req, res) => {
                 slug,
                 filename,
                 ...metadata,
-                content,
                 createdAt: (await fs.stat(filePath)).birthtime,
                 updatedAt: (await fs.stat(filePath)).mtime,
             });

+ 1 - 1
backend/themes.json

@@ -1,5 +1,5 @@
 {
-  "activeTheme": "default",
+  "activeTheme": "forest",
   "customThemes": [],
   "builtInThemes": [
     {

+ 4 - 1
public/posts/20251025-hey-there.md

@@ -4,4 +4,7 @@ desc: Why does this blog exist?
 > Because it should!
 > 
 
-I've looked at mkdocs and other pseudo-CMSes, and none of them were fitting enough to what I wanted to have, so this exists, and this works... I guess...
+I've looked at mkdocs and other pseudo-CMSes, and none of them were fitting enough to what I wanted to have, so this exists, and this works... I guess...
+
+
+Right?

+ 1 - 1
src/App.jsx

@@ -54,7 +54,7 @@ const md = new MarkdownIt({
     .use(emoji) // GitHub-style emoji :emoji_name:
     .use(footnote); // Standard footnotes [^1]
 
-const API_BASE = "https://goonblog.thevakhovske.eu.org/api";
+import { API_BASE } from "./config";
 
 // Navigation Header Component
 function NavHeader() {

+ 1 - 1
src/components/AdminDashboard.jsx

@@ -2,7 +2,7 @@ import React, { useState, useEffect } from "react";
 import { Link } from "react-router-dom";
 import { useTheme } from "../contexts/ThemeContext";
 
-const API_BASE = "https://goonblog.thevakhovske.eu.org/api";
+import { API_BASE } from "../config";
 
 function AdminDashboard() {
     const [posts, setPosts] = useState([]);

+ 1 - 1
src/components/PostEditor.jsx

@@ -3,7 +3,7 @@ import { useNavigate, useParams, Link } from "react-router-dom";
 import MDEditor from "@uiw/react-md-editor";
 import "@uiw/react-md-editor/markdown-editor.css";
 import "@uiw/react-markdown-preview/markdown.css";
-const API_BASE = "https://goonblog.thevakhovske.eu.org/api";
+import { API_BASE } from "../config";
 
 // Note: MDEditor handles its own markdown processing for the editor interface
 // The final blog rendering uses the MarkdownIt instance in App.jsx

+ 136 - 127
src/contexts/AuthContext.jsx

@@ -1,135 +1,144 @@
-import React, { createContext, useContext, useState, useEffect } from 'react';
+import React, { createContext, useContext, useState, useEffect } from "react";
 
-const API_BASE = 'https://goonblog.thevakhovske.eu.org/api';
+import { API_BASE } from "../config";
 
 const AuthContext = createContext();
 
 export function useAuth() {
-  const context = useContext(AuthContext);
-  if (context === undefined) {
-    throw new Error('useAuth must be used within an AuthProvider');
-  }
-  return context;
+    const context = useContext(AuthContext);
+    if (context === undefined) {
+        throw new Error("useAuth must be used within an AuthProvider");
+    }
+    return context;
 }
 
 export function AuthProvider({ children }) {
-  const [user, setUser] = useState(null);
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(null);
-
-  // Check if user is already authenticated on app start
-  useEffect(() => {
-    checkAuth();
-  }, []);
-
-  const checkAuth = async () => {
-    try {
-      console.log('Checking auth status...');
-      const response = await fetch(`${API_BASE}/auth/me`, {
-        credentials: 'include'
-      });
-      
-      console.log('Auth check response:', response.status, response.statusText);
-      
-      if (response.ok) {
-        const data = await response.json();
-        console.log('Auth check data:', data);
-        setUser(data.user);
-      } else {
-        console.log('Auth check failed, user not authenticated');
-        setUser(null);
-      }
-    } catch (err) {
-      console.error('Auth check failed:', err);
-      setUser(null);
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  const login = async (username, password) => {
-    try {
-      setLoading(true);
-      setError(null);
-      
-      const response = await fetch(`${API_BASE}/auth/login`, {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        credentials: 'include',
-        body: JSON.stringify({ username, password }),
-      });
-
-      const data = await response.json();
-
-      if (response.ok) {
-        setUser(data.user);
-        return { success: true };
-      } else {
-        setError(data.error || 'Login failed');
-        return { success: false, error: data.error || 'Login failed' };
-      }
-    } catch (err) {
-      const errorMessage = 'Network error. Please check if the server is running.';
-      setError(errorMessage);
-      return { success: false, error: errorMessage };
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  const logout = async () => {
-    try {
-      await fetch(`${API_BASE}/auth/logout`, {
-        method: 'POST',
-        credentials: 'include',
-      });
-    } catch (err) {
-      console.error('Logout request failed:', err);
-    } finally {
-      setUser(null);
-      setError(null);
-    }
-  };
-
-  const changePassword = async (currentPassword, newPassword) => {
-    try {
-      const response = await fetch(`${API_BASE}/auth/change-password`, {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        credentials: 'include',
-        body: JSON.stringify({ currentPassword, newPassword }),
-      });
-
-      const data = await response.json();
-
-      if (response.ok) {
-        return { success: true, message: data.message };
-      } else {
-        return { success: false, error: data.error || 'Password change failed' };
-      }
-    } catch (err) {
-      return { success: false, error: 'Network error. Please try again.' };
-    }
-  };
-
-  const value = {
-    user,
-    loading,
-    error,
-    login,
-    logout,
-    changePassword,
-    isAdmin: user?.role === 'admin',
-    isAuthenticated: !!user,
-  };
-
-  return (
-    <AuthContext.Provider value={value}>
-      {children}
-    </AuthContext.Provider>
-  );
-}
+    const [user, setUser] = useState(null);
+    const [loading, setLoading] = useState(true);
+    const [error, setError] = useState(null);
+
+    // Check if user is already authenticated on app start
+    useEffect(() => {
+        checkAuth();
+    }, []);
+
+    const checkAuth = async () => {
+        try {
+            console.log("Checking auth status...");
+            const response = await fetch(`${API_BASE}/auth/me`, {
+                credentials: "include",
+            });
+
+            console.log(
+                "Auth check response:",
+                response.status,
+                response.statusText,
+            );
+
+            if (response.ok) {
+                const data = await response.json();
+                console.log("Auth check data:", data);
+                setUser(data.user);
+            } else {
+                console.log("Auth check failed, user not authenticated");
+                setUser(null);
+            }
+        } catch (err) {
+            console.error("Auth check failed:", err);
+            setUser(null);
+        } finally {
+            setLoading(false);
+        }
+    };
+
+    const login = async (username, password) => {
+        try {
+            setLoading(true);
+            setError(null);
+
+            const response = await fetch(`${API_BASE}/auth/login`, {
+                method: "POST",
+                headers: {
+                    "Content-Type": "application/json",
+                },
+                credentials: "include",
+                body: JSON.stringify({ username, password }),
+            });
+
+            const data = await response.json();
+
+            if (response.ok) {
+                setUser(data.user);
+                return { success: true };
+            } else {
+                setError(data.error || "Login failed");
+                return { success: false, error: data.error || "Login failed" };
+            }
+        } catch (err) {
+            const errorMessage =
+                "Network error. Please check if the server is running.";
+            setError(errorMessage);
+            return { success: false, error: errorMessage };
+        } finally {
+            setLoading(false);
+        }
+    };
+
+    const logout = async () => {
+        try {
+            await fetch(`${API_BASE}/auth/logout`, {
+                method: "POST",
+                credentials: "include",
+            });
+        } catch (err) {
+            console.error("Logout request failed:", err);
+        } finally {
+            setUser(null);
+            setError(null);
+        }
+    };
+
+    const changePassword = async (currentPassword, newPassword) => {
+        try {
+            const response = await fetch(`${API_BASE}/auth/change-password`, {
+                method: "POST",
+                headers: {
+                    "Content-Type": "application/json",
+                },
+                credentials: "include",
+                body: JSON.stringify({ currentPassword, newPassword }),
+            });
+
+            const data = await response.json();
+
+            if (response.ok) {
+                return { success: true, message: data.message };
+            } else {
+                return {
+                    success: false,
+                    error: data.error || "Password change failed",
+                };
+            }
+        } catch (err) {
+            return {
+                success: false,
+                error: "Network error. Please try again.",
+            };
+        }
+    };
+
+    const value = {
+        user,
+        loading,
+        error,
+        login,
+        logout,
+        changePassword,
+        isAdmin: user?.role === "admin",
+        isAuthenticated: !!user,
+    };
+
+    return (
+        <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
+    );
+}

+ 363 - 334
src/contexts/ThemeContext.jsx

@@ -1,347 +1,376 @@
-import React, { createContext, useContext, useState, useEffect } from 'react';
+import React, { createContext, useContext, useState, useEffect } from "react";
 
-const API_BASE = 'https://goonblog.thevakhovske.eu.org/api';
+import { API_BASE } from "../config";
 
 const ThemeContext = createContext();
 
 export function useTheme() {
-  const context = useContext(ThemeContext);
-  if (context === undefined) {
-    throw new Error('useTheme must be used within a ThemeProvider');
-  }
-  return context;
+    const context = useContext(ThemeContext);
+    if (context === undefined) {
+        throw new Error("useTheme must be used within a ThemeProvider");
+    }
+    return context;
 }
 
 export function ThemeProvider({ children }) {
-  const [currentTheme, setCurrentTheme] = useState(null);
-  const [allThemes, setAllThemes] = useState([]);
-  const [activeThemeId, setActiveThemeId] = useState('default');
-  const [loading, setLoading] = useState(true);
-  const [error, setError] = useState(null);
-
-  // Load themes on mount
-  useEffect(() => {
-    loadThemes();
-  }, []);
-
-  // Apply theme CSS variables when theme changes
-  useEffect(() => {
-    if (currentTheme) {
-      applyThemeToCSS(currentTheme);
-    }
-  }, [currentTheme]);
-
-  const loadThemes = async () => {
-    try {
-      setLoading(true);
-      setError(null);
-      
-      console.log('Loading themes...');
-      
-      const response = await fetch(`${API_BASE}/themes`, {
-        credentials: 'include',
-      });
-      console.log('Themes response status:', response.status, response.statusText);
-      
-      if (!response.ok) throw new Error('Failed to fetch themes');
-      
-      const data = await response.json();
-      console.log('Themes data loaded:', data);
-      
-      setAllThemes(data.themes);
-      setActiveThemeId(data.activeTheme);
-      
-      // Find and set current theme
-      const activeTheme = data.themes.find(theme => theme.id === data.activeTheme);
-      console.log('Looking for active theme:', data.activeTheme);
-      console.log('Found active theme:', activeTheme);
-      
-      if (activeTheme) {
-        setCurrentTheme(activeTheme);
-        console.log('Applied theme:', activeTheme.name);
-      } else {
-        console.warn('Active theme not found, using default');
-        const defaultTheme = data.themes.find(theme => theme.id === 'default');
-        if (defaultTheme) {
-          setCurrentTheme(defaultTheme);
+    const [currentTheme, setCurrentTheme] = useState(null);
+    const [allThemes, setAllThemes] = useState([]);
+    const [activeThemeId, setActiveThemeId] = useState("default");
+    const [loading, setLoading] = useState(true);
+    const [error, setError] = useState(null);
+
+    // Load themes on mount
+    useEffect(() => {
+        loadThemes();
+    }, []);
+
+    // Apply theme CSS variables when theme changes
+    useEffect(() => {
+        if (currentTheme) {
+            applyThemeToCSS(currentTheme);
         }
-      }
-    } catch (err) {
-      console.error('Error loading themes:', err);
-      setError(err.message);
-    } finally {
-      setLoading(false);
-    }
-  };
-
-  const setActiveTheme = async (themeId) => {
-    try {
-      setError(null);
-      
-      console.log('Setting active theme:', themeId);
-      
-      const response = await fetch(`${API_BASE}/themes/active`, {
-        method: 'PUT',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        credentials: 'include',
-        body: JSON.stringify({ themeId }),
-      });
-
-      console.log('Response status:', response.status, response.statusText);
-      
-      if (!response.ok) {
-        let errorMessage = 'Failed to set active theme';
+    }, [currentTheme]);
+
+    const loadThemes = async () => {
         try {
-          const errorData = await response.json();
-          errorMessage = errorData.error || errorMessage;
-        } catch (e) {
-          // If we can't parse error as JSON, use default message
+            setLoading(true);
+            setError(null);
+
+            console.log("Loading themes...");
+
+            const response = await fetch(`${API_BASE}/themes`, {
+                credentials: "include",
+            });
+            console.log(
+                "Themes response status:",
+                response.status,
+                response.statusText,
+            );
+
+            if (!response.ok) throw new Error("Failed to fetch themes");
+
+            const data = await response.json();
+            console.log("Themes data loaded:", data);
+
+            setAllThemes(data.themes);
+            setActiveThemeId(data.activeTheme);
+
+            // Find and set current theme
+            const activeTheme = data.themes.find(
+                (theme) => theme.id === data.activeTheme,
+            );
+            console.log("Looking for active theme:", data.activeTheme);
+            console.log("Found active theme:", activeTheme);
+
+            if (activeTheme) {
+                setCurrentTheme(activeTheme);
+                console.log("Applied theme:", activeTheme.name);
+            } else {
+                console.warn("Active theme not found, using default");
+                const defaultTheme = data.themes.find(
+                    (theme) => theme.id === "default",
+                );
+                if (defaultTheme) {
+                    setCurrentTheme(defaultTheme);
+                }
+            }
+        } catch (err) {
+            console.error("Error loading themes:", err);
+            setError(err.message);
+        } finally {
+            setLoading(false);
         }
-        throw new Error(`${errorMessage} (Status: ${response.status})`);
-      }
-      
-      const data = await response.json();
-      console.log('Theme set successfully:', data);
-      
-      setActiveThemeId(themeId);
-      setCurrentTheme(data.theme);
-      
-      return { success: true };
-    } catch (err) {
-      console.error('Error setting active theme:', err);
-      setError(err.message);
-      return { success: false, error: err.message };
-    }
-  };
-
-  const createTheme = async (themeData) => {
-    try {
-      setError(null);
-      
-      const response = await fetch(`${API_BASE}/themes`, {
-        method: 'POST',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        credentials: 'include',
-        body: JSON.stringify(themeData),
-      });
-
-      if (!response.ok) {
-        const errorData = await response.json();
-        throw new Error(errorData.error || 'Failed to create theme');
-      }
-      
-      const newTheme = await response.json();
-      setAllThemes(prev => [...prev, newTheme]);
-      
-      return { success: true, theme: newTheme };
-    } catch (err) {
-      setError(err.message);
-      return { success: false, error: err.message };
-    }
-  };
-
-  const updateTheme = async (themeId, themeData) => {
-    try {
-      setError(null);
-      
-      const response = await fetch(`${API_BASE}/themes/${themeId}`, {
-        method: 'PUT',
-        headers: {
-          'Content-Type': 'application/json',
-        },
-        credentials: 'include',
-        body: JSON.stringify(themeData),
-      });
-
-      if (!response.ok) {
-        const errorData = await response.json();
-        throw new Error(errorData.error || 'Failed to update theme');
-      }
-      
-      const updatedTheme = await response.json();
-      
-      // Update in themes list
-      setAllThemes(prev => prev.map(theme => 
-        theme.id === themeId ? updatedTheme : theme
-      ));
-      
-      // Update current theme if it's the active one
-      if (activeThemeId === themeId) {
-        setCurrentTheme(updatedTheme);
-      }
-      
-      return { success: true, theme: updatedTheme };
-    } catch (err) {
-      setError(err.message);
-      return { success: false, error: err.message };
-    }
-  };
-
-  const deleteTheme = async (themeId) => {
-    try {
-      setError(null);
-      
-      const response = await fetch(`${API_BASE}/themes/${themeId}`, {
-        method: 'DELETE',
-        credentials: 'include',
-      });
-
-      if (!response.ok) {
-        const errorData = await response.json();
-        throw new Error(errorData.error || 'Failed to delete theme');
-      }
-      
-      // Remove from themes list
-      setAllThemes(prev => prev.filter(theme => theme.id !== themeId));
-      
-      return { success: true };
-    } catch (err) {
-      setError(err.message);
-      return { success: false, error: err.message };
-    }
-  };
-
-  const exportTheme = async (themeId) => {
-    try {
-      const response = await fetch(`${API_BASE}/themes/${themeId}/export`, {
-        credentials: 'include',
-      });
-
-      if (!response.ok) throw new Error('Failed to export theme');
-      
-      const themeData = await response.json();
-      
-      // Create and download file
-      const blob = new Blob([JSON.stringify(themeData, null, 2)], { 
-        type: 'application/json' 
-      });
-      const url = URL.createObjectURL(blob);
-      const a = document.createElement('a');
-      a.href = url;
-      a.download = `theme-${themeId}.json`;
-      document.body.appendChild(a);
-      a.click();
-      document.body.removeChild(a);
-      URL.revokeObjectURL(url);
-      
-      return { success: true };
-    } catch (err) {
-      setError(err.message);
-      return { success: false, error: err.message };
-    }
-  };
-
-  const importTheme = async (themeData) => {
-    try {
-      // Ensure unique ID
-      const uniqueId = `${themeData.id}-${Date.now()}`;
-      const importedTheme = {
-        ...themeData,
-        id: uniqueId,
-        name: `${themeData.name} (Imported)`
-      };
-      
-      return await createTheme(importedTheme);
-    } catch (err) {
-      setError(err.message);
-      return { success: false, error: err.message };
-    }
-  };
+    };
 
-  // Apply theme CSS variables to document root
-  const applyThemeToCSS = (theme) => {
-    if (!theme) {
-      console.warn('No theme to apply');
-      return;
-    }
-    
-    console.log('Applying theme to CSS:', theme.name, theme.colors);
-    
-    const root = document.documentElement;
-    
-    // Apply color variables
-    Object.entries(theme.colors).forEach(([key, value]) => {
-      root.style.setProperty(`--color-${kebabCase(key)}`, value);
-    });
-    
-    // Apply typography variables
-    if (theme.typography) {
-      root.style.setProperty('--font-family', theme.typography.fontFamily);
-      root.style.setProperty('--font-family-heading', theme.typography.headingFontFamily);
-      
-      if (theme.typography.fontSize) {
-        Object.entries(theme.typography.fontSize).forEach(([key, value]) => {
-          root.style.setProperty(`--font-size-${key}`, value);
-        });
-      }
-      
-      if (theme.typography.lineHeight) {
-        Object.entries(theme.typography.lineHeight).forEach(([key, value]) => {
-          root.style.setProperty(`--line-height-${key}`, value);
-        });
-      }
-    }
-    
-    // Apply layout variables
-    if (theme.layout) {
-      if (theme.layout.borderRadius) {
-        Object.entries(theme.layout.borderRadius).forEach(([key, value]) => {
-          root.style.setProperty(`--border-radius-${key}`, value);
-        });
-      }
-      
-      if (theme.layout.spacing) {
-        Object.entries(theme.layout.spacing).forEach(([key, value]) => {
-          root.style.setProperty(`--spacing-${key}`, value);
-        });
-      }
-      
-      if (theme.layout.shadows) {
-        Object.entries(theme.layout.shadows).forEach(([key, value]) => {
-          root.style.setProperty(`--shadow-${key}`, value);
+    const setActiveTheme = async (themeId) => {
+        try {
+            setError(null);
+
+            console.log("Setting active theme:", themeId);
+
+            const response = await fetch(`${API_BASE}/themes/active`, {
+                method: "PUT",
+                headers: {
+                    "Content-Type": "application/json",
+                },
+                credentials: "include",
+                body: JSON.stringify({ themeId }),
+            });
+
+            console.log(
+                "Response status:",
+                response.status,
+                response.statusText,
+            );
+
+            if (!response.ok) {
+                let errorMessage = "Failed to set active theme";
+                try {
+                    const errorData = await response.json();
+                    errorMessage = errorData.error || errorMessage;
+                } catch (e) {
+                    // If we can't parse error as JSON, use default message
+                }
+                throw new Error(`${errorMessage} (Status: ${response.status})`);
+            }
+
+            const data = await response.json();
+            console.log("Theme set successfully:", data);
+
+            setActiveThemeId(themeId);
+            setCurrentTheme(data.theme);
+
+            return { success: true };
+        } catch (err) {
+            console.error("Error setting active theme:", err);
+            setError(err.message);
+            return { success: false, error: err.message };
+        }
+    };
+
+    const createTheme = async (themeData) => {
+        try {
+            setError(null);
+
+            const response = await fetch(`${API_BASE}/themes`, {
+                method: "POST",
+                headers: {
+                    "Content-Type": "application/json",
+                },
+                credentials: "include",
+                body: JSON.stringify(themeData),
+            });
+
+            if (!response.ok) {
+                const errorData = await response.json();
+                throw new Error(errorData.error || "Failed to create theme");
+            }
+
+            const newTheme = await response.json();
+            setAllThemes((prev) => [...prev, newTheme]);
+
+            return { success: true, theme: newTheme };
+        } catch (err) {
+            setError(err.message);
+            return { success: false, error: err.message };
+        }
+    };
+
+    const updateTheme = async (themeId, themeData) => {
+        try {
+            setError(null);
+
+            const response = await fetch(`${API_BASE}/themes/${themeId}`, {
+                method: "PUT",
+                headers: {
+                    "Content-Type": "application/json",
+                },
+                credentials: "include",
+                body: JSON.stringify(themeData),
+            });
+
+            if (!response.ok) {
+                const errorData = await response.json();
+                throw new Error(errorData.error || "Failed to update theme");
+            }
+
+            const updatedTheme = await response.json();
+
+            // Update in themes list
+            setAllThemes((prev) =>
+                prev.map((theme) =>
+                    theme.id === themeId ? updatedTheme : theme,
+                ),
+            );
+
+            // Update current theme if it's the active one
+            if (activeThemeId === themeId) {
+                setCurrentTheme(updatedTheme);
+            }
+
+            return { success: true, theme: updatedTheme };
+        } catch (err) {
+            setError(err.message);
+            return { success: false, error: err.message };
+        }
+    };
+
+    const deleteTheme = async (themeId) => {
+        try {
+            setError(null);
+
+            const response = await fetch(`${API_BASE}/themes/${themeId}`, {
+                method: "DELETE",
+                credentials: "include",
+            });
+
+            if (!response.ok) {
+                const errorData = await response.json();
+                throw new Error(errorData.error || "Failed to delete theme");
+            }
+
+            // Remove from themes list
+            setAllThemes((prev) =>
+                prev.filter((theme) => theme.id !== themeId),
+            );
+
+            return { success: true };
+        } catch (err) {
+            setError(err.message);
+            return { success: false, error: err.message };
+        }
+    };
+
+    const exportTheme = async (themeId) => {
+        try {
+            const response = await fetch(
+                `${API_BASE}/themes/${themeId}/export`,
+                {
+                    credentials: "include",
+                },
+            );
+
+            if (!response.ok) throw new Error("Failed to export theme");
+
+            const themeData = await response.json();
+
+            // Create and download file
+            const blob = new Blob([JSON.stringify(themeData, null, 2)], {
+                type: "application/json",
+            });
+            const url = URL.createObjectURL(blob);
+            const a = document.createElement("a");
+            a.href = url;
+            a.download = `theme-${themeId}.json`;
+            document.body.appendChild(a);
+            a.click();
+            document.body.removeChild(a);
+            URL.revokeObjectURL(url);
+
+            return { success: true };
+        } catch (err) {
+            setError(err.message);
+            return { success: false, error: err.message };
+        }
+    };
+
+    const importTheme = async (themeData) => {
+        try {
+            // Ensure unique ID
+            const uniqueId = `${themeData.id}-${Date.now()}`;
+            const importedTheme = {
+                ...themeData,
+                id: uniqueId,
+                name: `${themeData.name} (Imported)`,
+            };
+
+            return await createTheme(importedTheme);
+        } catch (err) {
+            setError(err.message);
+            return { success: false, error: err.message };
+        }
+    };
+
+    // Apply theme CSS variables to document root
+    const applyThemeToCSS = (theme) => {
+        if (!theme) {
+            console.warn("No theme to apply");
+            return;
+        }
+
+        console.log("Applying theme to CSS:", theme.name, theme.colors);
+
+        const root = document.documentElement;
+
+        // Apply color variables
+        Object.entries(theme.colors).forEach(([key, value]) => {
+            root.style.setProperty(`--color-${kebabCase(key)}`, value);
         });
-      }
-    }
-    
-    // Apply custom CSS if present
-    if (theme.customCSS) {
-      let customStyleEl = document.getElementById('theme-custom-css');
-      if (!customStyleEl) {
-        customStyleEl = document.createElement('style');
-        customStyleEl.id = 'theme-custom-css';
-        document.head.appendChild(customStyleEl);
-      }
-      customStyleEl.textContent = theme.customCSS;
-    }
-  };
-
-  // Helper function to convert camelCase to kebab-case
-  const kebabCase = (str) => {
-    return str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
-  };
-
-  const value = {
-    currentTheme,
-    allThemes,
-    activeThemeId,
-    loading,
-    error,
-    loadThemes,
-    setActiveTheme,
-    createTheme,
-    updateTheme,
-    deleteTheme,
-    exportTheme,
-    importTheme
-  };
-
-  return (
-    <ThemeContext.Provider value={value}>
-      {children}
-    </ThemeContext.Provider>
-  );
-}
+
+        // Apply typography variables
+        if (theme.typography) {
+            root.style.setProperty(
+                "--font-family",
+                theme.typography.fontFamily,
+            );
+            root.style.setProperty(
+                "--font-family-heading",
+                theme.typography.headingFontFamily,
+            );
+
+            if (theme.typography.fontSize) {
+                Object.entries(theme.typography.fontSize).forEach(
+                    ([key, value]) => {
+                        root.style.setProperty(`--font-size-${key}`, value);
+                    },
+                );
+            }
+
+            if (theme.typography.lineHeight) {
+                Object.entries(theme.typography.lineHeight).forEach(
+                    ([key, value]) => {
+                        root.style.setProperty(`--line-height-${key}`, value);
+                    },
+                );
+            }
+        }
+
+        // Apply layout variables
+        if (theme.layout) {
+            if (theme.layout.borderRadius) {
+                Object.entries(theme.layout.borderRadius).forEach(
+                    ([key, value]) => {
+                        root.style.setProperty(`--border-radius-${key}`, value);
+                    },
+                );
+            }
+
+            if (theme.layout.spacing) {
+                Object.entries(theme.layout.spacing).forEach(([key, value]) => {
+                    root.style.setProperty(`--spacing-${key}`, value);
+                });
+            }
+
+            if (theme.layout.shadows) {
+                Object.entries(theme.layout.shadows).forEach(([key, value]) => {
+                    root.style.setProperty(`--shadow-${key}`, value);
+                });
+            }
+        }
+
+        // Apply custom CSS if present
+        if (theme.customCSS) {
+            let customStyleEl = document.getElementById("theme-custom-css");
+            if (!customStyleEl) {
+                customStyleEl = document.createElement("style");
+                customStyleEl.id = "theme-custom-css";
+                document.head.appendChild(customStyleEl);
+            }
+            customStyleEl.textContent = theme.customCSS;
+        }
+    };
+
+    // Helper function to convert camelCase to kebab-case
+    const kebabCase = (str) => {
+        return str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
+    };
+
+    const value = {
+        currentTheme,
+        allThemes,
+        activeThemeId,
+        loading,
+        error,
+        loadThemes,
+        setActiveTheme,
+        createTheme,
+        updateTheme,
+        deleteTheme,
+        exportTheme,
+        importTheme,
+    };
+
+    return (
+        <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>
+    );
+}