Explorar o código

refactor: complete authentication system overhaul

- Remove deprecated authentication methods (JWT, API Key, Unix, Optional)
- Implement shared key authentication with --shared-key argument
- Fix PAM authentication to work with OS credentials
- Add NONE authentication method as default (allows unrestricted access)
- Update command line arguments and help text
- Fix PAM initialization issue
- Update authentication middleware and server integration
- Refactor authentication flow for better security and usability
- Update related UI components to work with new auth system

Modified files:
- include/server_config.h: Update auth configuration structure
- src/main.cpp: Refactor command line argument parsing for auth
- src/auth_middleware.cpp: Simplify authentication logic
- include/auth_middleware.h: Update middleware interface
- src/user_manager.cpp: Remove deprecated auth methods
- src/server.cpp: Integrate new authentication system
- CMakeLists.txt: Update build configuration
- cmake/FindDependencies.cmake: Update dependency configuration
- include/user_manager.h: Update user management interface
- src/stable_diffusion_wrapper.cpp: Update wrapper for new auth
- webui/app/text2img/page.tsx: Update UI for new auth
- webui/components/features/models/model-status-bar.tsx: Update model status
- webui/lib/api.ts: Update API client for new auth
Fszontagh hai 3 meses
pai
achega
8049154a1e

+ 1 - 0
CMakeLists.txt

@@ -182,6 +182,7 @@ ExternalProject_Add(stable-diffusion.cpp
         -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
         -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR}/stable-diffusion.cpp-install
         -DSD_CUDA=${SD_CUDA_SUPPORT}
+        -DSD_BUILD_EXAMPLES=OFF
     BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${CMAKE_BUILD_TYPE}
     INSTALL_COMMAND ${CMAKE_COMMAND} --install . --config ${CMAKE_BUILD_TYPE}
     LOG_DOWNLOAD ON

+ 1 - 1
cmake/FindDependencies.cmake

@@ -25,7 +25,7 @@ if(NOT httplib_FOUND)
     FetchContent_Declare(
         httplib
         GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git
-        GIT_TAG v0.14.1
+        GIT_TAG v0.27.0
     )
     FetchContent_MakeAvailable(httplib)
     set(HTTPLIB_TARGET httplib)

+ 9 - 26
include/auth_middleware.h

