Fixed three critical issues:
1. Session persistence - Cookies not saved after page refresh
- Replaced APPLICATION_ROOT with SESSION_COOKIE_PATH
- Added proper cookie settings for reverse proxy (HttpOnly, SameSite)
- Set correct cookie path matching URL_PREFIX
2. AJAX detection for FormData uploads (JPG, etc.)
- Enhanced @login_required to detect POST/PUT/DELETE as AJAX
- Added Content-Type check for JSON requests
- Added path prefix check for API endpoints
3. JavaScript AJAX identification
- Updated fetchWithAuth() to add X-Requested-With header
- Properly handles both JSON and FormData requests using Headers API
- Ensures all fetch calls are identified as AJAX by server
Changes:
- web_app.py: Fixed Flask session cookie configuration
- src/auth.py: Improved AJAX detection logic in login_required decorator
- templates/index.html: Enhanced fetchWithAuth() with proper headers
This fixes:
- Users having to re-login on every page refresh
- "Unexpected token '<'" errors when uploading JPG files
- Session cookies not persisting through reverse proxy
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
Fixed two critical issues with API authentication on production server:
1. Modified @login_required decorator to detect AJAX/API requests and return
JSON error with 401 status instead of HTML redirect. This prevents
"Unexpected token '<'" errors when session expires.
2. Created fetchWithAuth() helper function in JavaScript that automatically
handles 401 responses by redirecting to login page. Updated all 11 API
fetch calls to use this wrapper.
Changes:
- src/auth.py: Added AJAX detection and JSON error responses to login_required
- templates/index.html: Added fetchWithAuth() and updated all fetch() calls
This fixes the console errors:
- "Failed to load templates: SyntaxError: Unexpected token '<'"
- 502 Bad Gateway errors now properly handled with session checks
Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
- Login button now uses MSAL.js loginRedirect() for PKCE
- oauth_callback uses MSAL.js handleRedirectPromise() to complete token exchange
- PKCE flow is now entirely in browser (SPA compatible)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Render oauth_callback.html with MSAL.js for browser token exchange
- Add /auth/token endpoint to receive token from JavaScript
- Token exchange happens in browser (cross-origin) for SPA compatibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove -back suffix, use single path for monolithic Flask app
- All routes now use /solventum-image-metadata/ prefix
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add deploy.sh for idempotent Docker deployments
- Configure API_BASE for /solventum-image-metadata-back/ reverse proxy
- Enable Azure AD SSO with public client flow (no secret required)
- Remove hardcoded tester user for production security
- Add ProxyFix middleware for reverse proxy header handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixed two critical issues:
1. OpenAI API compatibility for newer models:
- Added _get_token_param() method to detect model type
- Newer models (gpt-5-mini, gpt-4o, o1, o3) use max_completion_tokens
- Older models (gpt-3.5-turbo) use max_tokens
- Fixes 400 error: 'max_tokens' not supported parameter
2. Progress bar for AI generation:
- Added startProgressAnimation() and stopProgressAnimation()
- Animated progress bar shows activity during AI processing
- Progress slowly increments to 90% to indicate work in progress
- Stops animation when processing completes or errors occur
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed critical bug preventing download functionality:
- Changed currentSessionId to sessionId to match actual variable name
- Function was silently failing with 'No active session' error
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Modified download feature to work with selected files instead of all files:
- Button now shows 'Download Selected Files (N) as ZIP' with count
- New endpoint /download-selected accepts POST with file_indices
- Frontend sends only selected file indices to backend
- Button text updates dynamically when selection changes
- All files selected by default as before
- Users can select/deselect files before downloading
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Improved Download All Files button display:
- Changed insertion point from message div to after fileList element
- Added prominent styling with border, background, and shadow
- Increased button size and padding for better visibility
- Added hover effect for better UX
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added functionality to download all processed files at once in a ZIP archive:
- New endpoint /download-all/<session_id> in web_app.py
- Creates timestamped ZIP archive with all files from session
- Download All button appears after successful file updates
- Button shows at bottom of results with clear styling
- Added zipfile and datetime imports
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Modified updateAllFiles() function to display download buttons for each updated file.
Success messages now include download links with improved UX using flexbox layout.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problem:
- Users entered local folder paths that Docker container cannot access
- Files were not saved to user's folders
- output_dir check (os.path.isdir) always failed for host paths
Solution:
1. Backend (web_app.py):
- Only use output_dir in non-Docker mode
- In Docker mode, always update files in-place
- Users download files via browser instead
2. Frontend (templates/index.html):
- Hide output_dir field in Docker mode
- Show info message: files available for download
- Safe JS check for outputDir element
3. Template rendering:
- Pass docker_mode flag to template
- Conditional display of output directory section
Result:
✅ Docker mode: Files updated in-place, downloadable via browser
✅ Local mode: output_dir still works for direct folder saving
✅ No more confusion about folder paths
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Major Changes:
- Removed GUI version (run_gui.py, src/gui_app.py) - Web-only application
- Fixed duplicate JavaScript variable declaration (importSessionId)
- Fixed metadata import endpoint to use session data instead of Excel lookup
- Added .env.example with all configuration options
Bug Fixes:
- Fixed /update endpoint to use suggested_metadata from session
- Fixed JavaScript updateAllFiles() to send session_id and file_index
- Updated README.md to reflect web-only interface
Dependencies:
- Updated requirements.txt to use minimum version constraints (>=)
Configuration:
- Added comprehensive .env.example with all environment variables
- Documented OpenAI API, Microsoft SSO, and optional tool paths
Testing:
- Verified import metadata workflow end-to-end
- Confirmed file upload and metadata update functionality
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Removed non-functional Browse button for folder selection
- Removed Clear button
- Removed selectOutputFolder() and clearOutputFolder() JavaScript functions
- Added detailed instructions for copying folder paths on Mac and Windows
- Simplified UI to single text input field with helpful hints
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Removed validation check for 'excel' metadata source (no longer exists)
- Removed excelSessionId references from file upload handler
- Cleaned up duplicate validation logic
- Import now handles all file types (CSV/Excel/JSON) via importSessionId
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Removed separate Excel Lookup option from metadata source dropdown
- Consolidated into single 'Import from File (CSV/Excel/JSON)' option
- Removed duplicate Excel Mapping Modal and related functions
- Enhanced Import modal to handle Excel sheet selection
- Updated footer to show v3.1 and simplified metadata sources
- Updated README to reflect consolidated import functionality
- Removed redundant Excel-specific code for cleaner codebase
Benefits:
- Simpler user interface (3 metadata sources instead of 4)
- Unified mapping interface for all file types
- Less code duplication and easier maintenance
- Better UX with consistent workflow
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit implements a complete authentication system with local users,
session management, and Microsoft SSO support for enterprise environments.
New Files Created:
- src/database.py: SQLite database management with users, sessions, audit_log
- src/auth.py: Authentication module with login, SSO, and session management
- templates/login.html: Modern login page with SSO button
Database Schema:
- users table: username, password_hash, email, full_name, auth_method
- sessions table: session management with expiration
- audit_log table: user activity tracking
- Indexes for performance optimization
Authentication Features:
- Local authentication with test user (tester/oliveradmin)
- Password hashing with Werkzeug
- Session management with 24-hour expiration
- @login_required decorator for route protection
- Automatic session cleanup
Microsoft SSO Integration:
- MSAL library integration for Azure AD
- OAuth2 authorization code flow
- Microsoft Graph API user info retrieval
- Automatic user creation/update from SSO
- CSRF protection with state parameter
- Graceful fallback when SSO not configured
Security Improvements:
- All routes protected with @login_required
- Session-based authentication with database storage
- IP address and user agent logging
- Audit trail for user actions
- Secure session token generation
Configuration:
- Environment variables for Azure AD (AZURE_CLIENT_ID, AZURE_CLIENT_SECRET, AZURE_TENANT_ID)
- SECRET_KEY for Flask session encryption
- Optional MSAL dependency (SSO works only if configured)
Dependencies Added:
- Werkzeug>=3.0.0 for password hashing
- msal>=1.20.0 for Microsoft SSO (optional)
Test Credentials:
- Username: tester
- Password: oliveradmin
Phase 4 Status: Complete
Next Phase: Phase 5 (Modern UI Overhaul) for v3.1 release
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Enhanced metadata_analyzer.py with production-ready capabilities:
- Token counting with tiktoken for accurate OpenAI usage tracking
- Exponential backoff retry logic with tenacity library
- Intelligent content truncation based on token limits (not characters)
- Configurable timeout and max retries from Config
- Graceful fallback when tiktoken/tenacity unavailable
- Enhanced error reporting with _ai_error and _tokens_used metadata
Integrated AI generation in web interface:
- AI analyzer lazy initialization in web_app.py
- Real content extraction and AI analysis in upload endpoint
- Error handling for insufficient content or API failures
- Token usage logging for monitoring and optimization
UI improvements for AI experience:
- Special loading message for AI processing (10-30s per file)
- Display token usage for AI-generated metadata
- Show AI errors prominently with helpful messages
- Filter internal metadata fields (_tokens_used, _ai_error) from forms
Dependencies leveraged:
- tiktoken: Proper OpenAI token counting (10x more accurate)
- tenacity: Exponential backoff retry (3 attempts, 2-10s delays)
- openai: Production timeout support (30s default)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Updated application name to "Oliver Metadata Tool"
- Updated version to 3.0.0
- Added App Info constants to config.py (APP_NAME, APP_VERSION, APP_DESCRIPTION)
- Updated web interface (title, header, footer)
- Updated README with new branding and description
- Added AI configuration settings to config.py
- Added ExifTool check method to config.py
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added Flask web interface for batch metadata processing
- Added Excel-based metadata lookup (Celum ID mapping)
- Dual-sheet support: DSB (primary) and Medsurg (fallback)
- Unicode/hieroglyph support for CGA region (Chinese, Japanese, Korean)
- Multi-format support: PDF, images, Office docs, video
- OCR with multi-language support (Tesseract)
- Filename matching without extension (case-insensitive)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>