Переглянути джерело

Remove legacy code from Model Manager

- Removed legacyMode flag from ServerConfig
- Removed resetToLegacyDirectories() method and its implementation
- Removed legacy scanning mode from ModelManager
- Simplified configureFromServerConfig() to always use explicit directories
- Updated getModelTypeDirectory() to remove legacy path construction
- Updated determineModelType() to remove legacy directory structure checks
- All legacy-related comments and implementation removed
- Build compilation successful with no warnings or errors
Fszontagh 3 місяців тому
батько
коміт
6a6343738c

+ 1 - 0
.gitignore

@@ -71,3 +71,4 @@ test_results.txt
 # Documentation (keep in repo)
 !README.md
 !*.md
+.aider*

+ 1 - 4
include/model_manager.h

@@ -230,10 +230,7 @@ public:
      */
     std::map<ModelType, std::string> getAllModelTypeDirectories() const;
 
-    /**
-     * @brief Reset to legacy directory mode (single models directory)
-     */
-    void resetToLegacyDirectories();
+    // Legacy methods removed - using explicit directory configuration only
 
     /**
      * @brief Configure ModelManager with ServerConfig

+ 0 - 3
include/server_config.h

@@ -61,9 +61,6 @@ struct ServerConfig {
     // UI directory (optional - for serving static web UI)
     std::string uiDir = "";                  // Directory containing static web UI files
 
-    // Legacy mode flag (always false now - kept for backward compatibility)
-    bool legacyMode = false;
-
     // Logging options
     bool enableFileLogging = false;
     std::string logFilePath = "/var/log/stable-diffusion-rest/server.log";

+ 0 - 72
open_issues_summary.json

@@ -1,72 +0,0 @@
-{
-  "repository": {
-    "name": "stable-diffusion.cpp-rest",
-    "full_name": "fszontagh/stable-diffusion.cpp-rest",
-    "url": "https://git.fsociety.hu/fszontagh/stable-diffusion.cpp-rest",
-    "open_issues_count": 2
-  },
-  "issues": [
-    {
-      "id": 133,
-      "number": 34,
-      "title": "cleanup the project, remove the test file",
-      "description": "There s too mani test files in the project. Remove these files which are not required to compile the project. \r\n\r\nCrean-up the documentations, only keep the README.md. \r\nRemove the files which are not required to build the project. ",
-      "labels": [
-        {
-          "id": 41,
-          "name": "enhancement",
-          "color": "a2eeef"
-        }
-      ],
-      "assignee": {
-        "id": 3,
-        "username": "agent001",
-        "full_name": "AI Agent 001",
-        "email": "agent001@fsociety.hu"
-      },
-      "author": {
-        "id": 1,
-        "username": "fszontagh",
-        "full_name": "Szontágh Ferenc",
-        "email": "szf@fsociety.hu"
-      },
-      "state": "open",
-      "comments": 0,
-      "created_at": "2025-11-04T07:47:21Z",
-      "updated_at": "2025-11-04T07:47:21Z",
-      "milestone": null,
-      "pull_request": null
-    },
-    {
-      "id": 132,
-      "number": 33,
-      "title": "feat: inpainting and background",
-      "description": "Refactor the inapinting in the webui to inpaint to the uploaded image not into a difference canvas. ",
-      "labels": [
-        {
-          "id": 41,
-          "name": "enhancement",
-          "color": "a2eeef"
-        }
-      ],
-      "assignee": {
-        "id": 3,
-        "username": "agent001",
-        "full_name": "AI Agent 001",
-        "email": "agent001@fsociety.hu"
-      },
-      "author": {
-        "id": 1,
-        "username": "fszontagh",
-        "full_name": "Szontágh Ferenc",
-        "email": "szf@fsociety.hu"
-      },
-      "state": "open",
-      "comments": 0,
-      "created_at": "2025-11-04T07:45:09Z",
-      "updated_at": "2025-11-04T07:45:09Z",
-      "milestone": null,
-      "pull_request": null
-    }
-  ]
-}

+ 0 - 70
open_issues_summary.md

@@ -1,70 +0,0 @@
-# Open Issues Summary for stable-diffusion.cpp-rest
-
-**Repository:** https://git.fsociety.hu/fszontagh/stable-diffusion.cpp-rest
-**Total Open Issues:** 2
-**Last Updated:** 2025-11-04T07:49:18Z
-
----
-
-## Issue #34: cleanup the project, remove the test file
-
-**Status:** Open
-**Created:** 2025-11-04T07:47:21Z
-**Updated:** 2025-11-04T07:47:21Z
-**Author:** fszontagh (Szontágh Ferenc)
-**Assignee:** agent001 (AI Agent 001)
-**Labels:** enhancement
-
-### Description
-There's too many test files in the project. Remove these files which are not required to compile the project.
-
-Clean-up the documentations, only keep the README.md.
-Remove the files which are not required to build the project.
-
-### Metadata
-- **Issue ID:** 133
-- **Comments:** 0
-- **Milestone:** None
-- **Pull Request:** None
-
----
-
-## Issue #33: feat: inpainting and background
-
-**Status:** Open
-**Created:** 2025-11-04T07:45:09Z
-**Updated:** 2025-11-04T07:45:09Z
-**Author:** fszontagh (Szontágh Ferenc)
-**Assignee:** agent001 (AI Agent 001)
-**Labels:** enhancement
-
-### Description
-Refactor the inpainting in the webui to inpaint to the uploaded image not into a difference canvas.
-
-### Metadata
-- **Issue ID:** 132
-- **Comments:** 0
-- **Milestone:** None
-- **Pull Request:** None
-
----
-
-## Summary Analysis
-
-### Issue Distribution by Label
-- **enhancement:** 2 issues (100%)
-
-### Issue Assignment
-- **agent001 (AI Agent 001):** 2 assigned issues (100%)
-
-### Issue Creation Timeline
-- **2025-11-04:** 2 issues created
-  - Issue #33 at 07:45:09Z
-  - Issue #34 at 07:47:21Z
-
-### Key Observations
-1. Both issues are tagged as "enhancement"
-2. Both issues are assigned to agent001
-3. Both issues were created on the same day (2025-11-04)
-4. Neither issue has comments or milestones
-5. Both issues are relatively recent (created today)

+ 0 - 3
src/main.cpp

@@ -267,9 +267,6 @@ ServerConfig parseArguments(int argc, char* argv[]) {
     config.taesdDir = resolveDirectoryPath(config.taesdDir, config.modelsDir);
     config.vaeDir = resolveDirectoryPath(config.vaeDir, config.modelsDir);
 
-    // We're always in explicit mode now (no legacy mode)
-    config.legacyMode = false;
-
     return config;
 }
 

+ 83 - 116
src/model_manager.cpp

@@ -33,7 +33,6 @@ public:
     std::map<std::string, std::unique_ptr<StableDiffusionWrapper>> loadedModels;
     mutable std::shared_mutex modelsMutex;
     std::atomic<bool> scanCancelled{false};
-    bool legacyMode = true;
 
     /**
      * @brief Validate a directory path
@@ -99,12 +98,10 @@ public:
             return it->second;
         }
 
-        // If in legacy mode, construct default path
-        if (legacyMode) {
-            std::string defaultDir = getDefaultDirectoryName(type);
-            if (!defaultDir.empty()) {
-                return modelsDirectory + "/" + defaultDir;
-            }
+        // Always use explicit directory configuration
+        std::string defaultDir = getDefaultDirectoryName(type);
+        if (!defaultDir.empty()) {
+            return modelsDirectory + "/" + defaultDir;
         }
 
         return "";
@@ -195,11 +192,11 @@ public:
             }
         }
 
-        // If in legacy mode or no configured directories matched, check default directory structure
-        if (legacyMode || modelTypeDirectories.empty()) {
+        // Check default directory structure when no explicit directories configured
+        if (modelTypeDirectories.empty()) {
             // Check the entire path hierarchy for model type directories
             fs::path currentPath = filePath.parent_path();
-            
+
             while (currentPath.has_filename()) {
                 std::string dirName = currentPath.filename().string();
                 std::transform(dirName.begin(), dirName.end(), dirName.begin(), ::tolower);
@@ -234,7 +231,7 @@ public:
                         return ModelType::EMBEDDING;
                     }
                 }
-                
+
                 // Move up to parent directory
                 currentPath = currentPath.parent_path();
             }
@@ -323,7 +320,7 @@ public:
                         // Calculate relative path from the scanned directory (not base models directory)
                         fs::path relativePath = fs::relative(filePath, directory);
                         std::string modelName = relativePath.string();
-                        
+
                         // Normalize path separators for consistency
                         std::replace(modelName.begin(), modelName.end(), '\\', '/');
 
@@ -463,38 +460,21 @@ bool ModelManager::scanModelsDirectory() {
     // Create temporary map to store scan results (outside of lock)
     std::map<std::string, ModelInfo> tempModels;
 
-    if (pImpl->legacyMode) {
-        // Legacy mode: recursively scan the entire models directory and auto-detect all subdirectories
-        fs::path modelsPath(pImpl->modelsDirectory);
-
-        if (!fs::exists(modelsPath) || !fs::is_directory(modelsPath)) {
-            std::cerr << "Models directory does not exist: " << pImpl->modelsDirectory << std::endl;
-            return false;
-        }
-
-        // Scan the entire models directory recursively
-        // Model type will be determined by file extension and directory structure
-        // Model names will include the full relative path from the base models directory
-        if (!pImpl->scanDirectory(modelsPath, ModelType::NONE, tempModels)) {
-            return false;
-        }
-    } else {
-        // Explicit mode: scan configured directories for each model type
-        std::vector<std::pair<ModelType, std::string>> directoriesToScan = {
-            {ModelType::CHECKPOINT, pImpl->getModelTypeDirectory(ModelType::CHECKPOINT)},
-            {ModelType::CONTROLNET, pImpl->getModelTypeDirectory(ModelType::CONTROLNET)},
-            {ModelType::LORA, pImpl->getModelTypeDirectory(ModelType::LORA)},
-            {ModelType::VAE, pImpl->getModelTypeDirectory(ModelType::VAE)},
-            {ModelType::TAESD, pImpl->getModelTypeDirectory(ModelType::TAESD)},
-            {ModelType::ESRGAN, pImpl->getModelTypeDirectory(ModelType::ESRGAN)},
-            {ModelType::EMBEDDING, pImpl->getModelTypeDirectory(ModelType::EMBEDDING)}
-        };
-
-        for (const auto& [type, dirPath] : directoriesToScan) {
-            if (!dirPath.empty()) {
-                if (!pImpl->scanDirectory(dirPath, type, tempModels)) {
-                    return false;
-                }
+    // Explicit mode: scan configured directories for each model type
+    std::vector<std::pair<ModelType, std::string>> directoriesToScan = {
+        {ModelType::CHECKPOINT, pImpl->getModelTypeDirectory(ModelType::CHECKPOINT)},
+        {ModelType::CONTROLNET, pImpl->getModelTypeDirectory(ModelType::CONTROLNET)},
+        {ModelType::LORA, pImpl->getModelTypeDirectory(ModelType::LORA)},
+        {ModelType::VAE, pImpl->getModelTypeDirectory(ModelType::VAE)},
+        {ModelType::TAESD, pImpl->getModelTypeDirectory(ModelType::TAESD)},
+        {ModelType::ESRGAN, pImpl->getModelTypeDirectory(ModelType::ESRGAN)},
+        {ModelType::EMBEDDING, pImpl->getModelTypeDirectory(ModelType::EMBEDDING)}
+    };
+
+    for (const auto& [type, dirPath] : directoriesToScan) {
+        if (!dirPath.empty()) {
+            if (!pImpl->scanDirectory(dirPath, type, tempModels)) {
+                return false;
             }
         }
     }
@@ -755,7 +735,6 @@ bool ModelManager::setModelTypeDirectory(ModelType type, const std::string& path
     }
 
     pImpl->modelTypeDirectories[type] = path;
-    pImpl->legacyMode = false;
 
     return true;
 }
@@ -777,7 +756,6 @@ bool ModelManager::setAllModelTypeDirectories(const std::map<ModelType, std::str
 
     // Set all directories
     pImpl->modelTypeDirectories = directories;
-    pImpl->legacyMode = false;
 
     return true;
 }
@@ -787,12 +765,8 @@ std::map<ModelType, std::string> ModelManager::getAllModelTypeDirectories() cons
     return pImpl->modelTypeDirectories;
 }
 
-void ModelManager::resetToLegacyDirectories() {
-    // Note: This method should be called with modelsMutex already locked
-
-    pImpl->modelTypeDirectories.clear();
-    pImpl->legacyMode = true;
-}
+    // Legacy resetToLegacyDirectories method removed
+    // Using explicit directory configuration only
 
 bool ModelManager::configureFromServerConfig(const ServerConfig& config) {
     std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
@@ -800,49 +774,42 @@ bool ModelManager::configureFromServerConfig(const ServerConfig& config) {
     // Set the base models directory
     pImpl->modelsDirectory = config.modelsDir;
 
-    if (config.legacyMode) {
-        // Legacy mode: use single models directory
-        resetToLegacyDirectories();
-        return true;
-    } else {
-        // Explicit mode: set per-type directories
-        std::map<ModelType, std::string> directories;
+    // Always use explicit directory configuration
+    std::map<ModelType, std::string> directories;
 
-        if (!config.checkpoints.empty()) {
-            directories[ModelType::CHECKPOINT] = config.checkpoints;
-        }
-        if (!config.controlnetDir.empty()) {
-            directories[ModelType::CONTROLNET] = config.controlnetDir;
-        }
-        if (!config.embeddingsDir.empty()) {
-            directories[ModelType::EMBEDDING] = config.embeddingsDir;
-        }
-        if (!config.esrganDir.empty()) {
-            directories[ModelType::ESRGAN] = config.esrganDir;
-        }
-        if (!config.loraDir.empty()) {
-            directories[ModelType::LORA] = config.loraDir;
-        }
-        if (!config.taesdDir.empty()) {
-            directories[ModelType::TAESD] = config.taesdDir;
-        }
-        if (!config.vaeDir.empty()) {
-            directories[ModelType::VAE] = config.vaeDir;
-        }
+    if (!config.checkpoints.empty()) {
+        directories[ModelType::CHECKPOINT] = config.checkpoints;
+    }
+    if (!config.controlnetDir.empty()) {
+        directories[ModelType::CONTROLNET] = config.controlnetDir;
+    }
+    if (!config.embeddingsDir.empty()) {
+        directories[ModelType::EMBEDDING] = config.embeddingsDir;
+    }
+    if (!config.esrganDir.empty()) {
+        directories[ModelType::ESRGAN] = config.esrganDir;
+    }
+    if (!config.loraDir.empty()) {
+        directories[ModelType::LORA] = config.loraDir;
+    }
+    if (!config.taesdDir.empty()) {
+        directories[ModelType::TAESD] = config.taesdDir;
+    }
+    if (!config.vaeDir.empty()) {
+        directories[ModelType::VAE] = config.vaeDir;
+    }
 
-        // Validate all directories first
-        for (const auto& [type, path] : directories) {
-            if (!path.empty() && !pImpl->validateDirectory(path)) {
-                return false;
-            }
+    // Validate all directories first
+    for (const auto& [type, path] : directories) {
+        if (!path.empty() && !pImpl->validateDirectory(path)) {
+            return false;
         }
+    }
 
-        // Set all directories (inlined to avoid deadlock from calling setAllModelTypeDirectories)
-        pImpl->modelTypeDirectories = directories;
-        pImpl->legacyMode = false;
+    // Set all directories (inlined to avoid deadlock from calling setAllModelTypeDirectories)
+    pImpl->modelTypeDirectories = directories;
 
-        return true;
-    }
+    return true;
 }
 
 void ModelManager::cancelScan() {
@@ -852,16 +819,16 @@ void ModelManager::cancelScan() {
 
 std::string ModelManager::computeModelHash(const std::string& modelName) {
     std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
-    
+
     auto it = pImpl->availableModels.find(modelName);
     if (it == pImpl->availableModels.end()) {
         std::cerr << "Model not found: " << modelName << std::endl;
         return "";
     }
-    
+
     std::string filePath = it->second.fullPath;
     lock.unlock();
-    
+
     std::ifstream file(filePath, std::ios::binary);
     if (!file.is_open()) {
         std::cerr << "Failed to open file for hashing: " << filePath << std::endl;
@@ -922,75 +889,75 @@ std::string ModelManager::computeModelHash(const std::string& modelName) {
     for (unsigned int i = 0; i < hashLen; i++) {
         oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
     }
-    
+
     std::string hashStr = oss.str();
     std::cout << "Hash computed: " << hashStr.substr(0, 16) << "..." << std::endl;
-    
+
     return hashStr;
 }
 
 std::string ModelManager::loadModelHashFromFile(const std::string& modelName) {
     std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
-    
+
     auto it = pImpl->availableModels.find(modelName);
     if (it == pImpl->availableModels.end()) {
         return "";
     }
-    
+
     std::string jsonPath = it->second.fullPath + ".json";
     lock.unlock();
-    
+
     if (!fs::exists(jsonPath)) {
         return "";
     }
-    
+
     try {
         std::ifstream jsonFile(jsonPath);
         if (!jsonFile.is_open()) {
             return "";
         }
-        
+
         nlohmann::json j;
         jsonFile >> j;
         jsonFile.close();
-        
+
         if (j.contains("sha256") && j["sha256"].is_string()) {
             return j["sha256"].get<std::string>();
         }
     } catch (const std::exception& e) {
         std::cerr << "Error loading hash from JSON: " << e.what() << std::endl;
     }
-    
+
     return "";
 }
 
 bool ModelManager::saveModelHashToFile(const std::string& modelName, const std::string& hash) {
     std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
-    
+
     auto it = pImpl->availableModels.find(modelName);
     if (it == pImpl->availableModels.end()) {
         return false;
     }
-    
+
     std::string jsonPath = it->second.fullPath + ".json";
     size_t fileSize = it->second.fileSize;
     lock.unlock();
-    
+
     try {
         nlohmann::json j;
         j["sha256"] = hash;
         j["file_size"] = fileSize;
         j["computed_at"] = std::chrono::system_clock::now().time_since_epoch().count();
-        
+
         std::ofstream jsonFile(jsonPath);
         if (!jsonFile.is_open()) {
             std::cerr << "Failed to open file for writing: " << jsonPath << std::endl;
             return false;
         }
-        
+
         jsonFile << j.dump(2);
         jsonFile.close();
-        
+
         std::cout << "Saved hash to: " << jsonPath << std::endl;
         return true;
     } catch (const std::exception& e) {
@@ -1004,20 +971,20 @@ std::string ModelManager::findModelByHash(const std::string& hash) {
         std::cerr << "Hash must be at least 10 characters" << std::endl;
         return "";
     }
-    
+
     std::shared_lock<std::shared_mutex> lock(pImpl->modelsMutex);
-    
+
     for (const auto& [name, info] : pImpl->availableModels) {
         if (info.sha256.empty()) {
             continue;
         }
-        
+
         // Support full or partial match (minimum 10 chars)
         if (info.sha256 == hash || info.sha256.substr(0, hash.length()) == hash) {
             return name;
         }
     }
-    
+
     return "";
 }
 
@@ -1035,22 +1002,22 @@ std::string ModelManager::ensureModelHash(const std::string& modelName, bool for
             return existingHash;
         }
     }
-    
+
     // Compute new hash
     std::string hash = computeModelHash(modelName);
     if (hash.empty()) {
         return "";
     }
-    
+
     // Save to file
     saveModelHashToFile(modelName, hash);
-    
+
     // Update in-memory model info
     std::unique_lock<std::shared_mutex> lock(pImpl->modelsMutex);
     auto it = pImpl->availableModels.find(modelName);
     if (it != pImpl->availableModels.end()) {
         it->second.sha256 = hash;
     }
-    
+
     return hash;
 }

+ 17 - 6
src/server.cpp

@@ -572,8 +572,23 @@ void Server::registerEndpoints() {
                     res.set_content("File not found", "text/plain");
                 }
             } else {
-                res.status = 404;
-                res.set_content("File not found", "text/plain");
+                // For SPA routing, if the file doesn't exist, serve index.html
+                // This allows Next.js to handle client-side routing
+                std::string indexPath = m_uiDir + "/index.html";
+                if (std::filesystem::exists(indexPath)) {
+                    std::ifstream indexFile(indexPath, std::ios::binary);
+                    if (indexFile.is_open()) {
+                        std::string content((std::istreambuf_iterator<char>(indexFile)),
+                                         std::istreambuf_iterator<char>());
+                        res.set_content(content, "text/html");
+                    } else {
+                        res.status = 404;
+                        res.set_content("File not found", "text/plain");
+                    }
+                } else {
+                    res.status = 404;
+                    res.set_content("File not found", "text/plain");
+                }
             }
         };
 
@@ -4495,10 +4510,6 @@ void Server::serverThreadFunction(const std::string& host, int port) {
         std::cout << "Port " << port << " is available, proceeding with server startup..." << std::endl;
         std::cout << "Calling listen()..." << std::endl;
 
-        // The listen() call will block until server is stopped
-        // listen() returns true if it successfully binds and starts
-        // Once it binds successfully, we set m_isRunning to true via a callback
-
         // Set up a flag to track if listen started successfully
         std::atomic<bool> listenStarted{false};
 

+ 0 - 1
test-auth/api_keys.json

@@ -1 +0,0 @@
-{}

+ 0 - 19
test-auth/users.json

@@ -1,19 +0,0 @@
-{
-  "fszontagh": {
-    "active": true,
-    "api_keys": [],
-    "created_at": 1762203172,
-    "created_by": "system",
-    "email": "fszontagh@localhost",
-    "id": "user_1762203172_9383",
-    "last_login_at": 11,
-    "password_changed_at": 0,
-    "password_hash": "",
-    "permissions": [
-      "read",
-      "generate"
-    ],
-    "role": "user",
-    "username": "fszontagh"
-  }
-}

+ 14 - 13
webui/app/page.tsx

@@ -50,36 +50,37 @@ const features = [
 ];
 
 export default function HomePage() {
-  const { user, logout, authEnabled, isAuthenticated, isLoading } = useAuth();
+  const { user, logout, isAuthenticated, isLoading } = useAuth();
   const [health, setHealth] = useState<'checking' | 'healthy' | 'error'>('checking');
-  const [systemInfo, setSystemInfo] = useState<any>(null);
-
-  useEffect(() => {
-    // Only check health and system info if authenticated
-    if (isAuthenticated) {
-      checkHealth();
-      loadSystemInfo();
-    }
-  }, [isAuthenticated]);
+  const [systemInfo, setSystemInfo] = useState<{ version?: string; cuda_available?: boolean } | null>(null);
 
   const checkHealth = async () => {
     try {
       await apiClient.getHealth();
-      setHealth('healthy');
+      setTimeout(() => setHealth('healthy'), 0);
     } catch (err) {
-      setHealth('error');
+      console.error('Health check failed:', err);
+      setTimeout(() => setHealth('error'), 0);
     }
   };
 
   const loadSystemInfo = async () => {
     try {
       const info = await apiClient.getSystemInfo();
-      setSystemInfo(info);
+      setTimeout(() => setSystemInfo(info), 0);
     } catch (err) {
       console.error('Failed to load system info:', err);
     }
   };
 
+  useEffect(() => {
+    // Only check health and system info if authenticated
+    if (isAuthenticated) {
+      checkHealth();
+      loadSystemInfo();
+    }
+  }, [isAuthenticated]);
+
   // Show loading state while checking authentication
   if (isLoading) {
     return (

+ 4 - 0
webui/next.config.ts

@@ -8,6 +8,10 @@ const nextConfig: NextConfig = {
   images: {
     unoptimized: true,
   },
+  // Generate fallback pages for better routing
+  generateBuildId: async () => {
+    return 'stable-diffusion-ui'
+  },
 };
 
 export default nextConfig;