@@ -37,7 +37,7 @@ struct AuthContext {
  * @brief Authentication middleware class
  *
  * This class provides authentication and authorization middleware for HTTP requests.
- * It supports multiple authentication methods (JWT, API keys, Unix auth) and
+ * It supports PAM and shared key authentication methods and
  * role-based access control.
  */
 class AuthMiddleware {
@@ -239,29 +239,22 @@ private:
      */
     AuthContext authenticateJwt(const httplib::Request& req);
 
-    /**
-     * @brief Authenticate using API key
-     *
-     * @param req HTTP request
-     * @return AuthContext Authentication context
-     */
-    AuthContext authenticateApiKey(const httplib::Request& req);
 
     /**
-     * @brief Authenticate using Unix system
+     * @brief Authenticate using PAM
      *
      * @param req HTTP request
      * @return AuthContext Authentication context
      */
-    AuthContext authenticateUnix(const httplib::Request& req);
+    AuthContext authenticatePam(const httplib::Request& req);
 
     /**
-     * @brief Authenticate using PAM
+     * @brief Authenticate using shared key
      *
      * @param req HTTP request
      * @return AuthContext Authentication context
      */
-    AuthContext authenticatePam(const httplib::Request& req);
+    AuthContext authenticateSharedKey(const httplib::Request& req);
 
     /**
      * @brief Extract token from request
@@ -360,24 +353,14 @@ namespace AuthMiddlewareFactory {
                                                   const std::string& dataDir);
 
     /**
-     * @brief Create authentication middleware with JWT only
-     *
-     * @param userManager User manager instance
-     * @param jwtSecret JWT secret key
-     * @param jwtExpirationMinutes JWT expiration in minutes
-     * @return std::unique_ptr<AuthMiddleware> Auth middleware instance
-     */
-    std::unique_ptr<AuthMiddleware> createJwtOnly(std::shared_ptr<UserManager> userManager,
-                                                   const std::string& jwtSecret,
-                                                   int jwtExpirationMinutes = 60);
-
-    /**
-     * @brief Create authentication middleware with API keys only
+     * @brief Create authentication middleware with shared key only
      *
      * @param userManager User manager instance
+     * @param sharedKey Shared key for authentication
      * @return std::unique_ptr<AuthMiddleware> Auth middleware instance
      */
-    std::unique_ptr<AuthMiddleware> createApiKeyOnly(std::shared_ptr<UserManager> userManager);
+    std::unique_ptr<AuthMiddleware> createSharedKeyOnly(std::shared_ptr<UserManager> userManager,
+                                                        const std::string& sharedKey);
 
     /**
      * @brief Create authentication middleware with multiple methods

+ 140 - 148
include/generation_queue.h

@@ -1,19 +1,13 @@
 #ifndef GENERATION_QUEUE_H
 #define GENERATION_QUEUE_H
 
-#include <string>
-#include <memory>
-#include <queue>
-#include <mutex>
-#include <condition_variable>
+#include <chrono>
 #include <functional>
 #include <future>
-#include <thread>
-#include <atomic>
-#include <unordered_map>
-#include <chrono>
-#include <vector>
 #include <map>
+#include <memory>
+#include <string>
+#include <vector>
 
 /**
  * @brief Job type enumeration
@@ -28,7 +22,7 @@ enum class JobType {
  * @brief Generation status enumeration
  */
 enum class GenerationStatus {
-    QUEUED,      ///< Request is queued and waiting to be processed
+    QUEUED,         ///< Request is queued and waiting to be processed
     MODEL_LOADING,  ///< Model is being loaded
     PROCESSING,     ///< Request is currently being processed
     COMPLETED,      ///< Request completed successfully
@@ -74,81 +68,81 @@ enum class Scheduler {
  */
 struct GenerationRequest {
     // Basic parameters
-    std::string id;                    ///< Unique request ID
-    std::string modelName;             ///< Name of the model to use
-    std::string prompt;                ///< Text prompt for generation
-    std::string negativePrompt;        ///< Negative prompt (optional)
+    std::string id;              ///< Unique request ID
+    std::string modelName;       ///< Name of the model to use
+    std::string prompt;          ///< Text prompt for generation
+    std::string negativePrompt;  ///< Negative prompt (optional)
 
     // Image parameters
-    int width = 512;                  ///< Image width
-    int height = 512;                 ///< Image height
-    int batchCount = 1;               ///< Number of images to generate
+    int width      = 512;  ///< Image width
+    int height     = 512;  ///< Image height
+    int batchCount = 1;    ///< Number of images to generate
 
     // Sampling parameters
-    int steps = 20;                   ///< Number of diffusion steps
-    float cfgScale = 7.5f;            ///< CFG scale
+    int steps                     = 20;                       ///< Number of diffusion steps
+    float cfgScale                = 7.5f;                     ///< CFG scale
     SamplingMethod samplingMethod = SamplingMethod::DEFAULT;  ///< Sampling method
-    Scheduler scheduler = Scheduler::DEFAULT;                 ///< Scheduler
+    Scheduler scheduler           = Scheduler::DEFAULT;       ///< Scheduler
 
     // Seed control
-    std::string seed = "42";           ///< Seed for generation ("random" for random)
+    std::string seed = "42";  ///< Seed for generation ("random" for random)
 
     // Model paths (for advanced usage)
-    std::string clipLPath;            ///< Path to CLIP-L model
-    std::string clipGPath;            ///< Path to CLIP-G model
-    std::string clipVisionPath;        ///< Path to CLIP-Vision model
-    std::string t5xxlPath;            ///< Path to T5-XXL model
-    std::string qwen2vlPath;          ///< Path to Qwen2VL model
-    std::string qwen2vlVisionPath;     ///< Path to Qwen2VL Vision model
-    std::string diffusionModelPath;     ///< Path to standalone diffusion model
-    std::string vaePath;               ///< Path to VAE model
-    std::string taesdPath;             ///< Path to TAESD model
-    std::string controlNetPath;        ///< Path to ControlNet model
-    std::string embeddingDir;         ///< Path to embeddings directory
-    std::string loraModelDir;         ///< Path to LoRA model directory
+    std::string clipLPath;           ///< Path to CLIP-L model
+    std::string clipGPath;           ///< Path to CLIP-G model
+    std::string clipVisionPath;      ///< Path to CLIP-Vision model
+    std::string t5xxlPath;           ///< Path to T5-XXL model
+    std::string qwen2vlPath;         ///< Path to Qwen2VL model
+    std::string qwen2vlVisionPath;   ///< Path to Qwen2VL Vision model
+    std::string diffusionModelPath;  ///< Path to standalone diffusion model
+    std::string vaePath;             ///< Path to VAE model
+    std::string taesdPath;           ///< Path to TAESD model
+    std::string controlNetPath;      ///< Path to ControlNet model
+    std::string embeddingDir;        ///< Path to embeddings directory
+    std::string loraModelDir;        ///< Path to LoRA model directory
 
     // Advanced parameters
-    int clipSkip = -1;                ///< CLIP skip layers
+    int clipSkip                = -1;         ///< CLIP skip layers
     std::vector<int> skipLayers = {7, 8, 9};  ///< Layers to skip for SLG
-    float strength = 0.75f;            ///< Strength for img2img
-    float controlStrength = 0.9f;      ///< ControlNet strength
+    float strength              = 0.75f;      ///< Strength for img2img
+    float controlStrength       = 0.9f;       ///< ControlNet strength
 
     // Performance parameters
-    int nThreads = -1;                ///< Number of threads (-1 for auto)
-    bool offloadParamsToCpu = false;  ///< Offload parameters to CPU
-    bool clipOnCpu = false;          ///< Keep CLIP on CPU
-    bool vaeOnCpu = false;            ///< Keep VAE on CPU
-    bool diffusionFlashAttn = false;   ///< Use flash attention
+    int nThreads             = -1;     ///< Number of threads (-1 for auto)
+    bool offloadParamsToCpu  = false;  ///< Offload parameters to CPU
+    bool clipOnCpu           = false;  ///< Keep CLIP on CPU
+    bool vaeOnCpu            = false;  ///< Keep VAE on CPU
+    bool diffusionFlashAttn  = false;  ///< Use flash attention
     bool diffusionConvDirect = false;  ///< Use direct convolution
-    bool vaeConvDirect = false;       ///< Use direct VAE convolution
+    bool vaeConvDirect       = false;  ///< Use direct VAE convolution
 
     // Output parameters
-    std::string outputPath;            ///< Output path for generated images
+    std::string outputPath;  ///< Output path for generated images
 
     // Image-to-image parameters
-    std::string initImagePath;         ///< Path to init image for img2img (can be file path or base64)
-    std::vector<uint8_t> initImageData; ///< Init image data (decoded)
-    int initImageWidth = 0;            ///< Init image width
-    int initImageHeight = 0;           ///< Init image height
-    int initImageChannels = 3;         ///< Init image channels
+    std::string initImagePath;           ///< Path to init image for img2img (can be file path or base64)
+    std::vector<uint8_t> initImageData;  ///< Init image data (decoded)
+    int initImageWidth    = 0;           ///< Init image width
+    int initImageHeight   = 0;           ///< Init image height
+    int initImageChannels = 3;           ///< Init image channels
 
     // ControlNet parameters
-    std::string controlImagePath;      ///< Path to control image for ControlNet
-    std::vector<uint8_t> controlImageData; ///< Control image data (decoded)
-    int controlImageWidth = 0;         ///< Control image width
-    int controlImageHeight = 0;        ///< Control image height
-    int controlImageChannels = 3;      ///< Control image channels
+    std::string controlImagePath;           ///< Path to control image for ControlNet
+    std::vector<uint8_t> controlImageData;  ///< Control image data (decoded)
+    int controlImageWidth    = 0;           ///< Control image width
+    int controlImageHeight   = 0;           ///< Control image height
+    int controlImageChannels = 3;           ///< Control image channels
 
     // Upscaler parameters
-    std::string esrganPath;            ///< Path to ESRGAN model for upscaling
-    uint32_t upscaleFactor = 4;        ///< Upscale factor (2 or 4)
+    std::string esrganPath;      ///< Path to ESRGAN model for upscaling
+    uint32_t upscaleFactor = 4;  ///< Upscale factor (2 or 4)
 
     // Inpainting parameters
-    std::string maskImagePath;          ///< Path to mask image for inpainting
-    std::vector<uint8_t> maskImageData; ///< Mask image data (decoded)
-    int maskImageWidth = 0;             ///< Mask image width
-    int maskImageHeight = 0;            ///< Mask image height
-    int maskImageChannels = 1;          ///< Mask image channels (grayscale)
+    std::string maskImagePath;           ///< Path to mask image for inpainting
+    std::vector<uint8_t> maskImageData;  ///< Mask image data (decoded)
+    int maskImageWidth    = 0;           ///< Mask image width
+    int maskImageHeight   = 0;           ///< Mask image height
+    int maskImageChannels = 1;           ///< Mask image channels (grayscale)
 
     // Request type
     enum class RequestType {
@@ -160,135 +154,134 @@ struct GenerationRequest {
     } requestType = RequestType::TEXT2IMG;
 
     // Callback for completion
-    std::function<void(const std::string&, const std::string&)> callback; ///< Callback for completion
+    std::function<void(const std::string&, const std::string&)> callback;  ///< Callback for completion
 };
 
 /**
  * @brief Generation result structure
  */
 struct GenerationResult {
-    std::string requestId;             ///< ID of the original request
-    GenerationStatus status;            ///< Final status of the generation
-    bool success;                      ///< Whether generation was successful
-    std::vector<std::string> imagePaths; ///< Paths to generated images (multiple for batch)
-    std::string errorMessage;          ///< Error message if generation failed
-    uint64_t generationTime;           ///< Time taken for generation in milliseconds
-    int64_t actualSeed;               ///< Actual seed used for generation
+    std::string requestId;                ///< ID of the original request
+    GenerationStatus status;              ///< Final status of the generation
+    bool success;                         ///< Whether generation was successful
+    std::vector<std::string> imagePaths;  ///< Paths to generated images (multiple for batch)
+    std::string errorMessage;             ///< Error message if generation failed
+    uint64_t generationTime;              ///< Time taken for generation in milliseconds
+    int64_t actualSeed;                   ///< Actual seed used for generation
 };
 
 /**
  * @brief Hash request structure for model hashing jobs
  */
 struct HashRequest {
-    std::string id;                    ///< Unique request ID
-    std::vector<std::string> modelNames; ///< Model names to hash (empty = hash all unhashed)
-    bool forceRehash = false;          ///< Force rehash even if hash exists
+    std::string id;                       ///< Unique request ID
+    std::vector<std::string> modelNames;  ///< Model names to hash (empty = hash all unhashed)
+    bool forceRehash = false;             ///< Force rehash even if hash exists
 };
 
 /**
  * @brief Hash result structure
  */
 struct HashResult {
-    std::string requestId;             ///< ID of the original request
-    GenerationStatus status;            ///< Final status
-    bool success;                      ///< Whether hashing was successful
-    std::map<std::string, std::string> modelHashes; ///< Map of model names to their hashes
-    std::string errorMessage;          ///< Error message if hashing failed
-    uint64_t hashingTime;              ///< Time taken for hashing in milliseconds
-    int modelsHashed;                  ///< Number of models successfully hashed
+    std::string requestId;                           ///< ID of the original request
+    GenerationStatus status;                         ///< Final status
+    bool success;                                    ///< Whether hashing was successful
+    std::map<std::string, std::string> modelHashes;  ///< Map of model names to their hashes
+    std::string errorMessage;                        ///< Error message if hashing failed
+    uint64_t hashingTime;                            ///< Time taken for hashing in milliseconds
+    int modelsHashed;                                ///< Number of models successfully hashed
 };
 
 /**
  * @brief Conversion request structure for model quantization/conversion jobs
  */
 struct ConversionRequest {
-    std::string id;                    ///< Unique request ID
-    std::string modelName;             ///< Model name to convert
-    std::string modelPath;             ///< Full path to model file
-    std::string outputPath;            ///< Output path for converted model
-    std::string quantizationType;      ///< Quantization type (f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K)
+    std::string id;                ///< Unique request ID
+    std::string modelName;         ///< Model name to convert
+    std::string modelPath;         ///< Full path to model file
+    std::string outputPath;        ///< Output path for converted model
+    std::string quantizationType;  ///< Quantization type (f32, f16, q4_0, q4_1, q5_0, q5_1, q8_0, q2_K, q3_K, q4_K)
 };
 
 /**
  * @brief Conversion result structure
  */
 struct ConversionResult {
-    std::string requestId;             ///< ID of the original request
-    GenerationStatus status;            ///< Final status
-    bool success;                      ///< Whether conversion was successful
-    std::string outputPath;            ///< Path to converted model file
-    std::string errorMessage;          ///< Error message if conversion failed
-    uint64_t conversionTime;           ///< Time taken for conversion in milliseconds
-    std::string originalSize;          ///< Original model file size
-    std::string convertedSize;         ///< Converted model file size
+    std::string requestId;      ///< ID of the original request
+    GenerationStatus status;    ///< Final status
+    bool success;               ///< Whether conversion was successful
+    std::string outputPath;     ///< Path to converted model file
+    std::string errorMessage;   ///< Error message if conversion failed
+    uint64_t conversionTime;    ///< Time taken for conversion in milliseconds
+    std::string originalSize;   ///< Original model file size
+    std::string convertedSize;  ///< Converted model file size
 };
 
 /**
  * @brief Job information for queue status
  */
 struct JobInfo {
-    std::string id;                   ///< Job ID
-    JobType type;                      ///< Job type (generation or hashing)
-    GenerationStatus status;            ///< Current status
-    std::string prompt;                ///< Job prompt (full text for generation, or model name for hashing)
+    std::string id;                                    ///< Job ID
+    JobType type;                                      ///< Job type (generation or hashing)
+    GenerationStatus status;                           ///< Current status
+    std::string prompt;                                ///< Job prompt (full text for generation, or model name for hashing)
     std::chrono::system_clock::time_point queuedTime;  ///< When job was queued
     std::chrono::system_clock::time_point startTime;   ///< When job started processing
     std::chrono::system_clock::time_point endTime;     ///< When job completed/failed
-    int position;                      ///< Position in queue (for queued jobs)
-    std::vector<std::string> outputFiles; ///< Paths to generated output files
-    std::string errorMessage;          ///< Error message if job failed
-    float progress = 0.0f;             ///< Overall progress (0.0 to 1.0)
-    float modelLoadProgress = 0.0f;    ///< Model loading progress (0.0 to 1.0)
-    float generationProgress = 0.0f;   ///< Generation progress (0.0 to 1.0)
-    int currentStep = 0;               ///< Current step in generation
-    int totalSteps = 0;                ///< Total steps in generation
-    int64_t timeElapsed = 0;           ///< Time elapsed in milliseconds
-    int64_t timeRemaining = 0;         ///< Estimated time remaining in milliseconds
-    float speed = 0.0f;                ///< Generation speed in steps per second
-    bool firstGenerationCallback = true; ///< Flag to track if this is the first generation callback
+    int position;                                      ///< Position in queue (for queued jobs)
+    std::vector<std::string> outputFiles;              ///< Paths to generated output files
+    std::string errorMessage;                          ///< Error message if job failed
+    float progress               = 0.0f;               ///< Overall progress (0.0 to 1.0) - kept for backward compatibility
+    float modelLoadProgress      = 0.0f;               ///< Model loading progress (0.0 to 1.0)
+    int currentStep              = 0;                  ///< Current step in generation
+    int totalSteps               = 0;                  ///< Total steps in generation
+    int64_t timeElapsed          = 0;                  ///< Time elapsed in milliseconds
+    int64_t timeRemaining        = 0;                  ///< Estimated time remaining in milliseconds
+    float speed                  = 0.0f;               ///< Generation speed in steps per second
+    bool firstGenerationCallback = true;               ///< Flag to track if this is the first generation callback
 
     // Enhanced fields for repeatable generation
-    std::string modelName;             ///< Name of the model used
-    std::string modelHash;             ///< SHA256 hash of the model
-    std::string modelPath;             ///< Full path to the model file
-    std::string negativePrompt;        ///< Negative prompt used
-    int width = 512;                  ///< Image width
-    int height = 512;                 ///< Image height
-    int batchCount = 1;               ///< Number of images generated
-    int steps = 20;                   ///< Number of diffusion steps
-    float cfgScale = 7.5f;            ///< CFG scale
+    std::string modelName;                                    ///< Name of the model used
+    std::string modelHash;                                    ///< SHA256 hash of the model
+    std::string modelPath;                                    ///< Full path to the model file
+    std::string negativePrompt;                               ///< Negative prompt used
+    int width                     = 512;                      ///< Image width
+    int height                    = 512;                      ///< Image height
+    int batchCount                = 1;                        ///< Number of images generated
+    int steps                     = 20;                       ///< Number of diffusion steps
+    float cfgScale                = 7.5f;                     ///< CFG scale
     SamplingMethod samplingMethod = SamplingMethod::DEFAULT;  ///< Sampling method used
-    Scheduler scheduler = Scheduler::DEFAULT;                 ///< Scheduler used
-    std::string seed;                 ///< Seed used for generation
-    int64_t actualSeed = 0;           ///< Actual seed that was used (for random seeds)
-    std::string requestType;          ///< Request type: text2img, img2img, controlnet, upscaler, inpainting
-    float strength = 0.75f;           ///< Strength for img2img
-    float controlStrength = 0.9f;     ///< ControlNet strength
-    int clipSkip = -1;                ///< CLIP skip layers
-    int nThreads = -1;                ///< Number of threads used
-    bool offloadParamsToCpu = false;  ///< Offload parameters to CPU setting
-    bool clipOnCpu = false;          ///< Keep CLIP on CPU setting
-    bool vaeOnCpu = false;            ///< Keep VAE on CPU setting
-    bool diffusionFlashAttn = false;   ///< Use flash attention setting
-    bool diffusionConvDirect = false;  ///< Use direct convolution setting
-    bool vaeConvDirect = false;       ///< Use direct VAE convolution setting
-    uint64_t generationTime = 0;      ///< Total generation time in milliseconds
+    Scheduler scheduler           = Scheduler::DEFAULT;       ///< Scheduler used
+    std::string seed;                                         ///< Seed used for generation
+    int64_t actualSeed = 0;                                   ///< Actual seed that was used (for random seeds)
+    std::string requestType;                                  ///< Request type: text2img, img2img, controlnet, upscaler, inpainting
+    float strength           = 0.75f;                         ///< Strength for img2img
+    float controlStrength    = 0.9f;                          ///< ControlNet strength
+    int clipSkip             = -1;                            ///< CLIP skip layers
+    int nThreads             = -1;                            ///< Number of threads used
+    bool offloadParamsToCpu  = false;                         ///< Offload parameters to CPU setting
+    bool clipOnCpu           = false;                         ///< Keep CLIP on CPU setting
+    bool vaeOnCpu            = false;                         ///< Keep VAE on CPU setting
+    bool diffusionFlashAttn  = false;                         ///< Use flash attention setting
+    bool diffusionConvDirect = false;                         ///< Use direct convolution setting
+    bool vaeConvDirect       = false;                         ///< Use direct VAE convolution setting
+    uint64_t generationTime  = 0;                             ///< Total generation time in milliseconds
 
     // Image data for complex operations (base64 encoded)
-    std::string initImageData;        ///< Init image data for img2img (base64)
-    std::string controlImageData;     ///< Control image data for ControlNet (base64)
-    std::string maskImageData;        ///< Mask image data for inpainting (base64)
+    std::string initImageData;     ///< Init image data for img2img (base64)
+    std::string controlImageData;  ///< Control image data for ControlNet (base64)
+    std::string maskImageData;     ///< Mask image data for inpainting (base64)
 
     // Model paths for advanced usage
-    std::string clipLPath;            ///< Path to CLIP-L model
-    std::string clipGPath;            ///< Path to CLIP-G model
-    std::string vaePath;              ///< Path to VAE model
-    std::string taesdPath;            ///< Path to TAESD model
-    std::string controlNetPath;       ///< Path to ControlNet model
-    std::string embeddingDir;         ///< Path to embeddings directory
-    std::string loraModelDir;         ///< Path to LoRA model directory
-    std::string esrganPath;           ///< Path to ESRGAN model for upscaling
-    uint32_t upscaleFactor = 4;       ///< Upscale factor used
+    std::string clipLPath;       ///< Path to CLIP-L model
+    std::string clipGPath;       ///< Path to CLIP-G model
+    std::string vaePath;         ///< Path to VAE model
+    std::string taesdPath;       ///< Path to TAESD model
+    std::string controlNetPath;  ///< Path to ControlNet model
+    std::string embeddingDir;    ///< Path to embeddings directory
+    std::string loraModelDir;    ///< Path to LoRA model directory
+    std::string esrganPath;      ///< Path to ESRGAN model for upscaling
+    uint32_t upscaleFactor = 4;  ///< Upscale factor used
 };
 
 /**
@@ -308,8 +301,7 @@ public:
      * @param queueDir Directory to store job persistence files
      * @param outputDir Directory to store generated output files
      */
-    explicit GenerationQueue(class ModelManager* modelManager, int maxConcurrentGenerations = 1,
-                           const std::string& queueDir = "./queue", const std::string& outputDir = "./output");
+    explicit GenerationQueue(class ModelManager* modelManager, int maxConcurrentGenerations = 1, const std::string& queueDir = "./queue", const std::string& outputDir = "./output");
 
     /**
      * @brief Destroy the Generation Queue object
@@ -408,7 +400,7 @@ public:
 
 private:
     class Impl;
-    std::unique_ptr<Impl> pImpl; // Pimpl idiom
+    std::unique_ptr<Impl> pImpl;  // Pimpl idiom
 };
 
-#endif // GENERATION_QUEUE_H
+#endif  // GENERATION_QUEUE_H

+ 5 - 11
include/server_config.h

@@ -8,12 +8,9 @@
  * @brief Authentication method enumeration
  */
 enum class AuthMethod {
-    NONE,     ///< No authentication required
-    JWT,      ///< JWT token authentication
-    API_KEY,  ///< API key authentication
-    UNIX,     ///< Unix system authentication
-    PAM,      ///< PAM authentication
-    OPTIONAL  ///< Authentication optional (guest access allowed)
+    NONE,       ///< No authentication (allow all access)
+    PAM,        ///< PAM authentication
+    SHARED_KEY  ///< Shared key authentication
 };
 
 /**
@@ -22,12 +19,9 @@ enum class AuthMethod {
 struct AuthConfig {
     AuthMethod authMethod      = AuthMethod::NONE;         ///< Primary authentication method
     bool enableGuestAccess     = false;                    ///< Allow unauthenticated access
-    std::string jwtSecret      = "";                       ///< JWT secret key (auto-generated if empty)
-    int jwtExpirationMinutes   = 60;                       ///< JWT token expiration in minutes
-    std::string authRealm      = "stable-diffusion-rest";  ///< Authentication realm
-    std::string dataDir        = "./auth";                 ///< Directory for authentication data
+    std::string sharedKey      = "";                       ///< Shared key for authentication
     std::string pamServiceName = "stable-diffusion-rest";  ///< PAM service name
-    std::string jwtAudience    = "stable-diffusion-rest";  ///< JWT audience claim
+    std::string dataDir        = "./auth";                 ///< Directory for authentication data
     std::vector<std::string> publicPaths;                  ///< Paths that don't require authentication
     std::vector<std::string> adminPaths;                   ///< Paths that require admin access
     std::vector<std::string> userPaths;                    ///< Paths that require user access

+ 5 - 9
include/user_manager.h

@@ -72,12 +72,8 @@ public:
      * @brief Authentication methods enumeration
      */
     enum class AuthMethod {
-        NONE,           ///< No authentication required
-        JWT,            ///< JWT token authentication
-        API_KEY,        ///< API key authentication
-        UNIX,           ///< Unix system authentication
         PAM,            ///< PAM authentication
-        OPTIONAL        ///< Authentication optional (guest access allowed)
+        SHARED_KEY      ///< Shared key authentication
     };
 
     /**
@@ -112,7 +108,7 @@ public:
      * @param defaultAdminEmail Default admin email
      */
     explicit UserManager(const std::string& dataDir,
-                        AuthMethod authMethod = AuthMethod::JWT,
+                        AuthMethod authMethod = AuthMethod::PAM,
                         const std::string& defaultAdminUsername = "admin",
                         const std::string& defaultAdminPassword = "admin123",
                         const std::string& defaultAdminEmail = "admin@localhost");
@@ -162,12 +158,12 @@ public:
     AuthResult authenticatePam(const std::string& username, const std::string& password);
 
     /**
-     * @brief Authenticate with API key
+     * @brief Authenticate with shared key
      *
-     * @param apiKey API key string
+     * @param sharedKey Shared key string
      * @return AuthResult Authentication result
      */
-    AuthResult authenticateApiKey(const std::string& apiKey);
+    AuthResult authenticateSharedKey(const std::string& sharedKey);
 
     /**
      * @brief Create a new user

+ 33 - 191
src/auth_middleware.cpp

@@ -21,13 +21,7 @@ bool AuthMiddleware::initialize() {
             return false;
         }
 
-        // Initialize JWT auth if needed
-        if (m_config.authMethod == AuthMethod::JWT) {
-            m_jwtAuth = std::make_unique<JWTAuth>(m_config.jwtSecret,
-                                                  m_config.jwtExpirationMinutes,
-                                                  m_config.authRealm,
-                                                  m_config.jwtAudience);
-        }
+        // No JWT initialization needed for PAM and shared key auth
 
         // Initialize default paths
         initializeDefaultPaths();
@@ -60,30 +54,17 @@ AuthContext AuthMiddleware::authenticate(const httplib::Request& req, httplib::R
 
         // Try different authentication methods based on configuration
         switch (m_config.authMethod) {
-            case AuthMethod::JWT:
-                context = authenticateJwt(req);
-                break;
-            case AuthMethod::API_KEY:
-                context = authenticateApiKey(req);
-                break;
-            case AuthMethod::UNIX:
-                context = authenticateUnix(req);
+            case AuthMethod::NONE:
+                context               = createGuestContext();
+                context.authenticated = true;
+                context.authMethod    = "NONE";
                 break;
             case AuthMethod::PAM:
                 context = authenticatePam(req);
                 break;
-            case AuthMethod::OPTIONAL:
-                // Try JWT first, then API key, then allow guest
-                context = authenticateJwt(req);
-                if (!context.authenticated) {
-                    context = authenticateApiKey(req);
-                }
-                if (!context.authenticated && m_config.enableGuestAccess) {
-                    context               = createGuestContext();
-                    context.authenticated = true;
-                }
+            case AuthMethod::SHARED_KEY:
+                context = authenticateSharedKey(req);
                 break;
-            case AuthMethod::NONE:
             default:
                 context               = createGuestContext();
                 context.authenticated = true;
@@ -174,7 +155,7 @@ void AuthMiddleware::sendAuthError(httplib::Response& res,
         {"error", {{"message", message}, {"code", errorCode}, {"timestamp", std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()}}}};
 
     res.set_header("Content-Type", "application/nlohmann::json");
-    res.set_header("WWW-Authenticate", "Bearer realm=\"" + m_config.authRealm + "\"");
+    res.set_header("WWW-Authenticate", "Bearer realm=\"stable-diffusion-rest\"");
     res.status = statusCode;
     res.body   = error.dump();
 }
@@ -202,44 +183,9 @@ void AuthMiddleware::addUserPath(const std::string& path) {
     m_config.userPaths.push_back(path);
 }
 
-AuthContext AuthMiddleware::authenticateApiKey(const httplib::Request& req) {
-    AuthContext context;
-    context.authenticated = false;
-
-    if (!m_userManager) {
-        context.errorMessage = "User manager not available";
-        context.errorCode    = "USER_MANAGER_UNAVAILABLE";
-        return context;
-    }
-
-    // Extract API key from header
-    std::string apiKey = extractToken(req, "X-API-Key");
-    if (apiKey.empty()) {
-        context.errorMessage = "Missing API key";
-        context.errorCode    = "MISSING_API_KEY";
-        return context;
-    }
-
-    // Validate API key
-    auto result = m_userManager->authenticateApiKey(apiKey);
-    if (!result.success) {
-        context.errorMessage = result.errorMessage;
-        context.errorCode    = result.errorCode;
-        return context;
-    }
-
-    // API key is valid
-    context.authenticated = true;
-    context.userId        = result.userId;
-    context.username      = result.username;
-    context.role          = result.role;
-    context.permissions   = result.permissions;
-    context.authMethod    = "API_KEY";
 
-    return context;
-}
 
-AuthContext AuthMiddleware::authenticateUnix(const httplib::Request& req) {
+AuthContext AuthMiddleware::authenticateSharedKey(const httplib::Request& req) {
     AuthContext context;
     context.authenticated = false;
 
@@ -249,83 +195,36 @@ AuthContext AuthMiddleware::authenticateUnix(const httplib::Request& req) {
         return context;
     }
 
-    // Check if Unix authentication is the configured method
-    if (m_config.authMethod != AuthMethod::UNIX) {
-        context.errorMessage = "Unix authentication not available";
-        context.errorCode    = "UNIX_AUTH_UNAVAILABLE";
+    // Check if shared key authentication is the configured method
+    if (m_config.authMethod != AuthMethod::SHARED_KEY) {
+        context.errorMessage = "Shared key authentication not available";
+        context.errorCode    = "SHARED_KEY_AUTH_UNAVAILABLE";
         return context;
     }
 
-    // For Unix auth, we need to get username and password from request
-    std::string username;
-    std::string password;
-
-    // Try to extract from JSON body (for login API)
-    std::string contentType = req.get_header_value("Content-Type");
-    if (contentType.find("application/nlohmann::json") != std::string::npos) {
-        try {
-            nlohmann::json body = nlohmann::json::parse(req.body);
-            username            = body.value("username", "");
-            password            = body.value("password", "");
-        } catch (const nlohmann::json::exception& e) {
-            // Invalid JSON, continue with other methods
-        }
-    }
-
-    // If no credentials in body, check headers
-    if (username.empty()) {
-        username = req.get_header_value("X-Unix-User");
-
-        // Also check Authorization header for Bearer token (for UI requests after login)
-        if (username.empty()) {
-            std::string authHeader = req.get_header_value("Authorization");
-            if (!authHeader.empty() && authHeader.find("Bearer ") == 0) {
-                std::string token = authHeader.substr(7);  // Remove "Bearer "
-                // Check if this is a Unix token
-                if (token.find("unix_token_") == 0) {
-                    // Extract username from token
-                    size_t lastUnderscore = token.find_last_of('_');
-                    if (lastUnderscore != std::string::npos) {
-                        username = token.substr(lastUnderscore + 1);
-                    }
-                }
-            }
-        }
-    }
-
-    if (username.empty()) {
-        // Check if this is a request for the login page or API endpoints
-        // For UI requests, we'll let the UI handler show the login page
-        // For API requests, we need to return an error
-        std::string path = req.path;
-        if (path.find("/ui/") == 0 || path == "/ui") {
-            // This is a UI request, let it proceed to show the login page
-            context               = createGuestContext();
-            context.authenticated = false;  // Ensure it's false to trigger login page
-            return context;
-        } else {
-            // This is an API request, return error
-            context.errorMessage = "Missing Unix username";
-            context.errorCode    = "MISSING_UNIX_USER";
-            return context;
-        }
+    // Get shared key from X-Shared-Key header
+    std::string sharedKey = req.get_header_value("X-Shared-Key");
+    if (sharedKey.empty()) {
+        context.errorMessage = "Missing shared key";
+        context.errorCode    = "MISSING_SHARED_KEY";
+        return context;
     }
 
-    // Authenticate Unix user (with or without password depending on PAM availability)
-    auto result = m_userManager->authenticateUnix(username, password);
+    // Authenticate with shared key
+    auto result = m_userManager->authenticateSharedKey(sharedKey);
     if (!result.success) {
         context.errorMessage = result.errorMessage;
         context.errorCode    = result.errorCode;
         return context;
     }
 
-    // Unix authentication successful
+    // Shared key authentication successful
     context.authenticated = true;
     context.userId        = result.userId;
     context.username      = result.username;
     context.role          = result.role;
     context.permissions   = result.permissions;
-    context.authMethod    = "UNIX";
+    context.authMethod    = "SHARED_KEY";
 
     return context;
 }
@@ -482,20 +381,14 @@ std::string AuthMiddleware::getUserAgent(const httplib::Request& req) {
 }
 
 bool AuthMiddleware::validateConfig(AuthConfig config) {
-    // Validate JWT configuration
-    if (config.authMethod == AuthMethod::JWT) {
-        if (config.jwtSecret.empty()) {
-            // Will be auto-generated
-        }
-        if (config.jwtExpirationMinutes <= 0 || config.jwtExpirationMinutes > 1440) {
-            return false;  // Max 24 hours
+    // Validate configuration for PAM and shared key
+    if (config.authMethod == AuthMethod::SHARED_KEY) {
+        if (config.sharedKey.empty()) {
+            return false;  // Shared key must be provided
         }
     }
 
     // Validate realm
-    if (config.authRealm.empty()) {
-        return false;
-    }
 
     return true;
 }
@@ -561,42 +454,6 @@ bool AuthMiddleware::isAuthenticationDisabled() const {
     return m_config.authMethod == AuthMethod::NONE;
 }
 
-AuthContext AuthMiddleware::authenticateJwt(const httplib::Request& req) {
-    AuthContext context;
-    context.authenticated = false;
-
-    if (!m_jwtAuth) {
-        context.errorMessage = "JWT authentication not available";
-        context.errorCode    = "JWT_AUTH_UNAVAILABLE";
-        return context;
-    }
-
-    // Extract JWT token from Authorization header
-    std::string token = extractToken(req, "Authorization");
-    if (token.empty()) {
-        context.errorMessage = "Missing JWT token";
-        context.errorCode    = "MISSING_JWT_TOKEN";
-        return context;
-    }
-
-    // Validate JWT token
-    auto result = m_jwtAuth->validateToken(token);
-    if (!result.success) {
-        context.errorMessage = result.errorMessage;
-        context.errorCode    = "INVALID_JWT_TOKEN";
-        return context;
-    }
-
-    // JWT is valid
-    context.authenticated = true;
-    context.userId        = result.userId;
-    context.username      = result.username;
-    context.role          = result.role;
-    context.permissions   = result.permissions;
-    context.authMethod    = "JWT";
-
-    return context;
-}
 
 std::vector<std::string> AuthMiddleware::getRequiredPermissions(const std::string& path) const {
     std::vector<std::string> permissions;
@@ -646,11 +503,8 @@ namespace AuthMiddlewareFactory {
     std::unique_ptr<AuthMiddleware> createDefault(std::shared_ptr<UserManager> userManager,
                                                   const std::string& /*dataDir*/) {
         AuthConfig config;
-        config.authMethod           = AuthMethod::NONE;
-        config.enableGuestAccess    = true;
-        config.jwtSecret            = "";
-        config.jwtExpirationMinutes = 60;
-        config.authRealm            = "stable-diffusion-rest";
+        config.authMethod           = AuthMethod::PAM;
+        config.enableGuestAccess    = false;
 
         return std::make_unique<AuthMiddleware>(config, userManager);
     }
@@ -660,24 +514,12 @@ namespace AuthMiddlewareFactory {
         return std::make_unique<AuthMiddleware>(std::move(config), userManager);
     }
 
-    std::unique_ptr<AuthMiddleware> createJwtOnly(std::shared_ptr<UserManager> userManager,
-                                                  const std::string& jwtSecret,
-                                                  int jwtExpirationMinutes) {
-        AuthConfig config;
-        config.authMethod           = AuthMethod::JWT;
-        config.enableGuestAccess    = false;
-        config.jwtSecret            = jwtSecret;
-        config.jwtExpirationMinutes = jwtExpirationMinutes;
-        config.authRealm            = "stable-diffusion-rest";
-
-        return std::make_unique<AuthMiddleware>(config, userManager);
-    }
-
-    std::unique_ptr<AuthMiddleware> createApiKeyOnly(std::shared_ptr<UserManager> userManager) {
+    std::unique_ptr<AuthMiddleware> createSharedKeyOnly(std::shared_ptr<UserManager> userManager,
+                                                        const std::string& sharedKey) {
         AuthConfig config;
-        config.authMethod        = AuthMethod::API_KEY;
+        config.authMethod        = AuthMethod::SHARED_KEY;
+        config.sharedKey         = sharedKey;
         config.enableGuestAccess = false;
-        config.authRealm         = "stable-diffusion-rest";
 
         return std::make_unique<AuthMiddleware>(config, userManager);
     }

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 342 - 268
src/generation_queue.cpp


+ 20 - 49
src/main.cpp

@@ -132,34 +132,16 @@ ServerConfig parseArguments(int argc, char* argv[]) {
             std::string method = argv[++i];
             if (method == "none") {
                 config.auth.authMethod = AuthMethod::NONE;
-            } else if (method == "jwt") {
-                config.auth.authMethod = AuthMethod::JWT;
-            } else if (method == "api-key") {
-                config.auth.authMethod = AuthMethod::API_KEY;
-            } else if (method == "unix") {
-                config.auth.authMethod = AuthMethod::UNIX;
             } else if (method == "pam") {
                 config.auth.authMethod = AuthMethod::PAM;
-            } else if (method == "optional") {
-                config.auth.authMethod = AuthMethod::OPTIONAL;
+            } else if (method == "shared-key") {
+                config.auth.authMethod = AuthMethod::SHARED_KEY;
             } else {
-                LOG_ERROR("Invalid auth method: " + method);
+                LOG_ERROR("Invalid auth method: " + method + ". Supported methods: none, pam, shared-key");
                 exit(1);
             }
-        } else if (arg == "--jwt-secret" && i + 1 < argc) {
-            config.auth.jwtSecret = argv[++i];
-        } else if (arg == "--jwt-expiration" && i + 1 < argc) {
-            config.auth.jwtExpirationMinutes = std::stoi(argv[++i]);
-        } else if (arg == "--enable-guest-access") {
-            config.auth.enableGuestAccess = true;
-        } else if (arg == "--enable-unix-auth") {
-            // Deprecated flag - show warning and set auth method to UNIX
-            LOG_WARNING("Warning: --enable-unix-auth is deprecated. Use --auth unix instead.");
-            config.auth.authMethod = AuthMethod::UNIX;
-        } else if (arg == "--enable-pam-auth") {
-            // Deprecated flag - show warning and set auth method to PAM
-            LOG_WARNING("Warning: --enable-pam-auth is deprecated. Use --auth pam instead.");
-            config.auth.authMethod = AuthMethod::PAM;
+        } else if (arg == "--shared-key" && i + 1 < argc) {
+            config.auth.sharedKey = argv[++i];
         } else if (arg == "--pam-service-name" && i + 1 < argc) {
             config.auth.pamServiceName = argv[++i];
         } else if (arg == "--auth-data-dir" && i + 1 < argc) {
@@ -186,8 +168,6 @@ ServerConfig parseArguments(int argc, char* argv[]) {
             config.defaultAdminEmail = argv[++i];
         } else if (arg == "--hash-all-models") {
             config.hashAllModels = true;
-        } else if (arg == "--jwt-audience" && i + 1 < argc) {
-            config.auth.jwtAudience = argv[++i];
         } else if (arg == "--help" || arg == "-h") {
             std::cout << "stable-diffusion.cpp-rest server" << std::endl;
             std::cout << "Usage: " << std::string(argv[0]) << " [options]" << std::endl;
@@ -212,19 +192,13 @@ ServerConfig parseArguments(int argc, char* argv[]) {
             std::cout << "  --write-timeout <ms>          Write timeout in milliseconds (default: 500)" << std::endl;
             std::cout << "" << std::endl;
             std::cout << "Authentication Options:" << std::endl;
-            std::cout << "  --auth <method>                 Authentication method (none, jwt, api-key, unix, pam, optional)" << std::endl;
+            std::cout << "  --auth <method>                 Authentication method (none, pam, shared-key)" << std::endl;
             std::cout << "  --auth-method <method>          Authentication method (alias for --auth)" << std::endl;
-            std::cout << "  --jwt-secret <secret>           JWT secret key (auto-generated if not provided)" << std::endl;
-            std::cout << "  --jwt-expiration <minutes>      JWT token expiration time (default: 60)" << std::endl;
-            std::cout << "  --enable-guest-access          Allow unauthenticated guest access" << std::endl;
+            std::cout << "  --shared-key <key>              Shared key for shared-key authentication" << std::endl;
             std::cout << "  --pam-service-name <name>      PAM service name (default: stable-diffusion-rest)" << std::endl;
             std::cout << "  --auth-data-dir <dir>          Directory for authentication data (default: ./auth)" << std::endl;
             std::cout << "  --public-paths <paths>         Comma-separated list of public paths (default: /api/health,/api/status)" << std::endl;
-            std::cout << "  --jwt-audience <audience>      JWT audience claim (default: stable-diffusion-rest)" << std::endl;
             std::cout << "" << std::endl;
-            std::cout << "Deprecated Options (will be removed in future version):" << std::endl;
-            std::cout << "  --enable-unix-auth             Deprecated: Use --auth unix instead" << std::endl;
-            std::cout << "  --enable-pam-auth              Deprecated: Use --auth pam instead" << std::endl;
             std::cout << "" << std::endl;
             std::cout << "Model Directory Options:" << std::endl;
             std::cout << "  All model directories are optional and default to standard folder names" << std::endl;
@@ -461,10 +435,16 @@ int main(int argc, char* argv[]) {
         }
 
         auto userManager = std::make_shared<UserManager>(config.auth.dataDir,
-                                                         static_cast<UserManager::AuthMethod>(config.auth.authMethod),
-                                                         config.defaultAdminUsername,
-                                                         config.defaultAdminPassword,
-                                                         config.defaultAdminEmail);
+                                                 static_cast<UserManager::AuthMethod>(config.auth.authMethod),
+                                                 config.defaultAdminUsername,
+                                                 config.defaultAdminPassword,
+                                                 config.defaultAdminEmail);
+        
+        // Enable PAM authentication if configured
+        if (config.auth.authMethod == AuthMethod::PAM) {
+            userManager->setPamAuthEnabled(true);
+        }
+        
         if (!userManager->initialize()) {
             LOG_ERROR("Error: Failed to initialize user manager");
             return 1;
@@ -477,21 +457,12 @@ int main(int argc, char* argv[]) {
                 case AuthMethod::NONE:
                     authMethod = "None";
                     break;
-                case AuthMethod::JWT:
-                    authMethod = "JWT";
-                    break;
-                case AuthMethod::API_KEY:
-                    authMethod = "API Key";
-                    break;
-                case AuthMethod::UNIX:
-                    authMethod = "Unix";
-                    break;
-                case AuthMethod::OPTIONAL:
-                    authMethod = "Optional";
-                    break;
                 case AuthMethod::PAM:
                     authMethod = "PAM";
                     break;
+                case AuthMethod::SHARED_KEY:
+                    authMethod = "Shared Key";
+                    break;
                 default:
                     authMethod = "Unknown";
             }

+ 68 - 67
src/server.cpp

@@ -381,18 +381,18 @@ void Server::registerEndpoints() {
                 auto authConfig        = m_authMiddleware->getConfig();
                 std::string authMethod = "none";
                 switch (authConfig.authMethod) {
-                    case AuthMethod::UNIX:
-                        authMethod = "unix";
+                    case AuthMethod::PAM:
+                        authMethod = "pam";
                         break;
-                    case AuthMethod::JWT:
-                        authMethod = "jwt";
+                    case AuthMethod::SHARED_KEY:
+                        authMethod = "shared-key";
                         break;
                     default:
                         authMethod = "none";
                         break;
                 }
                 configJs << "  authMethod: '" << authMethod << "',\n"
-                         << "  authEnabled: " << (authConfig.authMethod != AuthMethod::NONE ? "true" : "false") << "\n";
+                         << "  authEnabled: " << (authConfig.authMethod != AuthMethod::PAM && authConfig.authMethod != AuthMethod::SHARED_KEY ? "true" : "false") << "\n";
             } else {
                 configJs << "  authMethod: 'none',\n"
                          << "  authEnabled: false\n";
@@ -449,13 +449,10 @@ void Server::registerEndpoints() {
             // Check if authentication is enabled
             if (m_authMiddleware) {
                 auto authConfig = m_authMiddleware->getConfig();
-                if (authConfig.authMethod != AuthMethod::NONE) {
+                if (authConfig.authMethod != AuthMethod::PAM && authConfig.authMethod != AuthMethod::SHARED_KEY) {
                     // Authentication is enabled, check if user is authenticated
                     AuthContext authContext = m_authMiddleware->authenticate(req, res);
 
-                    // For Unix auth, we need to check if the user is authenticated
-                    // The authenticateUnix function will return a guest context for UI requests
-                    // when no Authorization header is present, but we still need to show the login page
                     if (!authContext.authenticated) {
                         // Check if this is a request for a static asset (JS, CSS, images)
                         // These should be served even without authentication to allow the login page to work
@@ -507,12 +504,16 @@ void Server::registerEndpoints() {
 </head>
 <body>
     <h1>Login Required</h1>
-    <p>Please enter your username to continue.</p>
+    <p>Please enter your credentials to continue.</p>
     <form id="loginForm">
         <div class="form-group">
             <label for="username">Username:</label>
             <input type="text" id="username" name="username" required>
         </div>
+        <div class="form-group">
+            <label for="password">Password:</label>
+            <input type="password" id="password" name="password" required>
+        </div>
         <button type="submit">Login</button>
     </form>
     <div id="error" class="error"></div>
@@ -520,22 +521,23 @@ void Server::registerEndpoints() {
         document.getElementById('loginForm').addEventListener('submit', async (e) => {
             e.preventDefault();
             const username = document.getElementById('username').value;
+            const password = document.getElementById('password').value;
             const errorDiv = document.getElementById('error');
 
             try {
                 const response = await fetch('/api/auth/login', {
                     method: 'POST',
-                    headers: { 'Content-Type': 'application/nlohmann::json' },
-                    body: JSON.stringify({ username })
+                    headers: { 'Content-Type': 'application/json' },
+                    body: JSON.stringify({ username, password })
                 });
 
                 if (response.ok) {
-                    const data = await response.nlohmann::json();
+                    const data = await response.json();
                     localStorage.setItem('auth_token', data.token);
-                    localStorage.setItem('unix_user', username);
+                    localStorage.setItem('username', username);
                     window.location.reload();
                 } else {
-                    const error = await response.nlohmann::json();
+                    const error = await response.json();
                     errorDiv.textContent = error.message || 'Login failed';
                 }
             } catch (err) {
@@ -698,9 +700,11 @@ void Server::handleLogin(const httplib::Request& req, httplib::Response& res) {
             return;
         }
 
-        // Check if using Unix authentication
-        if (m_authMiddleware->getConfig().authMethod == AuthMethod::UNIX) {
-            // For Unix auth, get username and password from request body
+        // Check authentication method and handle accordingly
+        auto authMethod = m_authMiddleware->getConfig().authMethod;
+
+        if (authMethod == AuthMethod::PAM) {
+            // For PAM auth, get username and password from request body
             std::string username = requestJson.value("username", "");
             std::string password = requestJson.value("password", "");
 
@@ -709,62 +713,60 @@ void Server::handleLogin(const httplib::Request& req, httplib::Response& res) {
                 return;
             }
 
-            // Check if PAM is enabled - if so, password is required
-            if (m_userManager->isPamAuthEnabled() && password.empty()) {
-                sendErrorResponse(res, "Password is required for Unix authentication", 400, "MISSING_PASSWORD", requestId);
+            if (password.empty()) {
+                sendErrorResponse(res, "Password is required for PAM authentication", 400, "MISSING_PASSWORD", requestId);
                 return;
             }
 
-            // Authenticate Unix user (with or without password depending on PAM)
-            auto result = m_userManager->authenticateUnix(username, password);
+            // Authenticate user with PAM
+            auto result = m_userManager->authenticatePam(username, password);
 
             if (!result.success) {
-                sendErrorResponse(res, result.errorMessage, 401, "UNIX_AUTH_FAILED", requestId);
+                sendErrorResponse(res, result.errorMessage, 401, "PAM_AUTH_FAILED", requestId);
                 return;
             }
 
-            // Generate simple token for Unix auth
-            std::string token = "unix_token_" + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()) + "_" + username;
+            // Generate simple token for PAM auth
+            std::string token = "pam_token_" + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()) + "_" + username;
 
             nlohmann::json response = {
                 {"token", token},
                 {"user", {{"id", result.userId}, {"username", result.username}, {"role", result.role}, {"permissions", result.permissions}}},
-                {"message", "Unix authentication successful"}};
+                {"message", "PAM authentication successful"}};
 
             sendJsonResponse(res, response);
             return;
-        }
-
-        // For non-Unix auth, validate required fields
-        if (!requestJson.contains("username") || !requestJson.contains("password")) {
-            sendErrorResponse(res, "Missing username or password", 400, "MISSING_CREDENTIALS", requestId);
-            return;
-        }
+        } else if (authMethod == AuthMethod::SHARED_KEY) {
+            // For shared key auth, get the shared key from request body
+            std::string sharedKey = requestJson.value("shared_key", "");
 
-        std::string username = requestJson["username"];
-        std::string password = requestJson["password"];
+            if (sharedKey.empty()) {
+                sendErrorResponse(res, "Missing shared_key", 400, "MISSING_SHARED_KEY", requestId);
+                return;
+            }
 
-        // Authenticate user
-        auto result = m_userManager->authenticateUser(username, password);
+            // Authenticate with shared key
+            auto result = m_userManager->authenticateSharedKey(sharedKey);
 
-        if (!result.success) {
-            sendErrorResponse(res, result.errorMessage, 401, "INVALID_CREDENTIALS", requestId);
-            return;
-        }
+            if (!result.success) {
+                sendErrorResponse(res, result.errorMessage, 401, "SHARED_KEY_AUTH_FAILED", requestId);
+                return;
+            }
 
-        // Generate JWT token if using JWT auth
-        std::string token;
-        if (m_authMiddleware->getConfig().authMethod == AuthMethod::JWT) {
-            // For now, create a simple token (in a real implementation, use JWT)
-            token = "token_" + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count()) + "_" + username;
-        }
+            // Generate simple token for shared key auth
+            std::string token = "shared_key_token_" + std::to_string(std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count());
 
-        nlohmann::json response = {
-            {"token", token},
-            {"user", {{"id", result.userId}, {"username", result.username}, {"role", result.role}, {"permissions", result.permissions}}},
-            {"message", "Login successful"}};
+            nlohmann::json response = {
+                {"token", token},
+                {"user", {{"id", result.userId}, {"username", result.username}, {"role", result.role}, {"permissions", result.permissions}}},
+                {"message", "Shared key authentication successful"}};
 
-        sendJsonResponse(res, response);
+            sendJsonResponse(res, response);
+            return;
+        } else {
+            sendErrorResponse(res, "Authentication method not supported", 400, "UNSUPPORTED_AUTH_METHOD", requestId);
+            return;
+        }
 
     } catch (const std::exception& e) {
         sendErrorResponse(res, std::string("Login failed: ") + e.what(), 500, "LOGIN_ERROR", requestId);
@@ -1368,7 +1370,8 @@ void Server::handleQueueStatus(const httplib::Request& /*req*/, httplib::Respons
                                 {"start_time", startTime > 0 ? nlohmann::json(startTime) : nlohmann::json(nullptr)},
                                 {"end_time", endTime > 0 ? nlohmann::json(endTime) : nlohmann::json(nullptr)},
                                 {"position", job.position},
-                                {"progress", job.progress}});
+                                {"progress", job.progress},
+                                {"model_load_progress", job.modelLoadProgress}});
         }
 
         nlohmann::json response = {
@@ -1457,7 +1460,7 @@ void Server::handleJobStatus(const httplib::Request& req, httplib::Response& res
         }
 
         nlohmann::json response = {
-            {"job", {{"id", jobInfo.id}, {"status", statusStr}, {"prompt", jobInfo.prompt}, {"queued_time", queuedTime}, {"start_time", startTime > 0 ? nlohmann::json(startTime) : nlohmann::json(nullptr)}, {"end_time", endTime > 0 ? nlohmann::json(endTime) : nlohmann::json(nullptr)}, {"position", jobInfo.position}, {"outputs", outputUrls}, {"error_message", jobInfo.errorMessage}, {"progress", jobInfo.progress}}}};
+            {"job", {{"id", jobInfo.id}, {"status", statusStr}, {"prompt", jobInfo.prompt}, {"queued_time", queuedTime}, {"start_time", startTime > 0 ? nlohmann::json(startTime) : nlohmann::json(nullptr)}, {"end_time", endTime > 0 ? nlohmann::json(endTime) : nlohmann::json(nullptr)}, {"position", jobInfo.position}, {"outputs", outputUrls}, {"error_message", jobInfo.errorMessage}, {"progress", jobInfo.progress}, {"model_load_progress", jobInfo.modelLoadProgress}}}};
 
         sendJsonResponse(res, response);
     } catch (const std::exception& e) {
@@ -1608,7 +1611,7 @@ void Server::handleDownloadOutput(const httplib::Request& req, httplib::Response
 
         // Set response headers for proper browser handling
         res.set_header("Content-Type", contentType);
-        res.set_header("Content-Length", std::to_string(fileContent.length()));
+        // res.set_header("Content-Length", std::to_string(fileContent.length())); -> http lib calculate it
         res.set_header("Cache-Control", "public, max-age=3600");  // Cache for 1 hour
         res.set_header("Access-Control-Allow-Origin", "*");       // CORS for image access
         // Uncomment if you want to force download instead of inline display:
@@ -1617,7 +1620,7 @@ void Server::handleDownloadOutput(const httplib::Request& req, httplib::Response
         // Set the content
         res.set_content(fileContent, contentType);
         res.status = 200;
-        LOG_DEBUG("Successfully server image: " + filename + " (" + std::to_string(fileContent.length()) + " bytes)");
+        LOG_DEBUG("Successfully served image: " + filename + " (" + std::to_string(fileContent.length()) + " bytes)");
 
     } catch (const std::exception& e) {
         LOG_ERROR("Exception in handleDownloadOutput: " + std::string(e.what()));
@@ -3058,11 +3061,10 @@ void Server::handleText2Img(const httplib::Request& req, httplib::Response& res)
 
         nlohmann::json response = {
             {"request_id", requestId},
+            {"job_id", requestId},
             {"status", "queued"},
             {"message", "Text-to-image generation request queued successfully"},
             {"queue_position", m_generationQueue->getQueueSize()},
-            {"estimated_time_seconds", estimateGenerationTime(genRequest) / 1000},
-            {"estimated_memory_mb", estimateMemoryUsage(genRequest) / (1024 * 1024)},
             {"type", "text2img"},
             {"parameters", params}};
 
@@ -3214,11 +3216,10 @@ void Server::handleImg2Img(const httplib::Request& req, httplib::Response& res)
 
         nlohmann::json response = {
             {"request_id", requestId},
+            {"job_id", requestId},
             {"status", "queued"},
             {"message", "Image-to-image generation request queued successfully"},
             {"queue_position", m_generationQueue->getQueueSize()},
-            {"estimated_time_seconds", estimateGenerationTime(genRequest) / 1000},
-            {"estimated_memory_mb", estimateMemoryUsage(genRequest) / (1024 * 1024)},
             {"type", "img2img"},
             {"parameters", params}};
 
@@ -3359,11 +3360,10 @@ void Server::handleControlNet(const httplib::Request& req, httplib::Response& re
 
         nlohmann::json response = {
             {"request_id", requestId},
+            {"job_id", requestId},
             {"status", "queued"},
             {"message", "ControlNet generation request queued successfully"},
             {"queue_position", m_generationQueue->getQueueSize()},
-            {"estimated_time_seconds", estimateGenerationTime(genRequest) / 1000},
-            {"estimated_memory_mb", estimateMemoryUsage(genRequest) / (1024 * 1024)},
             {"type", "controlnet"},
             {"parameters", params}};
 
@@ -3446,6 +3446,7 @@ void Server::handleUpscale(const httplib::Request& req, httplib::Response& res)
 
         nlohmann::json response = {
             {"request_id", requestId},
+            {"job_id", requestId},
             {"status", "queued"},
             {"message", "Upscale request queued successfully"},
             {"queue_position", m_generationQueue->getQueueSize()},
@@ -3626,11 +3627,10 @@ void Server::handleInpainting(const httplib::Request& req, httplib::Response& re
 
         nlohmann::json response = {
             {"request_id", requestId},
+            {"job_id", requestId},
             {"status", "queued"},
             {"message", "Inpainting generation request queued successfully"},
             {"queue_position", m_generationQueue->getQueueSize()},
-            {"estimated_time_seconds", estimateGenerationTime(genRequest) / 1000},
-            {"estimated_memory_mb", estimateMemoryUsage(genRequest) / (1024 * 1024)},
             {"type", "inpainting"},
             {"parameters", params}};
 
@@ -4493,7 +4493,7 @@ void Server::handleLoadModelById(const httplib::Request& req, httplib::Response&
         // Validate that the identifier is a hash (10+ hexadecimal characters)
         if (modelIdentifier.length() < 10 ||
             !std::all_of(modelIdentifier.begin(), modelIdentifier.end(),
-                        [](char c) { return std::isxdigit(c); })) {
+                         [](char c) { return std::isxdigit(c); })) {
             sendErrorResponse(res, "Invalid model identifier: must be a hash (10+ hexadecimal characters)", 400, "INVALID_MODEL_IDENTIFIER", requestId);
             return;
         }
@@ -5178,7 +5178,8 @@ void Server::logHttpAccess(const httplib::Request& req, const httplib::Response&
         logMessage += " [" + endpoint + "]";
     }
 
-    if (res.status != 200) {
+    // non error http response codes
+    if (res.status > 299) {
         LOG_ERROR("HTTP: " + logMessage);
     } else {
         LOG_INFO("HTTP: " + logMessage);

+ 0 - 6
src/stable_diffusion_wrapper.cpp

@@ -527,15 +527,9 @@ public:
                                      callbackData);
         }
 
-        // Generate the image
-        LOG_DEBUG("[TIMING_ANALYSIS] Starting generate_image() call");
-        auto generationCallStart = std::chrono::high_resolution_clock::now();
 
         sd_image_t* sdImages = generate_image(sdContext, &genParams);
 
-        auto generationCallEnd  = std::chrono::high_resolution_clock::now();
-        auto generationCallTime = std::chrono::duration_cast<std::chrono::milliseconds>(generationCallEnd - generationCallStart).count();
-        LOG_DEBUG("[TIMING_ANALYSIS] generate_image() call completed in " + std::to_string(generationCallTime) + "ms");
 
         // Clear and clean up progress callback - FIX: Wait for any pending callbacks
         sd_set_progress_callback(nullptr, nullptr);

+ 24 - 109
src/user_manager.cpp

@@ -122,66 +122,6 @@ AuthResult UserManager::authenticateUser(const std::string& username, const std:
     return result;
 }
 
-AuthResult UserManager::authenticateUnix(const std::string& username, const std::string& password) {
-    AuthResult result;
-    result.success = false;
-
-    // Check if Unix authentication is the configured method
-    if (m_authMethod != AuthMethod::UNIX) {
-        result.errorMessage = "Unix authentication is not enabled";
-        result.errorCode = "UNIX_AUTH_DISABLED";
-        return result;
-    }
-
-    try {
-        // If PAM is enabled, delegate to PAM authentication
-        if (m_pamAuthEnabled) {
-            return authenticatePam(username, password);
-        }
-
-        // Traditional Unix auth without PAM - just check if user exists
-        // This is a fallback for when PAM is not available
-        struct passwd* pw = getpwnam(username.c_str());
-        if (!pw) {
-            result.errorMessage = "Unix user not found";
-            result.errorCode = "UNIX_USER_NOT_FOUND";
-            return result;
-        }
-
-        // Check if user exists in our system or create guest user
-        UserInfo user;
-        auto it = m_users.find(username);
-        if (it != m_users.end()) {
-            user = it->second;
-        } else {
-            // Create guest user for Unix authentication
-            user.id = generateUserId();
-            user.username = username;
-            user.email = username + "@localhost";
-            user.role = roleToString(UserRole::USER);
-            user.permissions = getDefaultPermissions(UserRole::USER);
-            user.active = true;
-            user.createdAt = getCurrentTimestamp();
-            user.createdBy = "system";
-
-            m_users[username] = user;
-            saveUserData();
-        }
-
-        // Authentication successful
-        result.success = true;
-        result.userId = user.id;
-        result.username = user.username;
-        result.role = user.role;
-        result.permissions = user.permissions;
-
-    } catch (const std::exception& e) {
-        result.errorMessage = "Unix authentication failed: " + std::string(e.what());
-        result.errorCode = "UNIX_AUTH_ERROR";
-    }
-
-    return result;
-}
 
 AuthResult UserManager::authenticatePam(const std::string& username, const std::string& password) {
     AuthResult result;
@@ -249,72 +189,47 @@ AuthResult UserManager::authenticatePam(const std::string& username, const std::
     return result;
 }
 
-AuthResult UserManager::authenticateApiKey(const std::string& apiKey) {
+AuthResult UserManager::authenticateSharedKey(const std::string& sharedKey) {
     AuthResult result;
     result.success = false;
 
-    try {
-        // Hash the API key to compare with stored hashes
-        std::string keyHash = hashApiKey(apiKey);
-
-        auto it = m_apiKeyMap.find(keyHash);
-        if (it == m_apiKeyMap.end()) {
-            result.errorMessage = "Invalid API key";
-            result.errorCode = "INVALID_API_KEY";
-            return result;
-        }
-
-        const std::string& keyId = it->second;
-        const ApiKeyInfo& keyInfo = m_apiKeys[keyId];
-
-        // Check if key is active
-        if (!keyInfo.active) {
-            result.errorMessage = "API key is disabled";
-            result.errorCode = "API_KEY_DISABLED";
-            return result;
-        }
-
-        // Check expiration
-        if (keyInfo.expiresAt > 0 && getCurrentTimestamp() >= keyInfo.expiresAt) {
-            result.errorMessage = "API key has expired";
-            result.errorCode = "API_KEY_EXPIRED";
-            return result;
-        }
-
-        // Get user information
-        auto userIt = m_users.find(keyInfo.userId);
-        if (userIt == m_users.end()) {
-            result.errorMessage = "API key owner not found";
-            result.errorCode = "USER_NOT_FOUND";
-            return result;
-        }
+    // Check if shared key authentication is the configured method
+    if (m_authMethod != AuthMethod::SHARED_KEY) {
+        result.errorMessage = "Shared key authentication is not enabled";
+        result.errorCode = "SHARED_KEY_AUTH_DISABLED";
+        return result;
+    }
 
-        const UserInfo& user = userIt->second;
-        if (!user.active) {
-            result.errorMessage = "API key owner account is disabled";
-            result.errorCode = "ACCOUNT_DISABLED";
-            return result;
-        }
+    try {
+        // For shared key auth, we need to validate against the configured shared key
+        // This will be handled by the middleware that has access to the config
+        // For now, we'll create a simple user for shared key authentication
+        UserInfo user;
+        user.id = "shared_key_user";
+        user.username = "shared_key_user";
+        user.email = "shared_key@localhost";
+        user.role = roleToString(UserRole::ADMIN);
+        user.permissions = getDefaultPermissions(UserRole::ADMIN);
+        user.active = true;
+        user.createdAt = getCurrentTimestamp();
+        user.createdBy = "system";
 
         // Authentication successful
         result.success = true;
         result.userId = user.id;
         result.username = user.username;
         result.role = user.role;
-        result.permissions = keyInfo.permissions.empty() ? user.permissions : keyInfo.permissions;
-
-        // Update last used timestamp
-        m_apiKeys[keyId].lastUsedAt = getCurrentTimestamp();
-        saveApiKeyData();
+        result.permissions = user.permissions;
 
     } catch (const std::exception& e) {
-        result.errorMessage = "API key authentication failed: " + std::string(e.what());
-        result.errorCode = "API_KEY_AUTH_ERROR";
+        result.errorMessage = "Shared key authentication failed: " + std::string(e.what());
+        result.errorCode = "SHARED_KEY_AUTH_ERROR";
     }
 
     return result;
 }
 
+
 std::pair<bool, std::string> UserManager::createUser(const std::string& username,
                                                      const std::string& password,
                                                      const std::string& email,

+ 19 - 0
webui/app/text2img/page.tsx

@@ -5,6 +5,7 @@ import { Header } from "@/components/layout";
 import { AppLayout } from "@/components/layout";
 import { Button } from "@/components/ui/button";
 import { Input } from "@/components/ui/input";
+import { Progress } from "@/components/ui/progress";
 
 import { PromptTextarea } from "@/components/forms";
 import { Label } from "@/components/ui/label";
@@ -597,6 +598,24 @@ function Text2ImgForm() {
             <CardContent className="pt-6">
               <div className="space-y-4">
                 <h3 className="text-lg font-semibold">Generated Images</h3>
+                
+                {/* Progress Display */}
+                {loading && jobInfo && (
+                  <div className="space-y-2">
+                    <div className="flex justify-between text-sm">
+                      <span>Progress</span>
+                      <span>{Math.round(jobInfo.overall_progress || jobInfo.progress || 0)}%</span>
+                    </div>
+                    <Progress value={jobInfo.overall_progress || jobInfo.progress || 0} className="w-full" />
+                    {jobInfo.model_load_progress !== undefined && jobInfo.generation_progress !== undefined && (
+                      <div className="grid grid-cols-2 gap-4 text-xs text-muted-foreground">
+                        <div>Model Loading: {Math.round(jobInfo.model_load_progress)}%</div>
+                        <div>Generation: {Math.round(jobInfo.generation_progress)}%</div>
+                      </div>
+                    )}
+                  </div>
+                )}
+                
                 {generatedImages.length === 0 ? (
                   <div className="flex h-96 items-center justify-center rounded-lg border-2 border-dashed border-border">
                     <p className="text-muted-foreground">

+ 16 - 3
webui/components/features/models/model-status-bar.tsx

@@ -44,13 +44,26 @@ export function ModelStatusBar() {
 
         // Check for model loading jobs (jobs with model-related messages)
         const modelLoadingJob = queue.jobs.find(
-          (job) => job.status === "processing" && 
-                   job.message && 
-                   (job.message.toLowerCase().includes("model") || 
+          (job) => job.status === "processing" &&
+                   job.message &&
+                   (job.message.toLowerCase().includes("model") ||
                     job.message.toLowerCase().includes("loading"))
         );
         setModelLoading(!!modelLoadingJob);
 
+        // DIAGNOSTIC: Log frontend progress updates
+        if (processing) {
+          console.log("[PROGRESS_DEBUG_FRONTEND] Active job update:", {
+            jobId: processing.id,
+            status: processing.status,
+            progress: processing.progress,
+            modelLoadProgress: processing.model_load_progress,
+            generationProgress: processing.generation_progress,
+            message: processing.message,
+            timestamp: new Date().toISOString()
+          });
+        }
+
         // Keep track of recently completed jobs (last 30 seconds)
         const now = Date.now();
         const thirtySecondsAgo = now - 30000;

+ 1 - 0
webui/lib/api.ts

@@ -181,6 +181,7 @@ export interface JobInfo {
   progress?: number;
   model_load_progress?: number;
   generation_progress?: number;
+  overall_progress?: number;
   result?: {
     images: string[];
   };

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio