Merge branch 'main' into copilot/implement-database-backed-settings-page
This commit is contained in:
+50
-123
@@ -5,12 +5,7 @@ All notable changes to DocuElevate will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
# Changelog
|
## [Unreleased]
|
||||||
|
|
||||||
All notable changes to DocuElevate will be documented in this file.
|
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [0.5.0] - 2026-02-08
|
## [0.5.0] - 2026-02-08
|
||||||
|
|
||||||
@@ -21,14 +16,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Settings organized by category: Core, Authentication, AI Services, Storage Providers, Email, IMAP, Monitoring, Processing, Notifications, Feature Flags
|
- Settings organized by category: Core, Authentication, AI Services, Storage Providers, Email, IMAP, Monitoring, Processing, Notifications, Feature Flags
|
||||||
- Form pre-filled with current values, all fields optional for flexible editing
|
- Form pre-filled with current values, all fields optional for flexible editing
|
||||||
- Bulk update support for changing multiple settings at once
|
- Bulk update support for changing multiple settings at once
|
||||||
|
|
||||||
- **Encryption for Sensitive Settings**: Fernet symmetric encryption for database storage
|
- **Encryption for Sensitive Settings**: Fernet symmetric encryption for database storage
|
||||||
- Automatic encryption/decryption for passwords, API keys, tokens, and secrets
|
- Automatic encryption/decryption for passwords, API keys, tokens, and secrets
|
||||||
- Encryption key derived from `SESSION_SECRET` via SHA256
|
- Encryption key derived from `SESSION_SECRET` via SHA256
|
||||||
- Values prefixed with `enc:` in database to identify encrypted data
|
- Values prefixed with `enc:` in database to identify encrypted data
|
||||||
- Graceful fallback if cryptography library unavailable (logs warning)
|
- Graceful fallback if cryptography library unavailable (logs warning)
|
||||||
- Lock icon (🔒) in UI indicates encrypted fields
|
- Lock icon (🔒) in UI indicates encrypted fields
|
||||||
|
|
||||||
- **Setup Wizard**: First-time configuration wizard for fresh installations
|
- **Setup Wizard**: First-time configuration wizard for fresh installations
|
||||||
- 3-step wizard: Infrastructure → Security → AI Services
|
- 3-step wizard: Infrastructure → Security → AI Services
|
||||||
- Auto-detects missing critical settings and redirects from homepage
|
- Auto-detects missing critical settings and redirects from homepage
|
||||||
@@ -36,13 +29,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Auto-generate option for session secrets
|
- Auto-generate option for session secrets
|
||||||
- Skippable for advanced users
|
- Skippable for advanced users
|
||||||
- Settings saved encrypted to database
|
- Settings saved encrypted to database
|
||||||
|
|
||||||
- **Settings Precedence System**: Clear resolution order with visual indicators
|
- **Settings Precedence System**: Clear resolution order with visual indicators
|
||||||
- Precedence: Database > Environment Variables > Defaults
|
- Precedence: Database > Environment Variables > Defaults
|
||||||
- Color-coded badges in UI: 🟢 DB (green), 🔵 ENV (blue), ⚪ DEFAULT (gray)
|
- Color-coded badges in UI: 🟢 DB (green), 🔵 ENV (blue), ⚪ DEFAULT (gray)
|
||||||
- Source detection for each setting shows where value originates
|
- Source detection for each setting shows where value originates
|
||||||
- Info section explaining precedence order
|
- Info section explaining precedence order
|
||||||
|
|
||||||
- **OAuth Admin Support**: Enhanced authentication for settings access
|
- **OAuth Admin Support**: Enhanced authentication for settings access
|
||||||
- Admin flag set from OAuth group membership (`admin` or `administrators`)
|
- Admin flag set from OAuth group membership (`admin` or `administrators`)
|
||||||
- Proper decorator pattern for admin access control
|
- Proper decorator pattern for admin access control
|
||||||
@@ -75,7 +66,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- `app/views/wizard.py` - Wizard routes (GET/POST /setup)
|
- `app/views/wizard.py` - Wizard routes (GET/POST /setup)
|
||||||
- `frontend/templates/setup_wizard.html` - Wizard UI
|
- `frontend/templates/setup_wizard.html` - Wizard UI
|
||||||
- `frontend/templates/settings.html` - Enhanced settings page
|
- `frontend/templates/settings.html` - Enhanced settings page
|
||||||
|
|
||||||
- Modified files:
|
- Modified files:
|
||||||
- `app/utils/settings_service.py` - Encryption integration, 102 setting metadata
|
- `app/utils/settings_service.py` - Encryption integration, 102 setting metadata
|
||||||
- `app/views/settings.py` - Fixed decorator, source detection
|
- `app/views/settings.py` - Fixed decorator, source detection
|
||||||
@@ -93,80 +83,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [0.3.3] - 2026-02-08
|
## [0.3.3] - 2026-02-08
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- **Settings Management System**: Database-backed configuration management with web UI
|
- **Drag-and-drop file upload on Files page**: You can now drag and drop files anywhere on the Files view (`/files`) to upload them, making it more convenient to add documents without navigating to the dedicated Upload page
|
||||||
- Admin-only settings page at `/settings` with 102 settings across 10 categories
|
- Visual drop overlay that appears when dragging files over the Files page
|
||||||
- REST API endpoints: `GET/POST /api/settings/{key}`, `POST /api/settings/bulk-update`, `DELETE /api/settings/{key}`
|
- Upload progress modal in bottom-right corner showing real-time upload status
|
||||||
- Settings organized by category: Core, Authentication, AI Services, Storage Providers, Email, IMAP, Monitoring, Processing, Notifications, Feature Flags
|
- Reusable `upload.js` module extracted from upload page for better code maintainability
|
||||||
- Form pre-filled with current values, all fields optional for flexible editing
|
- Tests for drag-and-drop functionality presence in Files view
|
||||||
- Bulk update support for changing multiple settings at once
|
- Enhanced visual feedback with animations and improved styling
|
||||||
|
|
||||||
- **Encryption for Sensitive Settings**: Fernet symmetric encryption for database storage
|
|
||||||
- Automatic encryption/decryption for passwords, API keys, tokens, and secrets
|
|
||||||
- Encryption key derived from `SESSION_SECRET` via SHA256
|
|
||||||
- Values prefixed with `enc:` in database to identify encrypted data
|
|
||||||
- Graceful fallback if cryptography library unavailable (logs warning)
|
|
||||||
- Lock icon (🔒) in UI indicates encrypted fields
|
|
||||||
|
|
||||||
- **Setup Wizard**: First-time configuration wizard for fresh installations
|
|
||||||
- 3-step wizard: Infrastructure → Security → AI Services
|
|
||||||
- Auto-detects missing critical settings and redirects from homepage
|
|
||||||
- Beautiful UI with progress indicators and step navigation
|
|
||||||
- Auto-generate option for session secrets
|
|
||||||
- Skippable for advanced users
|
|
||||||
- Settings saved encrypted to database
|
|
||||||
|
|
||||||
- **Settings Precedence System**: Clear resolution order with visual indicators
|
|
||||||
- Precedence: Database > Environment Variables > Defaults
|
|
||||||
- Color-coded badges in UI: 🟢 DB (green), 🔵 ENV (blue), ⚪ DEFAULT (gray)
|
|
||||||
- Source detection for each setting shows where value originates
|
|
||||||
- Info section explaining precedence order
|
|
||||||
|
|
||||||
- **OAuth Admin Support**: Enhanced authentication for settings access
|
|
||||||
- Admin flag set from OAuth group membership (`admin` or `administrators`)
|
|
||||||
- Proper decorator pattern for admin access control
|
|
||||||
- Session-based authorization with redirect on unauthorized access
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Updated `requirements.txt` to include `cryptography>=41.0.0` for encryption
|
- Refactored upload functionality into a shared JavaScript module (`/static/js/upload.js`)
|
||||||
- Enhanced settings service to auto-encrypt/decrypt sensitive values transparently
|
- Updated Upload page to use the new shared upload module
|
||||||
- Improved `/settings` route with proper admin decorator (fixes redirect loop)
|
- Improved drop zone visual styling with better colors and animations
|
||||||
- Updated settings template with enhanced UI: source badges, encryption indicators, show/hide toggles
|
|
||||||
- Modified `app/views/general.py` to redirect to wizard when setup required
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- Fixed `/settings` endpoint returning 301 redirect to `/` (converted to proper decorator)
|
|
||||||
- Resolved redirect loop for logged-in non-admin users
|
|
||||||
- Fixed OAuth users not receiving admin privileges from group membership
|
|
||||||
|
|
||||||
### Documentation
|
|
||||||
- Added [docs/SettingsManagement.md](docs/SettingsManagement.md) - Comprehensive user guide
|
|
||||||
- Added [SETTINGS_IMPLEMENTATION.md](SETTINGS_IMPLEMENTATION.md) - Technical documentation
|
|
||||||
- Added [FRAMEWORK_ANALYSIS.md](FRAMEWORK_ANALYSIS.md) - Research on existing frameworks
|
|
||||||
- Added [IMPLEMENTATION_CHECKLIST.md](IMPLEMENTATION_CHECKLIST.md) - Feature tracking
|
|
||||||
- Updated TODO.md with completed features
|
|
||||||
- Updated MILESTONES.md with v0.3.3 release details
|
|
||||||
|
|
||||||
### Technical Details
|
|
||||||
- New files:
|
|
||||||
- `app/utils/encryption.py` - Fernet encryption utilities
|
|
||||||
- `app/utils/setup_wizard.py` - Wizard detection and logic
|
|
||||||
- `app/views/wizard.py` - Wizard routes (GET/POST /setup)
|
|
||||||
- `frontend/templates/setup_wizard.html` - Wizard UI
|
|
||||||
- `frontend/templates/settings.html` - Enhanced settings page
|
|
||||||
|
|
||||||
- Modified files:
|
|
||||||
- `app/utils/settings_service.py` - Encryption integration, 102 setting metadata
|
|
||||||
- `app/views/settings.py` - Fixed decorator, source detection
|
|
||||||
- `app/auth.py` - OAuth admin support
|
|
||||||
- `app/api/settings.py` - Enhanced admin checks
|
|
||||||
- `tests/test_settings.py` - Comprehensive test coverage
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
- Sensitive settings encrypted at rest in database using Fernet (AES-128-CBC + HMAC)
|
- Continued security improvements from v0.3.2 (authlib, starlette updates)
|
||||||
- Encryption key derived from `SESSION_SECRET` (minimum 32 characters required)
|
|
||||||
- Admin-only access enforced on all settings operations
|
|
||||||
- Visual masking of sensitive values in UI by default
|
|
||||||
- CodeQL security scan: 0 alerts
|
|
||||||
|
|
||||||
## [0.3.2] - 2026-02-06
|
## [0.3.2] - 2026-02-06
|
||||||
|
|
||||||
@@ -175,6 +105,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Security scanning workflows (CodeQL, Bandit)
|
- Security scanning workflows (CodeQL, Bandit)
|
||||||
- SECURITY_AUDIT.md documentation
|
- SECURITY_AUDIT.md documentation
|
||||||
- ROADMAP.md and MILESTONES.md planning documents
|
- ROADMAP.md and MILESTONES.md planning documents
|
||||||
|
- API integration tests and configuration validation tests
|
||||||
- Pre-commit hooks configuration
|
- Pre-commit hooks configuration
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
@@ -182,6 +113,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
- Updated starlette to 0.49.1+ (DoS vulnerability fix)
|
- Updated starlette to 0.49.1+ (DoS vulnerability fix)
|
||||||
- Improved SESSION_SECRET validation and handling
|
- Improved SESSION_SECRET validation and handling
|
||||||
- Enhanced .gitignore for security
|
- Enhanced .gitignore for security
|
||||||
|
- Updated README with improved documentation structure
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Critical security vulnerabilities in dependencies
|
- Critical security vulnerabilities in dependencies
|
||||||
@@ -190,61 +122,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
## [0.3.1] - 2026-01-15
|
## [0.3.1] - 2026-01-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- OAuth2 authentication with Authentik support
|
- Files view with sorting and filtering capabilities
|
||||||
- Basic admin authentication
|
- Bulk operations (delete, reprocess) for multiple files
|
||||||
- Session management
|
- File detail view with processing history
|
||||||
|
- Processing flow visualization
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Improved authentication flow
|
- Improved UI responsiveness
|
||||||
- Enhanced error handling
|
- Enhanced error handling and user feedback
|
||||||
|
|
||||||
## [0.3.0] - 2026-01-01
|
### Fixed
|
||||||
|
- Various bug fixes in file processing pipeline
|
||||||
|
|
||||||
|
## [0.3.0] - 2025-12-20
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Multi-provider storage support (Dropbox, Google Drive, OneDrive, S3, FTP, SFTP, WebDAV)
|
- OAuth2 authentication support with Authentik
|
||||||
- Document processing pipeline with OCR
|
- Multi-provider storage support (Dropbox, Google Drive, OneDrive, S3, Nextcloud)
|
||||||
- Metadata extraction with OpenAI
|
- Azure Document Intelligence integration for OCR
|
||||||
- Basic web UI with file listing
|
- OpenAI metadata extraction
|
||||||
- REST API for document operations
|
- Gotenberg PDF conversion service integration
|
||||||
- Celery task queue for async processing
|
- IMAP integration for email attachment processing
|
||||||
|
- REST API with FastAPI
|
||||||
|
- Web UI for document management
|
||||||
|
- Celery task queue for asynchronous processing
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Migrated from Flask to FastAPI
|
- Major architectural improvements
|
||||||
- Updated database schema
|
- Database schema optimizations
|
||||||
- Improved error handling
|
|
||||||
|
|
||||||
## [0.2.0] - 2025-12-01
|
## [0.2.0] - 2025-11-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Initial document processing capabilities
|
- Basic document upload functionality
|
||||||
- Basic storage integration
|
- Simple storage integration
|
||||||
- Simple web interface
|
- Basic metadata extraction
|
||||||
|
|
||||||
## [0.1.0] - 2025-11-01
|
## [0.1.0] - 2025-10-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Initial project setup
|
- Initial release
|
||||||
- Basic FastAPI application structure
|
- Core document processing framework
|
||||||
- Database models
|
- Basic file handling
|
||||||
- Docker configuration
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version History Summary
|
[Unreleased]: https://github.com/christianlouis/DocuElevate/compare/v0.5.0...HEAD
|
||||||
|
[0.5.0]: https://github.com/christianlouis/DocuElevate/compare/v0.3.3...v0.5.0
|
||||||
- **v0.5.0** (2026-02-08): Settings management, encryption, setup wizard
|
[0.3.3]: https://github.com/christianlouis/DocuElevate/compare/v0.3.2...v0.3.3
|
||||||
- **v0.3.3** (2026-02-08): Drag-and-drop upload
|
[0.3.2]: https://github.com/christianlouis/DocuElevate/compare/v0.3.1...v0.3.2
|
||||||
- **v0.3.2** (2026-02-06): Security hardening, testing infrastructure
|
[0.3.1]: https://github.com/christianlouis/DocuElevate/compare/v0.3.0...v0.3.1
|
||||||
- **v0.3.1** (2026-01-15): OAuth2 authentication
|
[0.3.0]: https://github.com/christianlouis/DocuElevate/compare/v0.2.0...v0.3.0
|
||||||
- **v0.3.0** (2026-01-01): Multi-provider storage, OCR, metadata extraction
|
[0.2.0]: https://github.com/christianlouis/DocuElevate/compare/v0.1.0...v0.2.0
|
||||||
- **v0.2.0** (2025-12-01): Document processing
|
[0.1.0]: https://github.com/christianlouis/DocuElevate/releases/tag/v0.1.0
|
||||||
- **v0.1.0** (2025-11-01): Initial release
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Links
|
|
||||||
|
|
||||||
- [GitHub Repository](https://github.com/christianlouis/DocuElevate)
|
|
||||||
- [Documentation](https://docuelevate.readthedocs.io)
|
|
||||||
- [Issue Tracker](https://github.com/christianlouis/DocuElevate/issues)
|
|
||||||
- [Release Notes](https://github.com/christianlouis/DocuElevate/releases)
|
|
||||||
+21
-180
@@ -39,17 +39,15 @@ DocuElevate follows [Semantic Versioning 2.0.0](https://semver.org/):
|
|||||||
- Enhanced upload UI and functionality
|
- Enhanced upload UI and functionality
|
||||||
|
|
||||||
### v0.3.2 (February 2026)
|
### v0.3.2 (February 2026)
|
||||||
- Production-ready document processing
|
- Security hardening (Authlib/Starlette updates)
|
||||||
- Multi-provider storage support
|
- Testing infrastructure implementation
|
||||||
- Basic web UI and REST API
|
- CI/CD improvements
|
||||||
- OAuth2 authentication
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Upcoming Milestones
|
## Completed Milestones
|
||||||
|
|
||||||
### v0.5.0 - Settings Management & Configuration (February 2026)
|
### v0.5.0 - Settings Management & Configuration (February 2026)
|
||||||
**Target Date:** February 15, 2026
|
|
||||||
**Release Date:** February 8, 2026
|
**Release Date:** February 8, 2026
|
||||||
**Status:** ✅ Released
|
**Status:** ✅ Released
|
||||||
**Theme:** Configuration Management, Security, User Experience
|
**Theme:** Configuration Management, Security, User Experience
|
||||||
@@ -80,23 +78,6 @@ DocuElevate follows [Semantic Versioning 2.0.0](https://semver.org/):
|
|||||||
- **Source Indicators**: Visual badges showing where each setting value originates (🟢 DB, 🔵 ENV, ⚪ DEFAULT)
|
- **Source Indicators**: Visual badges showing where each setting value originates (🟢 DB, 🔵 ENV, ⚪ DEFAULT)
|
||||||
- **Admin Access Control**: OAuth admin group support and proper decorator pattern for authorization
|
- **Admin Access Control**: OAuth admin group support and proper decorator pattern for authorization
|
||||||
|
|
||||||
#### Technical Improvements
|
|
||||||
- Fixed /settings redirect loop issue
|
|
||||||
- Added cryptography>=41.0.0 dependency
|
|
||||||
- Created encryption utilities (app/utils/encryption.py)
|
|
||||||
- Implemented settings service with auto-encrypt/decrypt
|
|
||||||
- Built responsive wizard UI with progress indicators
|
|
||||||
- Comprehensive test coverage for settings functionality
|
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- None
|
|
||||||
|
|
||||||
#### Migration Notes
|
|
||||||
- Setup wizard automatically appears for fresh installations
|
|
||||||
- Existing installations can skip wizard
|
|
||||||
- All settings remain backward compatible with environment variables
|
|
||||||
- Database settings override environment variables when present
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### v0.3.3 - Drag-and-Drop Upload (February 2026)
|
### v0.3.3 - Drag-and-Drop Upload (February 2026)
|
||||||
@@ -104,75 +85,42 @@ DocuElevate follows [Semantic Versioning 2.0.0](https://semver.org/):
|
|||||||
**Status:** ✅ Released
|
**Status:** ✅ Released
|
||||||
**Theme:** User Experience Enhancement
|
**Theme:** User Experience Enhancement
|
||||||
|
|
||||||
#### Features
|
#### Goals
|
||||||
- Drag-and-drop file upload on Files page
|
- [x] Add drag-and-drop file upload to Files view
|
||||||
- Visual drop overlay with animations
|
- [x] Refactor upload logic for maintainability
|
||||||
- Upload progress modal
|
- [x] Improve visual feedback during file interactions
|
||||||
- Shared upload JavaScript module
|
|
||||||
|
#### Deliverables
|
||||||
|
- [x] Drag-and-drop upload functionality in Files view
|
||||||
|
- [x] Reusable `upload.js` module for code DRYness
|
||||||
|
- [x] Visual drop overlay and progress modal
|
||||||
|
- [x] Enhanced upload error handling
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### v0.3.2 - Security & Testing Hardening (February 2026)
|
### v0.3.2 - Security & Testing Hardening (February 2026)
|
||||||
**Target Date:** February 15, 2026
|
**Release Date:** February 6, 2026
|
||||||
**Release Date:** February 8, 2026
|
|
||||||
**Status:** ✅ Released
|
**Status:** ✅ Released
|
||||||
**Theme:** Security, Quality, Testing, Configuration Management
|
**Theme:** Security, Quality, Testing
|
||||||
|
|
||||||
#### Goals
|
#### Goals
|
||||||
- [x] Fix critical security vulnerabilities (authlib, starlette)
|
- [x] Fix critical security vulnerabilities (authlib, starlette)
|
||||||
- [x] Implement comprehensive test suite
|
- [x] Implement comprehensive test suite
|
||||||
- [x] Add security scanning (CodeQL, Bandit)
|
- [x] Add security scanning (CodeQL, Bandit)
|
||||||
- [x] Improve CI/CD pipeline
|
- [x] Improve CI/CD pipeline
|
||||||
- [x] **Implement database-backed settings management**
|
|
||||||
- [x] **Add encryption for sensitive configuration**
|
|
||||||
- [x] **Create setup wizard for first-time installation**
|
|
||||||
- [ ] Achieve 60% test coverage (ongoing)
|
|
||||||
- [ ] Add pre-commit hooks (ongoing)
|
|
||||||
- [ ] Update all dependencies to latest secure versions (ongoing)
|
|
||||||
|
|
||||||
#### Deliverables
|
#### Deliverables
|
||||||
- [x] SECURITY_AUDIT.md documentation
|
- [x] SECURITY_AUDIT.md documentation
|
||||||
- [x] pytest configuration and fixtures
|
- [x] pytest configuration and fixtures
|
||||||
- [x] API integration tests
|
- [x] API integration tests
|
||||||
- [x] Configuration validation tests
|
- [x] Configuration validation tests
|
||||||
- [x] **Settings management UI at /settings**
|
|
||||||
- [x] **Setup wizard at /setup**
|
|
||||||
- [x] **Fernet encryption for sensitive settings**
|
|
||||||
- [x] **Source indicators (DB/ENV/DEFAULT)**
|
|
||||||
- [x] **Complete settings documentation**
|
|
||||||
- [x] **Framework analysis (FRAMEWORK_ANALYSIS.md)**
|
|
||||||
- [ ] Task processing tests (ongoing)
|
|
||||||
- [ ] Storage provider integration tests (ongoing)
|
|
||||||
- [x] Updated CI/CD workflows
|
- [x] Updated CI/CD workflows
|
||||||
- [ ] Security best practices guide (ongoing)
|
- [x] Pre-commit hooks configuration
|
||||||
|
|
||||||
#### New Features
|
|
||||||
- **Settings Management System**: Web-based admin UI for viewing and editing 102 application settings across 10 categories
|
|
||||||
- **Encryption**: Fernet symmetric encryption for sensitive values (passwords, API keys, tokens) with key derived from SESSION_SECRET
|
|
||||||
- **Setup Wizard**: 3-step wizard for first-time configuration (Infrastructure → Security → AI Services)
|
|
||||||
- **Precedence System**: Settings resolved in order: Database > Environment Variables > Defaults
|
|
||||||
- **Source Indicators**: Visual badges showing where each setting value originates (🟢 DB, 🔵 ENV, ⚪ DEFAULT)
|
|
||||||
- **Admin Access Control**: OAuth admin group support and proper decorator pattern for authorization
|
|
||||||
|
|
||||||
#### Technical Improvements
|
|
||||||
- Fixed /settings redirect loop issue
|
|
||||||
- Added cryptography>=41.0.0 dependency
|
|
||||||
- Created encryption utilities (app/utils/encryption.py)
|
|
||||||
- Implemented settings service with auto-encrypt/decrypt
|
|
||||||
- Built responsive wizard UI with progress indicators
|
|
||||||
- Comprehensive test coverage for settings functionality
|
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- None
|
|
||||||
|
|
||||||
#### Migration Notes
|
|
||||||
- Setup wizard automatically appears for fresh installations
|
|
||||||
- Existing installations can skip wizard
|
|
||||||
- All settings remain backward compatible with environment variables
|
|
||||||
- Database settings override environment variables when present
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Upcoming Milestones
|
||||||
|
|
||||||
### v0.6.0 - Enhanced Search & UI Improvements (April 2026)
|
### v0.6.0 - Enhanced Search & UI Improvements (April 2026)
|
||||||
**Target Date:** April 1, 2026
|
**Target Date:** April 1, 2026
|
||||||
**Status:** 📋 Planned
|
**Status:** 📋 Planned
|
||||||
@@ -225,9 +173,6 @@ DocuElevate follows [Semantic Versioning 2.0.0](https://semver.org/):
|
|||||||
- Integration examples and templates
|
- Integration examples and templates
|
||||||
- Webhook payload documentation
|
- Webhook payload documentation
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- None
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### v0.7.0 - Advanced AI & Multi-language (August 2026)
|
### v0.7.0 - Advanced AI & Multi-language (August 2026)
|
||||||
@@ -251,9 +196,6 @@ DocuElevate follows [Semantic Versioning 2.0.0](https://semver.org/):
|
|||||||
- Translation framework (10+ languages)
|
- Translation framework (10+ languages)
|
||||||
- Localized documentation
|
- Localized documentation
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- Configuration file format changes (auto-migration script provided)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
### v1.0.0 - Enterprise Edition (November 2026)
|
### v1.0.0 - Enterprise Edition (November 2026)
|
||||||
@@ -291,88 +233,6 @@ This is our first major release, marking production-ready enterprise capabilitie
|
|||||||
- Database replication support
|
- Database replication support
|
||||||
- Message queue clustering
|
- Message queue clustering
|
||||||
|
|
||||||
- **Observability**
|
|
||||||
- Comprehensive audit logs
|
|
||||||
- Prometheus metrics export
|
|
||||||
- Grafana dashboards
|
|
||||||
- APM integration (New Relic, DataDog)
|
|
||||||
- SLA monitoring
|
|
||||||
|
|
||||||
- **Documentation**
|
|
||||||
- Enterprise deployment guide
|
|
||||||
- High availability setup
|
|
||||||
- Disaster recovery procedures
|
|
||||||
- Security compliance guide
|
|
||||||
- Professional services offerings
|
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- Database schema migration (automatic with Alembic)
|
|
||||||
- Configuration file restructure (migration tool provided)
|
|
||||||
- API v1 deprecated (v2 required for new features)
|
|
||||||
|
|
||||||
#### Migration Path
|
|
||||||
- Detailed migration guide provided
|
|
||||||
- Automated migration scripts
|
|
||||||
- Rollback procedures documented
|
|
||||||
- Migration support via GitHub Discussions
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### v1.1.0 - Collaboration & Analytics (January 2027)
|
|
||||||
**Target Date:** January 15, 2027
|
|
||||||
**Status:** 📋 Planned
|
|
||||||
**Theme:** Collaboration, Reporting, Analytics
|
|
||||||
|
|
||||||
#### Goals
|
|
||||||
- Document sharing with expiring links
|
|
||||||
- Comments and annotations
|
|
||||||
- Version history
|
|
||||||
- Analytics dashboard
|
|
||||||
- Cost analysis
|
|
||||||
- Export reports
|
|
||||||
|
|
||||||
#### Deliverables
|
|
||||||
- Sharing interface with permissions
|
|
||||||
- Comment system with threading
|
|
||||||
- Version control and diff viewer
|
|
||||||
- Analytics dashboard with charts
|
|
||||||
- Cost breakdown by provider
|
|
||||||
- Report generation (PDF, CSV, Excel)
|
|
||||||
- User activity tracking
|
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- None
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### v2.0.0 - On-Premise AI & Platform Expansion (Q3 2027)
|
|
||||||
**Target Date:** Q3 2027
|
|
||||||
**Status:** 🔮 Future
|
|
||||||
**Theme:** Self-hosting, Privacy, Platform Diversity
|
|
||||||
|
|
||||||
#### Goals
|
|
||||||
- Self-hosted AI models (no cloud dependencies)
|
|
||||||
- Local LLM integration
|
|
||||||
- Desktop and mobile applications
|
|
||||||
- Offline-first capabilities
|
|
||||||
- Enhanced privacy features
|
|
||||||
- Plugin marketplace
|
|
||||||
|
|
||||||
#### Deliverables
|
|
||||||
- Tesseract/EasyOCR integration
|
|
||||||
- Ollama/LLaMA support
|
|
||||||
- Desktop app (Windows, Mac, Linux)
|
|
||||||
- Mobile apps (iOS, Android)
|
|
||||||
- Browser extensions (Chrome, Firefox)
|
|
||||||
- Plugin SDK and marketplace
|
|
||||||
- Offline mode
|
|
||||||
|
|
||||||
#### Breaking Changes
|
|
||||||
- Major API restructure (v3)
|
|
||||||
- New authentication system
|
|
||||||
- Configuration format change
|
|
||||||
- Minimum Python version: 3.12
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Release Process
|
## Release Process
|
||||||
@@ -395,14 +255,6 @@ This is our first major release, marking production-ready enterprise capabilitie
|
|||||||
- Helm charts (future)
|
- Helm charts (future)
|
||||||
- Documentation site update
|
- Documentation site update
|
||||||
|
|
||||||
### Post-release
|
|
||||||
- [ ] GitHub release created
|
|
||||||
- [ ] Blog post published
|
|
||||||
- [ ] Social media announcement
|
|
||||||
- [ ] Community notification
|
|
||||||
- [ ] Support documentation updated
|
|
||||||
- [ ] Monitor for critical issues
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version History
|
## Version History
|
||||||
@@ -412,9 +264,10 @@ This is our first major release, marking production-ready enterprise capabilitie
|
|||||||
| v0.1.0 | 2024-Q1 | Initial Release | Released |
|
| v0.1.0 | 2024-Q1 | Initial Release | Released |
|
||||||
| v0.2.0 | 2024-Q3 | Multi-provider Support | Released |
|
| v0.2.0 | 2024-Q3 | Multi-provider Support | Released |
|
||||||
| v0.3.0 | 2025-Q4 | UI & Authentication | Released |
|
| v0.3.0 | 2025-Q4 | UI & Authentication | Released |
|
||||||
|
| v0.3.1 | 2026-01-15 | OAuth2 Integration | Released |
|
||||||
| v0.3.2 | 2026-02-06 | Security Updates | Released |
|
| v0.3.2 | 2026-02-06 | Security Updates | Released |
|
||||||
| v0.3.3 | 2026-02-08 | Drag-and-Drop Upload | Released |
|
| v0.3.3 | 2026-02-08 | Drag-and-Drop Upload | Released |
|
||||||
| v0.5.0 | 2026-02-08 | **Current Stable** - Settings Management | **Released** |
|
| v0.5.0 | 2026-02-08 | **Settings & Encryption** | **Released** |
|
||||||
| v0.6.0 | 2026-04 | Search & UX | Planned |
|
| v0.6.0 | 2026-04 | Search & UX | Planned |
|
||||||
| v0.7.0 | 2026-08 | Advanced AI | Planned |
|
| v0.7.0 | 2026-08 | Advanced AI | Planned |
|
||||||
| v1.0.0 | 2026-11 | Enterprise | Planned |
|
| v1.0.0 | 2026-11 | Enterprise | Planned |
|
||||||
@@ -440,16 +293,4 @@ This is our first major release, marking production-ready enterprise capabilitie
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Contributing to Milestones
|
|
||||||
|
|
||||||
Want to contribute to a specific milestone?
|
|
||||||
|
|
||||||
1. Check the [GitHub Projects](https://github.com/christianlouis/DocuElevate/projects) board
|
|
||||||
2. Look for issues tagged with milestone labels
|
|
||||||
3. Read [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
||||||
4. Comment on the issue you'd like to work on
|
|
||||||
5. Submit a PR linked to the issue
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*This milestone document is updated regularly. For real-time status, check our [GitHub Projects](https://github.com/christianlouis/DocuElevate/projects) board.*
|
*This milestone document is updated regularly. For real-time status, check our [GitHub Projects](https://github.com/christianlouis/DocuElevate/projects) board.*
|
||||||
@@ -79,20 +79,24 @@ Users can choose to send documents to any combination of these destinations thro
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
|
- **Intuitive File Upload**:
|
||||||
|
- Drag-and-drop file upload on both Upload and Files pages—upload anywhere on the Files page
|
||||||
|
- Real-time upload progress with validation
|
||||||
|
- Support for PDF, Office documents, images, and more (up to 500MB per file)
|
||||||
- **Document Upload & Storage**:
|
- **Document Upload & Storage**:
|
||||||
- Manual uploads (via API or UI) to Dropbox, Nextcloud, Google Drive, or Paperless.
|
- Manual uploads (via API or UI) to Dropbox, Nextcloud, Google Drive, or Paperless
|
||||||
- **OCR Processing (Azure)**:
|
- **OCR Processing (Azure)**:
|
||||||
- Extract text from scanned PDFs using Azure Document Intelligence.
|
- Extract text from scanned PDFs using Azure Document Intelligence
|
||||||
- **Metadata Extraction (OpenAI)**:
|
- **Metadata Extraction (OpenAI)**:
|
||||||
- Use GPT to classify, label, or otherwise enrich the text with structured metadata.
|
- Use GPT to classify, label, or otherwise enrich the text with structured metadata
|
||||||
- **PDF Conversion (Gotenberg)**:
|
- **PDF Conversion (Gotenberg)**:
|
||||||
- Convert non-PDF attachments (e.g., Word docs, images) into PDFs.
|
- Convert non-PDF attachments (e.g., Word docs, images) into PDFs
|
||||||
- **Document Management (Paperless NGX)**:
|
- **Document Management (Paperless NGX)**:
|
||||||
- Store processed documents and metadata in a Paperless NGX instance.
|
- Store processed documents and metadata in a Paperless NGX instance
|
||||||
- **IMAP Integration**:
|
- **IMAP Integration**:
|
||||||
- Fetch documents from multiple mailboxes (including Gmail) and automatically enqueue them for processing.
|
- Fetch documents from multiple mailboxes (including Gmail) and automatically enqueue them for processing
|
||||||
- **Authentication**:
|
- **Authentication**:
|
||||||
- Secure access to the system using **Authentik** for OAuth2-based login.
|
- Secure access to the system using **Authentik** for OAuth2-based login
|
||||||
|
|
||||||
## Frameworks Used
|
## Frameworks Used
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ This document tracks actionable tasks for the current development cycle. For lon
|
|||||||
- [ ] Add dark mode
|
- [ ] Add dark mode
|
||||||
- [ ] Add loading spinners for async operations
|
- [ ] Add loading spinners for async operations
|
||||||
- [ ] Improve error messages for users
|
- [ ] Improve error messages for users
|
||||||
- [ ] Add drag-and-drop file upload
|
- [x] Add drag-and-drop file upload (completed 2026-02-08)
|
||||||
- [ ] Add file type icons
|
- [ ] Add file type icons
|
||||||
- [ ] Implement toast notifications
|
- [ ] Implement toast notifications
|
||||||
- [ ] Add keyboard shortcuts
|
- [ ] Add keyboard shortcuts
|
||||||
@@ -224,6 +224,9 @@ This document tracks actionable tasks for the current development cycle. For lon
|
|||||||
- [x] Created comprehensive settings documentation
|
- [x] Created comprehensive settings documentation
|
||||||
- [x] Added cryptography dependency for encryption
|
- [x] Added cryptography dependency for encryption
|
||||||
- [x] Analyzed existing frameworks (justified custom implementation)
|
- [x] Analyzed existing frameworks (justified custom implementation)
|
||||||
|
- [x] Added drag-and-drop file upload to Files view
|
||||||
|
- [x] Extracted reusable upload.js module for code reuse
|
||||||
|
- [x] Enhanced UX with visual drop overlay and upload progress modal
|
||||||
|
|
||||||
### 2026-02-06
|
### 2026-02-06
|
||||||
- [x] Created comprehensive test infrastructure
|
- [x] Created comprehensive test infrastructure
|
||||||
|
|||||||
+27
-5
@@ -43,13 +43,35 @@ DocuElevate features a simple navigation system with the following main sections
|
|||||||
|
|
||||||
## Uploading Documents
|
## Uploading Documents
|
||||||
|
|
||||||
### Web Upload
|
DocuElevate provides multiple convenient ways to upload documents to the system.
|
||||||
|
|
||||||
|
### Web Upload (Upload Page)
|
||||||
|
|
||||||
1. Navigate to the **Upload** page
|
1. Navigate to the **Upload** page
|
||||||
2. Drag and drop files onto the upload area, or click to browse your files
|
2. **Drag and drop** files onto the upload area, or **click** the upload area to browse your files
|
||||||
3. Select files to upload (supported formats include PDF, Word documents, images, etc.)
|
3. Select files to upload (supported formats include PDF, Word documents, Excel spreadsheets, PowerPoint presentations, images, and more)
|
||||||
4. Click "Upload" to begin the process
|
4. The system will automatically validate and upload your files
|
||||||
5. Your documents will be processed automatically according to the system configuration
|
5. You'll see real-time progress for each file being uploaded
|
||||||
|
6. Your documents will be processed automatically according to the system configuration
|
||||||
|
|
||||||
|
#### Supported File Types
|
||||||
|
- **Documents**: PDF, Word (.doc, .docx), Excel (.xls, .xlsx), PowerPoint (.ppt, .pptx)
|
||||||
|
- **Images**: JPEG, PNG, GIF, BMP, TIFF, WebP, SVG
|
||||||
|
- **Text**: Plain text (.txt), CSV, RTF, HTML, XML, Markdown
|
||||||
|
- **Maximum file size**: 500MB per file
|
||||||
|
|
||||||
|
### Drag-and-Drop on Files Page
|
||||||
|
|
||||||
|
For even more convenience, you can upload files directly from the **Files** page:
|
||||||
|
|
||||||
|
1. Navigate to the **Files** page where you view your processed documents
|
||||||
|
2. **Drag files from your computer** and drop them **anywhere** on the page
|
||||||
|
3. A visual overlay will appear to confirm you're in drag mode
|
||||||
|
4. Release the files to begin uploading
|
||||||
|
5. An upload progress modal will appear in the bottom-right corner
|
||||||
|
6. The page will automatically refresh to show your newly uploaded files once complete
|
||||||
|
|
||||||
|
This feature allows you to quickly add new files without navigating away from your document management view.
|
||||||
|
|
||||||
### Email Attachments
|
### Email Attachments
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,264 @@
|
|||||||
|
// frontend/static/js/upload.js
|
||||||
|
// Reusable drag-and-drop upload functionality for DocuElevate
|
||||||
|
|
||||||
|
// Configuration
|
||||||
|
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500MB
|
||||||
|
|
||||||
|
// Allowed file types
|
||||||
|
const ACCEPTED_TYPES = {
|
||||||
|
// PDF files
|
||||||
|
'application/pdf': true,
|
||||||
|
|
||||||
|
// Image formats
|
||||||
|
'image/jpeg': true, 'image/jpg': true, 'image/png': true,
|
||||||
|
'image/gif': true, 'image/bmp': true, 'image/tiff': true,
|
||||||
|
'image/webp': true, 'image/svg+xml': true,
|
||||||
|
|
||||||
|
// Office document formats - Word
|
||||||
|
'application/msword': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': true,
|
||||||
|
'application/vnd.ms-word.document.macroEnabled.12': true,
|
||||||
|
|
||||||
|
// Excel
|
||||||
|
'application/vnd.ms-excel': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.template': true,
|
||||||
|
'application/vnd.ms-excel.sheet.macroEnabled.12': true,
|
||||||
|
|
||||||
|
// PowerPoint
|
||||||
|
'application/vnd.ms-powerpoint': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.presentation': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.template': true,
|
||||||
|
'application/vnd.openxmlformats-officedocument.presentationml.slideshow': true,
|
||||||
|
|
||||||
|
// Other common formats
|
||||||
|
'text/plain': true,
|
||||||
|
'text/csv': true,
|
||||||
|
'application/rtf': true,
|
||||||
|
'text/rtf': true,
|
||||||
|
'text/html': true,
|
||||||
|
'application/xml': true,
|
||||||
|
'text/xml': true
|
||||||
|
};
|
||||||
|
|
||||||
|
// File extensions that are always allowed (even if mime type is not recognized)
|
||||||
|
const ACCEPTED_EXTENSIONS = [
|
||||||
|
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
||||||
|
'.odt', '.ods', '.odp', '.rtf', '.txt', '.csv',
|
||||||
|
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.svg', '.md'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process a list of files for upload
|
||||||
|
* @param {FileList} files - Files to process
|
||||||
|
* @param {HTMLElement} progressContainer - Container element for progress display
|
||||||
|
* @param {HTMLElement} statusMessage - Element for status message display
|
||||||
|
*/
|
||||||
|
function processFiles(files, progressContainer, statusMessage) {
|
||||||
|
if (files.length === 0) return;
|
||||||
|
|
||||||
|
if (statusMessage) {
|
||||||
|
statusMessage.textContent = `Processing ${files.length} file(s)...`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear previous upload progress
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.innerHTML = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process each file
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
const file = files[i];
|
||||||
|
validateAndUpload(file, progressContainer, statusMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate and upload a single file
|
||||||
|
* @param {File} file - File to validate and upload
|
||||||
|
* @param {HTMLElement} progressContainer - Container element for progress display
|
||||||
|
* @param {HTMLElement} statusMessage - Element for status message display
|
||||||
|
*/
|
||||||
|
function validateAndUpload(file, progressContainer, statusMessage) {
|
||||||
|
// Create progress element for this file
|
||||||
|
const fileProgress = document.createElement("div");
|
||||||
|
fileProgress.className = "flex flex-col mb-2";
|
||||||
|
fileProgress.innerHTML = `
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-sm truncate" title="${file.name}">${file.name}</span>
|
||||||
|
<span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="w-full bg-gray-200 h-2 rounded-full mt-1">
|
||||||
|
<div class="file-progress-bar bg-blue-500 h-2 rounded-full" style="width: 0%"></div>
|
||||||
|
</div>
|
||||||
|
<div class="file-status text-xs text-gray-600 mt-1">Validating...</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (progressContainer) {
|
||||||
|
progressContainer.appendChild(fileProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
const progressBar = fileProgress.querySelector(".file-progress-bar");
|
||||||
|
const statusEl = fileProgress.querySelector(".file-status");
|
||||||
|
|
||||||
|
// Validate file type by checking both MIME type and extension
|
||||||
|
const isValidMimeType = ACCEPTED_TYPES[file.type] || false;
|
||||||
|
const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
|
||||||
|
const isValidExtension = ACCEPTED_EXTENSIONS.includes(fileExtension);
|
||||||
|
|
||||||
|
if (!isValidMimeType && !isValidExtension) {
|
||||||
|
statusEl.textContent = `Error: ${file.name} - Unsupported file type`;
|
||||||
|
statusEl.className = "text-xs text-red-500 mt-1";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate file size
|
||||||
|
if (file.size > MAX_FILE_SIZE) {
|
||||||
|
statusEl.textContent = `Error: ${file.name} - File size exceeds 500MB limit`;
|
||||||
|
statusEl.className = "text-xs text-red-500 mt-1";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload the file
|
||||||
|
uploadFile(file, progressBar, statusEl, statusMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Upload a file to the server
|
||||||
|
* @param {File} file - File to upload
|
||||||
|
* @param {HTMLElement} progressBar - Progress bar element
|
||||||
|
* @param {HTMLElement} statusEl - Status element
|
||||||
|
* @param {HTMLElement} statusMessage - Overall status message element
|
||||||
|
*/
|
||||||
|
async function uploadFile(file, progressBar, statusEl, statusMessage) {
|
||||||
|
statusEl.textContent = `Uploading...`;
|
||||||
|
try {
|
||||||
|
let formData = new FormData();
|
||||||
|
formData.append("file", file);
|
||||||
|
|
||||||
|
const xhr = new XMLHttpRequest();
|
||||||
|
xhr.open("POST", "/api/ui-upload", true);
|
||||||
|
|
||||||
|
xhr.upload.onprogress = (e) => {
|
||||||
|
if (e.lengthComputable) {
|
||||||
|
const percentComplete = (e.loaded / e.total) * 100;
|
||||||
|
progressBar.style.width = percentComplete + "%";
|
||||||
|
statusEl.textContent = `Uploading: ${Math.round(percentComplete)}%`;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onload = function() {
|
||||||
|
if (xhr.status === 200) {
|
||||||
|
const result = JSON.parse(xhr.responseText);
|
||||||
|
progressBar.style.width = "100%";
|
||||||
|
progressBar.className = "file-progress-bar bg-green-500 h-2 rounded-full";
|
||||||
|
statusEl.textContent = `Success: Task ID: ${result.task_id}`;
|
||||||
|
statusEl.className = "text-xs text-green-600 mt-1";
|
||||||
|
updateOverallStatus(statusMessage);
|
||||||
|
} else {
|
||||||
|
throw new Error(`Upload failed with status ${xhr.status}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.onerror = function() {
|
||||||
|
throw new Error("Network error occurred");
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.send(formData);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
statusEl.textContent = `Error: ${err.message}`;
|
||||||
|
statusEl.className = "text-xs text-red-500 mt-1";
|
||||||
|
progressBar.className = "file-progress-bar bg-red-500 h-2 rounded-full";
|
||||||
|
updateOverallStatus(statusMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update the overall status message based on file statuses
|
||||||
|
* @param {HTMLElement} statusMessage - Status message element
|
||||||
|
*/
|
||||||
|
function updateOverallStatus(statusMessage) {
|
||||||
|
if (!statusMessage) return;
|
||||||
|
|
||||||
|
// Count success/failure
|
||||||
|
const fileStatuses = document.querySelectorAll('.file-status');
|
||||||
|
let completed = 0;
|
||||||
|
let total = fileStatuses.length;
|
||||||
|
|
||||||
|
fileStatuses.forEach(status => {
|
||||||
|
if (status.textContent.includes('Success') || status.textContent.includes('Error')) {
|
||||||
|
completed++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (completed === total) {
|
||||||
|
statusMessage.textContent = `All uploads completed (${completed}/${total})`;
|
||||||
|
} else {
|
||||||
|
statusMessage.textContent = `Uploading files (${completed}/${total})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format file size for display
|
||||||
|
* @param {number} bytes - File size in bytes
|
||||||
|
* @returns {string} Formatted file size
|
||||||
|
*/
|
||||||
|
function formatFileSize(bytes) {
|
||||||
|
if (bytes === 0) return '0 Bytes';
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize drag-and-drop on an element
|
||||||
|
* @param {HTMLElement} element - Element to enable drag-and-drop on
|
||||||
|
* @param {HTMLElement} progressContainer - Container for progress display
|
||||||
|
* @param {HTMLElement} statusMessage - Element for status messages
|
||||||
|
* @param {Object} options - Additional options
|
||||||
|
*/
|
||||||
|
function initDragAndDrop(element, progressContainer, statusMessage, options = {}) {
|
||||||
|
if (!element) {
|
||||||
|
console.error("Element not found for drag-and-drop initialization");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event listeners for drag-and-drop
|
||||||
|
element.addEventListener("dragover", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
e.dataTransfer.dropEffect = "copy";
|
||||||
|
|
||||||
|
// Add visual feedback
|
||||||
|
if (options.dragOverClass) {
|
||||||
|
element.classList.add(options.dragOverClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
element.addEventListener("dragleave", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Remove visual feedback
|
||||||
|
if (options.dragOverClass) {
|
||||||
|
element.classList.remove(options.dragOverClass);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
element.addEventListener("drop", (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
|
||||||
|
// Remove visual feedback
|
||||||
|
if (options.dragOverClass) {
|
||||||
|
element.classList.remove(options.dragOverClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.dataTransfer.files.length) {
|
||||||
|
processFiles(e.dataTransfer.files, progressContainer, statusMessage);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -3,6 +3,99 @@
|
|||||||
|
|
||||||
{% block head_extra %}
|
{% block head_extra %}
|
||||||
<script src="/static/js/common.js"></script>
|
<script src="/static/js/common.js"></script>
|
||||||
|
<script src="/static/js/upload.js"></script>
|
||||||
|
<style>
|
||||||
|
/* Drop overlay styles - covers entire page for drag-and-drop anywhere */
|
||||||
|
.drop-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: rgba(59, 130, 246, 0.15);
|
||||||
|
border: 6px dashed #3b82f6;
|
||||||
|
z-index: 999;
|
||||||
|
pointer-events: none;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.drop-overlay.active {
|
||||||
|
display: flex;
|
||||||
|
animation: fadeIn 0.2s ease-in;
|
||||||
|
}
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
.drop-message {
|
||||||
|
background-color: white;
|
||||||
|
padding: 2rem 3rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
|
||||||
|
text-align: center;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.drop-message i {
|
||||||
|
font-size: 4rem;
|
||||||
|
color: #3b82f6;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.drop-message p {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2937;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.drop-message .drop-hint {
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #6b7280;
|
||||||
|
font-weight: 400;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Upload progress modal */
|
||||||
|
.upload-modal {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 1rem;
|
||||||
|
right: 1rem;
|
||||||
|
width: 400px;
|
||||||
|
max-width: 90vw;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
||||||
|
z-index: 1000;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.upload-modal.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.upload-modal-header {
|
||||||
|
background-color: #3b82f6;
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.upload-modal-body {
|
||||||
|
padding: 1rem;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.close-modal-btn {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.close-modal-btn:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.file-table {
|
.file-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -241,6 +334,29 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<!-- Drop overlay -->
|
||||||
|
<div id="dropOverlay" class="drop-overlay">
|
||||||
|
<div class="drop-message">
|
||||||
|
<i class="fas fa-cloud-upload-alt"></i>
|
||||||
|
<p>Drop files anywhere to upload</p>
|
||||||
|
<div class="drop-hint">Supports PDF, Office docs, images, and more</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Upload progress modal -->
|
||||||
|
<div id="uploadModal" class="upload-modal">
|
||||||
|
<div class="upload-modal-header">
|
||||||
|
<span><i class="fas fa-upload"></i> Uploading Files</span>
|
||||||
|
<button class="close-modal-btn" onclick="closeUploadModal()">
|
||||||
|
<i class="fas fa-times"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="upload-modal-body">
|
||||||
|
<div id="uploadStatusMessage" class="text-sm text-gray-700 mb-2"></div>
|
||||||
|
<div id="uploadProgressContainer" class="space-y-2"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container mx-auto px-4 py-8">
|
<div class="container mx-auto px-4 py-8">
|
||||||
<h2 class="text-3xl font-bold mb-6">File Records</h2>
|
<h2 class="text-3xl font-bold mb-6">File Records</h2>
|
||||||
|
|
||||||
@@ -673,6 +789,76 @@
|
|||||||
alert(`Error reprocessing files: ${error.message}`);
|
alert(`Error reprocessing files: ${error.message}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===== Drag-and-Drop Upload Functionality =====
|
||||||
|
const dropOverlay = document.getElementById('dropOverlay');
|
||||||
|
const uploadModal = document.getElementById('uploadModal');
|
||||||
|
const uploadStatusMessage = document.getElementById('uploadStatusMessage');
|
||||||
|
const uploadProgressContainer = document.getElementById('uploadProgressContainer');
|
||||||
|
|
||||||
|
let dragCounter = 0; // Track nested drag events
|
||||||
|
|
||||||
|
// Show overlay when dragging files over the window
|
||||||
|
window.addEventListener('dragenter', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dragCounter++;
|
||||||
|
|
||||||
|
// Only show overlay if dragging files
|
||||||
|
if (e.dataTransfer.types.includes('Files')) {
|
||||||
|
dropOverlay.classList.add('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('dragleave', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dragCounter--;
|
||||||
|
|
||||||
|
if (dragCounter === 0) {
|
||||||
|
dropOverlay.classList.remove('active');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('dragover', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = 'copy';
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener('drop', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
dragCounter = 0;
|
||||||
|
dropOverlay.classList.remove('active');
|
||||||
|
|
||||||
|
if (e.dataTransfer.files.length > 0) {
|
||||||
|
// Show upload modal
|
||||||
|
uploadModal.classList.add('active');
|
||||||
|
uploadProgressContainer.innerHTML = '';
|
||||||
|
|
||||||
|
// Process the dropped files
|
||||||
|
processFiles(e.dataTransfer.files, uploadProgressContainer, uploadStatusMessage);
|
||||||
|
|
||||||
|
// Optionally reload page after uploads complete (with a delay)
|
||||||
|
setTimeout(() => {
|
||||||
|
const fileStatuses = document.querySelectorAll('.file-status');
|
||||||
|
let allCompleted = true;
|
||||||
|
fileStatuses.forEach(status => {
|
||||||
|
if (!status.textContent.includes('Success') && !status.textContent.includes('Error')) {
|
||||||
|
allCompleted = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (allCompleted && fileStatuses.length > 0) {
|
||||||
|
// Refresh the page after a short delay to show the new files
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.reload();
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeUploadModal() {
|
||||||
|
uploadModal.classList.remove('active');
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
+13
-174
@@ -37,55 +37,8 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block scripts %}
|
{% block scripts %}
|
||||||
|
<script src="/static/js/upload.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Configuration
|
|
||||||
const MAX_FILE_SIZE = 500 * 1024 * 1024; // 500MB
|
|
||||||
|
|
||||||
// Allowed file types
|
|
||||||
const ACCEPTED_TYPES = {
|
|
||||||
// PDF files
|
|
||||||
'application/pdf': true,
|
|
||||||
|
|
||||||
// Image formats
|
|
||||||
'image/jpeg': true, 'image/jpg': true, 'image/png': true,
|
|
||||||
'image/gif': true, 'image/bmp': true, 'image/tiff': true,
|
|
||||||
'image/webp': true, 'image/svg+xml': true,
|
|
||||||
|
|
||||||
// Office document formats - Word
|
|
||||||
'application/msword': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.template': true,
|
|
||||||
'application/vnd.ms-word.document.macroEnabled.12': true,
|
|
||||||
|
|
||||||
// Excel
|
|
||||||
'application/vnd.ms-excel': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.template': true,
|
|
||||||
'application/vnd.ms-excel.sheet.macroEnabled.12': true,
|
|
||||||
|
|
||||||
// PowerPoint
|
|
||||||
'application/vnd.ms-powerpoint': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.template': true,
|
|
||||||
'application/vnd.openxmlformats-officedocument.presentationml.slideshow': true,
|
|
||||||
|
|
||||||
// Other common formats
|
|
||||||
'text/plain': true,
|
|
||||||
'text/csv': true,
|
|
||||||
'application/rtf': true,
|
|
||||||
'text/rtf': true,
|
|
||||||
'text/html': true,
|
|
||||||
'application/xml': true,
|
|
||||||
'text/xml': true
|
|
||||||
};
|
|
||||||
|
|
||||||
// File extensions that are always allowed (even if mime type is not recognized)
|
|
||||||
const ACCEPTED_EXTENSIONS = [
|
|
||||||
'.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx',
|
|
||||||
'.odt', '.ods', '.odp', '.rtf', '.txt', '.csv',
|
|
||||||
'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp', '.svg', '.md'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Drag-and-drop / file input logic
|
// Drag-and-drop / file input logic
|
||||||
const dropZone = document.getElementById("dropZone");
|
const dropZone = document.getElementById("dropZone");
|
||||||
const fileInput = document.getElementById("fileInput");
|
const fileInput = document.getElementById("fileInput");
|
||||||
@@ -109,21 +62,6 @@
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
dropZone.classList.remove("bg-gray-100");
|
dropZone.classList.remove("bg-gray-100");
|
||||||
if (e.dataTransfer.files.length) {
|
if (e.dataTransfer.files.length) {
|
||||||
processFiles(e.dataTransfer.files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleFileSelect(e) {
|
|
||||||
if (e.target.files.length) {
|
|
||||||
processFiles(e.target.files);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function processFiles(files) {
|
|
||||||
if (files.length === 0) return;
|
|
||||||
|
|
||||||
statusMessage.textContent = `Processing ${files.length} file(s)...`;
|
|
||||||
|
|
||||||
// Clear previous upload progress
|
// Clear previous upload progress
|
||||||
uploadProgress.innerHTML = "";
|
uploadProgress.innerHTML = "";
|
||||||
|
|
||||||
@@ -132,123 +70,24 @@
|
|||||||
progressContainer.className = "space-y-2";
|
progressContainer.className = "space-y-2";
|
||||||
uploadProgress.appendChild(progressContainer);
|
uploadProgress.appendChild(progressContainer);
|
||||||
|
|
||||||
// Process each file
|
// Use the shared processFiles function
|
||||||
for (let i = 0; i < files.length; i++) {
|
processFiles(e.dataTransfer.files, progressContainer, statusMessage);
|
||||||
const file = files[i];
|
|
||||||
validateAndUpload(file, progressContainer);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateAndUpload(file, progressContainer) {
|
function handleFileSelect(e) {
|
||||||
// Create progress element for this file
|
if (e.target.files.length) {
|
||||||
const fileProgress = document.createElement("div");
|
// Clear previous upload progress
|
||||||
fileProgress.className = "flex flex-col mb-2";
|
uploadProgress.innerHTML = "";
|
||||||
fileProgress.innerHTML = `
|
|
||||||
<div class="flex justify-between">
|
|
||||||
<span class="text-sm truncate" title="${file.name}">${file.name}</span>
|
|
||||||
<span class="text-xs text-gray-500">${formatFileSize(file.size)}</span>
|
|
||||||
</div>
|
|
||||||
<div class="w-full bg-gray-200 h-2 rounded-full mt-1">
|
|
||||||
<div class="file-progress-bar bg-blue-500 h-2 rounded-full" style="width: 0%"></div>
|
|
||||||
</div>
|
|
||||||
<div class="file-status text-xs text-gray-600 mt-1">Validating...</div>
|
|
||||||
`;
|
|
||||||
progressContainer.appendChild(fileProgress);
|
|
||||||
|
|
||||||
const progressBar = fileProgress.querySelector(".file-progress-bar");
|
// Create progress container
|
||||||
const statusEl = fileProgress.querySelector(".file-status");
|
const progressContainer = document.createElement("div");
|
||||||
|
progressContainer.className = "space-y-2";
|
||||||
|
uploadProgress.appendChild(progressContainer);
|
||||||
|
|
||||||
// Validate file type by checking both MIME type and extension
|
// Use the shared processFiles function
|
||||||
const isValidMimeType = ACCEPTED_TYPES[file.type] || false;
|
processFiles(e.target.files, progressContainer, statusMessage);
|
||||||
const fileExtension = '.' + file.name.split('.').pop().toLowerCase();
|
|
||||||
const isValidExtension = ACCEPTED_EXTENSIONS.includes(fileExtension);
|
|
||||||
|
|
||||||
if (!isValidMimeType && !isValidExtension) {
|
|
||||||
statusEl.textContent = `Error: ${file.name} - Unsupported file type`;
|
|
||||||
statusEl.className = "text-xs text-red-500 mt-1";
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate file size
|
|
||||||
if (file.size > MAX_FILE_SIZE) {
|
|
||||||
statusEl.textContent = `Error: ${file.name} - File size exceeds 500MB limit`;
|
|
||||||
statusEl.className = "text-xs text-red-500 mt-1";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload the file
|
|
||||||
uploadFile(file, progressBar, statusEl);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function uploadFile(file, progressBar, statusEl) {
|
|
||||||
statusEl.textContent = `Uploading...`;
|
|
||||||
try {
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("file", file);
|
|
||||||
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("POST", "/api/ui-upload", true);
|
|
||||||
|
|
||||||
xhr.upload.onprogress = (e) => {
|
|
||||||
if (e.lengthComputable) {
|
|
||||||
const percentComplete = (e.loaded / e.total) * 100;
|
|
||||||
progressBar.style.width = percentComplete + "%";
|
|
||||||
statusEl.textContent = `Uploading: ${Math.round(percentComplete)}%`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onload = function() {
|
|
||||||
if (xhr.status === 200) {
|
|
||||||
const result = JSON.parse(xhr.responseText);
|
|
||||||
progressBar.style.width = "100%";
|
|
||||||
progressBar.className = "file-progress-bar bg-green-500 h-2 rounded-full";
|
|
||||||
statusEl.textContent = `Success: Task ID: ${result.task_id}`;
|
|
||||||
statusEl.className = "text-xs text-green-600 mt-1";
|
|
||||||
updateOverallStatus();
|
|
||||||
} else {
|
|
||||||
throw new Error(`Upload failed with status ${xhr.status}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.onerror = function() {
|
|
||||||
throw new Error("Network error occurred");
|
|
||||||
};
|
|
||||||
|
|
||||||
xhr.send(formData);
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
statusEl.textContent = `Error: ${err.message}`;
|
|
||||||
statusEl.className = "text-xs text-red-500 mt-1";
|
|
||||||
progressBar.className = "file-progress-bar bg-red-500 h-2 rounded-full";
|
|
||||||
updateOverallStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateOverallStatus() {
|
|
||||||
// Count success/failure
|
|
||||||
const fileStatuses = document.querySelectorAll('.file-status');
|
|
||||||
let completed = 0;
|
|
||||||
let total = fileStatuses.length;
|
|
||||||
|
|
||||||
fileStatuses.forEach(status => {
|
|
||||||
if (status.textContent.includes('Success') || status.textContent.includes('Error')) {
|
|
||||||
completed++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (completed === total) {
|
|
||||||
statusMessage.textContent = `All uploads completed (${completed}/${total})`;
|
|
||||||
} else {
|
|
||||||
statusMessage.textContent = `Uploading files (${completed}/${total})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatFileSize(bytes) {
|
|
||||||
if (bytes === 0) return '0 Bytes';
|
|
||||||
const k = 1024;
|
|
||||||
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
|
|
||||||
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
||||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -68,3 +68,25 @@ class TestFilesView:
|
|||||||
# The key test: ensure the min/max functions work in the template
|
# The key test: ensure the min/max functions work in the template
|
||||||
# (they're used for pagination on lines 397 and 406 of files.html)
|
# (they're used for pagination on lines 397 and 406 of files.html)
|
||||||
_assert_no_template_errors(content)
|
_assert_no_template_errors(content)
|
||||||
|
|
||||||
|
def test_files_view_includes_drag_drop_elements(self, client: TestClient, db_session):
|
||||||
|
"""Test that the /files view includes drag-and-drop upload elements."""
|
||||||
|
# Access the /files view
|
||||||
|
response = client.get("/files")
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
content = response.text
|
||||||
|
|
||||||
|
# Check that drag-and-drop elements are present
|
||||||
|
assert "dropOverlay" in content, "Drop overlay element should be present"
|
||||||
|
assert "uploadModal" in content, "Upload modal element should be present"
|
||||||
|
assert "drop-overlay" in content, "Drop overlay CSS class should be present"
|
||||||
|
assert "upload-modal" in content, "Upload modal CSS class should be present"
|
||||||
|
|
||||||
|
# Check that the upload.js script is included
|
||||||
|
assert "/static/js/upload.js" in content, "upload.js script should be included"
|
||||||
|
|
||||||
|
# Check for drag-and-drop event handlers
|
||||||
|
assert "dragenter" in content or "drag" in content, "Drag event handlers should be present"
|
||||||
|
assert "Drop files anywhere to upload" in content, "Drop message should be present"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user