diff --git a/.env.example b/.env.example index a3b55c3..ff78461 100644 --- a/.env.example +++ b/.env.example @@ -27,6 +27,7 @@ DB_RETRY_INTERVAL=2 DB_DATA_PATH=/srv/docker-test/mariadb LOGS_PATH=/srv/docker-test/logs INSTANCE_PATH=/srv/docker-test/instance +BACKUP_PATH=/srv/docker-test/backups # ============================================================================ # APPLICATION CONFIGURATION diff --git a/README.md b/README.md new file mode 100644 index 0000000..04809c9 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +# Quality Recticel Application + +Production traceability and quality management system. + +## ๐Ÿ“š Documentation + +All development and deployment documentation has been moved to the **[documentation](./documentation/)** folder. + +### Quick Links + +- **[Documentation Index](./documentation/README.md)** - Complete documentation overview +- **[Database Setup](./documentation/DATABASE_DOCKER_SETUP.md)** - Database configuration guide +- **[Docker Guide](./documentation/DOCKER_QUICK_REFERENCE.md)** - Docker commands reference +- **[Backup System](./documentation/BACKUP_SYSTEM.md)** - Database backup documentation + +## ๐Ÿš€ Quick Start + +```bash +# Start application +cd /srv/quality_app/py_app +bash start_production.sh + +# Stop application +bash stop_production.sh + +# View logs +tail -f /srv/quality_app/logs/error.log +``` + +## ๐Ÿ“ฆ Docker Deployment + +```bash +# Start with Docker Compose +docker-compose up -d + +# View logs +docker-compose logs -f web + +# Stop services +docker-compose down +``` + +## ๐Ÿ” Default Access + +- **URL**: http://localhost:8781 +- **Username**: superadmin +- **Password**: superadmin123 + +## ๐Ÿ“ Project Structure + +``` +quality_app/ +โ”œโ”€โ”€ documentation/ # All documentation files +โ”œโ”€โ”€ py_app/ # Flask application +โ”œโ”€โ”€ backups/ # Database backups +โ”œโ”€โ”€ logs/ # Application logs +โ”œโ”€โ”€ docker-compose.yml # Docker configuration +โ””โ”€โ”€ Dockerfile # Container image definition +``` + +## ๐Ÿ“– For More Information + +See the **[documentation](./documentation/)** folder for comprehensive guides on: + +- Setup and deployment +- Docker configuration +- Database management +- Backup and restore procedures +- Application features + +--- + +**Version**: 1.0.0 +**Last Updated**: November 3, 2025 diff --git a/docker-compose.yml b/docker-compose.yml index 2c51e6f..5786a61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -144,6 +144,11 @@ services: # ====================================================================== TZ: ${TZ:-Europe/Bucharest} LANG: ${LANG:-en_US.UTF-8} + + # ====================================================================== + # Backup Configuration + # ====================================================================== + BACKUP_PATH: ${BACKUP_PATH:-/srv/quality_recticel/backups} ports: - "${APP_PORT:-8781}:8781" @@ -155,6 +160,9 @@ services: # Instance configuration directory - ${INSTANCE_PATH:-/srv/docker-test/instance}:/app/instance + # Database backups directory + - ${BACKUP_PATH:-/srv/docker-test/backups}:/srv/quality_recticel/backups + # โš ๏ธ DEVELOPMENT ONLY: Mount application code for live updates # DISABLE IN PRODUCTION - causes configuration and security issues # - ./py_app:/app diff --git a/documentation/BACKUP_SYSTEM.md b/documentation/BACKUP_SYSTEM.md new file mode 100644 index 0000000..b993ae2 --- /dev/null +++ b/documentation/BACKUP_SYSTEM.md @@ -0,0 +1,205 @@ +# Database Backup System Documentation + +## Overview +The Quality Recticel application now includes a comprehensive database backup management system accessible from the Settings page for superadmin and admin users. + +## Features + +### 1. Manual Backup +- **Backup Now** button creates an immediate full database backup +- Uses `mysqldump` to create complete SQL export +- Includes all tables, triggers, routines, and events +- Each backup is timestamped: `backup_trasabilitate_YYYYMMDD_HHMMSS.sql` + +### 2. Scheduled Backups +Configure automated backups with: +- **Enable/Disable**: Toggle scheduled backups on/off +- **Backup Time**: Set time of day for automatic backup (default: 02:00) +- **Frequency**: Choose Daily, Weekly, or Monthly backups +- **Retention Period**: Automatically delete backups older than N days (default: 30 days) + +### 3. Backup Management +- **List Backups**: View all available backup files with size and creation date +- **Download**: Download any backup file to your local computer +- **Delete**: Remove old or unnecessary backup files +- **Restore**: (Superadmin only) Restore database from a backup file + +## Configuration + +### Backup Path +The backup location can be configured in three ways (priority order): + +1. **Environment Variable** (Docker): + ```yaml + # docker-compose.yml + environment: + BACKUP_PATH: /srv/quality_recticel/backups + volumes: + - /srv/docker-test/backups:/srv/quality_recticel/backups + ``` + +2. **Configuration File**: + ```ini + # py_app/instance/external_server.conf + backup_path=/srv/quality_app/backups + ``` + +3. **Default Path**: `/srv/quality_app/backups` + +### .env Configuration +Add to your `.env` file: +```bash +BACKUP_PATH=/srv/docker-test/backups +``` + +## Usage + +### Access Backup Management +1. Login as **superadmin** or **admin** +2. Navigate to **Settings** page +3. Scroll to **๐Ÿ’พ Database Backup Management** card +4. The backup management interface is only visible to superadmin/admin users + +### Create Manual Backup +1. Click **โšก Backup Now** button +2. Wait for confirmation message +3. New backup appears in the list + +### Configure Scheduled Backups +1. Check **Enable Scheduled Backups** +2. Set desired backup time (24-hour format) +3. Select frequency (Daily/Weekly/Monthly) +4. Set retention period (days to keep backups) +5. Click **๐Ÿ’พ Save Schedule** + +### Download Backup +1. Locate backup in the list +2. Click **โฌ‡๏ธ Download** button +3. File downloads to your computer + +### Delete Backup +1. Locate backup in the list +2. Click **๐Ÿ—‘๏ธ Delete** button +3. Confirm deletion + +### Restore Backup (Superadmin Only) +โš ๏ธ **WARNING**: Restore will replace current database! +1. This feature requires superadmin privileges +2. API endpoint: `/api/backup/restore/` +3. Use with extreme caution + +## Technical Details + +### Backup Module +Location: `py_app/app/database_backup.py` + +Key Class: `DatabaseBackupManager` + +Methods: +- `create_backup()`: Create new backup +- `list_backups()`: Get all backup files +- `delete_backup(filename)`: Remove backup file +- `restore_backup(filename)`: Restore from backup +- `get_backup_schedule()`: Get current schedule +- `save_backup_schedule(schedule)`: Update schedule +- `cleanup_old_backups(days)`: Remove old backups + +### API Endpoints + +| Endpoint | Method | Access | Description | +|----------|--------|--------|-------------| +| `/api/backup/create` | POST | Admin+ | Create new backup | +| `/api/backup/list` | GET | Admin+ | List all backups | +| `/api/backup/download/` | GET | Admin+ | Download backup file | +| `/api/backup/delete/` | DELETE | Admin+ | Delete backup file | +| `/api/backup/schedule` | GET/POST | Admin+ | Get/Set backup schedule | +| `/api/backup/restore/` | POST | Superadmin | Restore from backup | + +### Backup File Format +- **Format**: SQL dump file (`.sql`) +- **Compression**: Not compressed (can be gzip manually if needed) +- **Contents**: Complete database with structure and data +- **Metadata**: Stored in `backups_metadata.json` + +### Schedule Storage +Schedule configuration stored in: `{BACKUP_PATH}/backup_schedule.json` + +Example: +```json +{ + "enabled": true, + "time": "02:00", + "frequency": "daily", + "retention_days": 30 +} +``` + +## Security Considerations + +1. **Access Control**: Backup features restricted to admin and superadmin users +2. **Path Traversal Protection**: Filenames validated to prevent directory traversal attacks +3. **Credentials**: Database credentials read from `external_server.conf` +4. **Backup Location**: Should be on different mount point than application for safety + +## Maintenance + +### Disk Space +Monitor backup directory size: +```bash +du -sh /srv/quality_app/backups +``` + +### Manual Cleanup +Remove old backups manually: +```bash +find /srv/quality_app/backups -name "*.sql" -mtime +30 -delete +``` + +### Backup Verification +Test restore in development environment: +```bash +mysql -u root -p trasabilitate < backup_trasabilitate_20251103_020000.sql +``` + +## Troubleshooting + +### Backup Fails +- Check database credentials in `external_server.conf` +- Ensure `mysqldump` is installed +- Verify write permissions on backup directory +- Check disk space availability + +### Scheduled Backups Not Running +- TODO: Implement scheduled backup daemon/cron job +- Check backup schedule is enabled +- Verify time format is correct (HH:MM) + +### Cannot Download Backup +- Check backup file exists +- Verify file permissions +- Ensure adequate network bandwidth + +## Future Enhancements + +### Planned Features (Task 4) +- [ ] Implement APScheduler for automated scheduled backups +- [ ] Add backup to external storage (S3, FTP, etc.) +- [ ] Email notifications for backup success/failure +- [ ] Backup compression (gzip) +- [ ] Incremental backups +- [ ] Backup encryption +- [ ] Backup verification tool + +## Support + +For issues or questions about the backup system: +1. Check application logs: `/srv/quality_app/logs/error.log` +2. Verify backup directory permissions +3. Test manual backup first before relying on scheduled backups +4. Keep at least 2 recent backups before deleting old ones + +--- + +**Created**: November 3, 2025 +**Module**: Database Backup Management +**Version**: 1.0.0 diff --git a/DATABASE_DOCKER_SETUP.md b/documentation/DATABASE_DOCKER_SETUP.md similarity index 100% rename from DATABASE_DOCKER_SETUP.md rename to documentation/DATABASE_DOCKER_SETUP.md diff --git a/DOCKER_IMPROVEMENTS.md b/documentation/DOCKER_IMPROVEMENTS.md similarity index 100% rename from DOCKER_IMPROVEMENTS.md rename to documentation/DOCKER_IMPROVEMENTS.md diff --git a/DOCKER_QUICK_REFERENCE.md b/documentation/DOCKER_QUICK_REFERENCE.md similarity index 100% rename from DOCKER_QUICK_REFERENCE.md rename to documentation/DOCKER_QUICK_REFERENCE.md diff --git a/documentation/README.md b/documentation/README.md new file mode 100644 index 0000000..1c3e0e0 --- /dev/null +++ b/documentation/README.md @@ -0,0 +1,148 @@ +# Quality Recticel Application - Documentation + +This folder contains all development and deployment documentation for the Quality Recticel application. + +## Documentation Index + +### Setup & Deployment + +- **[DATABASE_DOCKER_SETUP.md](./DATABASE_DOCKER_SETUP.md)** - Complete guide for database configuration and Docker setup +- **[DOCKER_IMPROVEMENTS.md](./DOCKER_IMPROVEMENTS.md)** - Detailed changelog of Docker-related improvements and optimizations +- **[DOCKER_QUICK_REFERENCE.md](./DOCKER_QUICK_REFERENCE.md)** - Quick reference guide for common Docker commands and operations + +### Features & Systems + +- **[BACKUP_SYSTEM.md](./BACKUP_SYSTEM.md)** - Database backup management system documentation + - Manual and scheduled backups + - Backup configuration and management + - Restore procedures + +## Quick Links + +### Application Structure + +``` +quality_app/ +โ”œโ”€โ”€ py_app/ # Python application code +โ”‚ โ”œโ”€โ”€ app/ # Flask application modules +โ”‚ โ”‚ โ”œโ”€โ”€ __init__.py # App factory +โ”‚ โ”‚ โ”œโ”€โ”€ routes.py # Main routes +โ”‚ โ”‚ โ”œโ”€โ”€ daily_mirror.py # Daily Mirror module +โ”‚ โ”‚ โ”œโ”€โ”€ database_backup.py # Backup system +โ”‚ โ”‚ โ”œโ”€โ”€ templates/ # HTML templates +โ”‚ โ”‚ โ””โ”€โ”€ static/ # CSS, JS, images +โ”‚ โ”œโ”€โ”€ instance/ # Configuration files +โ”‚ โ””โ”€โ”€ requirements.txt # Python dependencies +โ”œโ”€โ”€ backups/ # Database backups +โ”œโ”€โ”€ logs/ # Application logs +โ”œโ”€โ”€ documentation/ # This folder +โ””โ”€โ”€ docker-compose.yml # Docker configuration +``` + +### Key Configuration Files + +- `py_app/instance/external_server.conf` - Database connection settings +- `docker-compose.yml` - Docker services configuration +- `.env` - Environment variables (create from .env.example) +- `py_app/gunicorn.conf.py` - Gunicorn WSGI server settings + +### Access Levels + +The application uses a 4-tier role system: + +1. **Superadmin** (Level 100) - Full system access +2. **Admin** (Level 90) - Administrative access +3. **Manager** (Level 70) - Module management +4. **Worker** (Level 50) - Basic operations + +### Modules + +- **Quality** - Production scanning and quality reports +- **Warehouse** - Warehouse management +- **Labels** - Label printing and management +- **Daily Mirror** - Business intelligence and reporting + +## Development Notes + +### Recent Changes (November 2025) + +1. **SQLAlchemy Removal** - Simplified to direct MariaDB connections +2. **Daily Mirror Module** - Fully integrated with access control +3. **Backup System** - Complete database backup management +4. **Access Control** - Superadmin gets automatic full access +5. **Docker Optimization** - Production-ready configuration + +### Common Tasks + +**Start Application:** +```bash +cd /srv/quality_app/py_app +bash start_production.sh +``` + +**Stop Application:** +```bash +cd /srv/quality_app/py_app +bash stop_production.sh +``` + +**View Logs:** +```bash +tail -f /srv/quality_app/logs/error.log +tail -f /srv/quality_app/logs/access.log +``` + +**Create Backup:** +- Login as superadmin/admin +- Go to Settings page +- Click "Backup Now" button + +**Check Application Status:** +```bash +ps aux | grep gunicorn | grep trasabilitate +``` + +## Support & Maintenance + +### Log Locations + +- **Access Log**: `/srv/quality_app/logs/access.log` +- **Error Log**: `/srv/quality_app/logs/error.log` +- **Backup Location**: `/srv/quality_app/backups/` + +### Database + +- **Host**: localhost (or as configured) +- **Port**: 3306 +- **Database**: trasabilitate +- **User**: trasabilitate + +### Default Login + +- **Username**: superadmin +- **Password**: superadmin123 + +โš ๏ธ **Change default credentials in production!** + +## Contributing + +When adding new documentation: + +1. Place markdown files in this folder +2. Update this README with links +3. Use clear, descriptive filenames +4. Include date and version when applicable + +## Version History + +- **v1.0.0** (November 2025) - Initial production release + - Docker deployment ready + - Backup system implemented + - Daily Mirror module integrated + - SQLAlchemy removed + +--- + +**Last Updated**: November 3, 2025 +**Application**: Quality Recticel Traceability System +**Technology Stack**: Flask, MariaDB, Gunicorn, Docker diff --git a/logs/access.log b/logs/access.log index 9fea05e..87609c9 100644 --- a/logs/access.log +++ b/logs/access.log @@ -433,3 +433,72 @@ 192.168.0.132 - - [22/Oct/2025:11:22:49 +0300] "GET / HTTP/1.1" 200 1627 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" 2032 192.168.0.132 - - [22/Oct/2025:11:22:50 +0300] "GET /favicon.ico HTTP/1.1" 404 207 "https://quality.moto-adv.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" 1773 192.168.0.132 - - [22/Oct/2025:18:45:12 +0300] "GET /favicon.ico HTTP/1.1" 404 207 "https://quality.moto-adv.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36" 1959 +127.0.0.1 - - [03/Nov/2025:20:06:54 +0200] "GET / HTTP/1.1" 200 1688 "-" "curl/8.14.1" 63649 ยตs +192.168.0.132 - - [03/Nov/2025:20:09:42 +0200] "GET /user_management_simple HTTP/1.1" 200 45867 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 113475 ยตs +127.0.0.1 - - [03/Nov/2025:20:16:12 +0200] "GET / HTTP/1.1" 200 1688 "-" "curl/8.14.1" 64029 ยตs +192.168.0.132 - - [03/Nov/2025:20:16:45 +0200] "GET /user_management_simple HTTP/1.1" 200 46876 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 62091 ยตs +192.168.0.132 - - [03/Nov/2025:20:17:23 +0200] "POST /edit_user_simple HTTP/1.1" 302 233 "https://quality.moto-adv.com/user_management_simple" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 31144 ยตs +192.168.0.132 - - [03/Nov/2025:20:17:24 +0200] "GET /user_management_simple HTTP/1.1" 200 47317 "https://quality.moto-adv.com/user_management_simple" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 96688 ยตs +192.168.0.132 - - [03/Nov/2025:20:17:44 +0200] "POST /edit_user_simple HTTP/1.1" 302 233 "https://quality.moto-adv.com/user_management_simple" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 9053 ยตs +192.168.0.132 - - [03/Nov/2025:20:17:44 +0200] "GET /user_management_simple HTTP/1.1" 200 47635 "https://quality.moto-adv.com/user_management_simple" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 6762 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:17 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/user_management_simple" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 12037 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:20 +0200] "GET /warehouse HTTP/1.1" 200 2987 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 65997 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:29 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/warehouse" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 13728 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:31 +0200] "GET /etichete HTTP/1.1" 200 3204 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 9236 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:31 +0200] "GET /static/css/print_module.css HTTP/1.1" 200 0 "https://quality.moto-adv.com/etichete" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 13002 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:33 +0200] "GET /upload_data HTTP/1.1" 200 11226 "https://quality.moto-adv.com/etichete" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 21495 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:43 +0200] "GET /logout HTTP/1.1" 302 189 "https://quality.moto-adv.com/upload_data" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2430 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:43 +0200] "GET / HTTP/1.1" 200 1688 "https://quality.moto-adv.com/upload_data" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 8582 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:49 +0200] "POST / HTTP/1.1" 302 207 "https://quality.moto-adv.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 6791 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:49 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2657 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:54 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 7222 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:54 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2610 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:59 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 29065 ยตs +192.168.0.132 - - [03/Nov/2025:20:18:59 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 51296 ยตs +192.168.0.132 - - [03/Nov/2025:20:19:45 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 8535 ยตs +192.168.0.132 - - [03/Nov/2025:20:19:45 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 12117 ยตs +127.0.0.1 - - [03/Nov/2025:20:21:14 +0200] "GET /daily_mirror/ HTTP/1.1" 302 189 "-" "curl/8.14.1" 2105 ยตs +127.0.0.1 - - [03/Nov/2025:20:24:47 +0200] "GET /daily_mirror/ HTTP/1.1" 302 189 "-" "curl/8.14.1" 20248 ยตs +192.168.0.132 - - [03/Nov/2025:20:24:57 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 29323 ยตs +192.168.0.132 - - [03/Nov/2025:20:24:57 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 51985 ยตs +192.168.0.132 - - [03/Nov/2025:20:25:00 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2846 ยตs +192.168.0.132 - - [03/Nov/2025:20:25:02 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2657 ยตs +192.168.0.132 - - [03/Nov/2025:20:25:03 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 7317 ยตs +192.168.0.132 - - [03/Nov/2025:20:25:03 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 70160 ยตs +192.168.0.132 - - [03/Nov/2025:20:25:10 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 3335 ยตs +192.168.0.132 - - [03/Nov/2025:20:26:57 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 69436 ยตs +192.168.0.132 - - [03/Nov/2025:20:26:58 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 11113 ยตs +192.168.0.132 - - [03/Nov/2025:20:26:58 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2802 ยตs +192.168.0.132 - - [03/Nov/2025:20:27:00 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 29247 ยตs +192.168.0.132 - - [03/Nov/2025:20:27:00 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 50880 ยตs +127.0.0.1 - - [03/Nov/2025:20:28:14 +0200] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.14.1" 63789 ยตs +192.168.0.132 - - [03/Nov/2025:20:30:34 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 69748 ยตs +192.168.0.132 - - [03/Nov/2025:20:30:36 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 20964 ยตs +192.168.0.132 - - [03/Nov/2025:20:30:36 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 51986 ยตs +192.168.0.132 - - [03/Nov/2025:20:30:39 +0200] "GET /settings HTTP/1.1" 200 10631 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 31292 ยตs +192.168.0.132 - - [03/Nov/2025:20:31:04 +0200] "GET /user_management_simple HTTP/1.1" 200 47635 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 117040 ยตs +127.0.0.1 - - [03/Nov/2025:20:42:01 +0200] "HEAD / HTTP/1.1" 200 0 "-" "curl/8.14.1" 63181 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:16 +0200] "GET /settings HTTP/1.1" 200 10631 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 87361 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:22 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 12878 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:23 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 8459 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:24 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2708 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:25 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 7038 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:25 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2642 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:30 +0200] "GET /logout HTTP/1.1" 302 189 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2612 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:30 +0200] "GET / HTTP/1.1" 200 1688 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 8570 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:35 +0200] "POST / HTTP/1.1" 302 207 "https://quality.moto-adv.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 7549 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:35 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2668 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:37 +0200] "GET /daily_mirror/main HTTP/1.1" 302 207 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 6891 ยตs +192.168.0.132 - - [03/Nov/2025:20:42:37 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2666 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:16 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2631 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:18 +0200] "GET /daily_mirror/main HTTP/1.1" 200 10549 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 18688 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:21 +0200] "GET /daily_mirror/build_database HTTP/1.1" 200 31802 "https://quality.moto-adv.com/daily_mirror/main" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 56457 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:21 +0200] "GET /static/daily_mirror_tune.css HTTP/1.1" 404 207 "https://quality.moto-adv.com/daily_mirror/build_database" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 5332 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:30 +0200] "GET /daily_mirror/main HTTP/1.1" 200 10549 "https://quality.moto-adv.com/daily_mirror/main" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 77877 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:32 +0200] "GET /daily_mirror/main HTTP/1.1" 200 10549 "https://quality.moto-adv.com/daily_mirror/main" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 6436 ยตs +192.168.0.132 - - [03/Nov/2025:20:45:35 +0200] "GET /dashboard HTTP/1.1" 200 3827 "https://quality.moto-adv.com/daily_mirror/main" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 2671 ยตs +192.168.0.132 - - [03/Nov/2025:20:46:50 +0200] "GET /settings HTTP/1.1" 200 10631 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 87865 ยตs +127.0.0.1 - - [03/Nov/2025:21:08:25 +0200] "GET /settings HTTP/1.1" 302 189 "-" "curl/8.14.1" 19562 ยตs +192.168.0.132 - - [03/Nov/2025:21:08:33 +0200] "GET /settings HTTP/1.1" 200 19546 "https://quality.moto-adv.com/dashboard" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 94529 ยตs +192.168.0.132 - - [03/Nov/2025:21:08:33 +0200] "GET /api/backup/list HTTP/1.1" 200 30 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 21173 ยตs +192.168.0.132 - - [03/Nov/2025:21:08:33 +0200] "GET /api/backup/schedule HTTP/1.1" 200 101 "https://quality.moto-adv.com/settings" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36" 23522 ยตs diff --git a/logs/error.log b/logs/error.log index 7ab5b28..b4ee5ce 100644 --- a/logs/error.log +++ b/logs/error.log @@ -125,3 +125,773 @@ [2025-10-22 20:52:12 +0300] [299445] [INFO] Worker exiting (pid: 299445) [2025-10-22 20:52:12 +0300] [299443] [INFO] Worker exiting (pid: 299443) [2025-10-22 20:52:13 +0300] [299414] [INFO] Shutting down: Master +[2025-11-03 20:05:59 +0200] [395583] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:05:59 +0200] [395583] [INFO] ============================================================ +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:05:59 +0200] [395583] [INFO] ============================================================ +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:05:59 +0200] [395583] [INFO] โ€ข Workers: 9 +[2025-11-03 20:05:59 +0200] [395583] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:05:59 +0200] [395583] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:05:59 +0200] [395583] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:05:59 +0200] [395583] [INFO] โ€ข Preload App: True +[2025-11-03 20:05:59 +0200] [395583] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:05:59 +0200] [395583] [INFO] ============================================================ +[2025-11-03 20:05:59 +0200] [395583] [INFO] Listening at: http://0.0.0.0:8781 (395583) +[2025-11-03 20:05:59 +0200] [395583] [INFO] Using worker: sync +[2025-11-03 20:05:59 +0200] [395583] [INFO] ============================================================ +[2025-11-03 20:05:59 +0200] [395583] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:05:59 +0200] [395583] [INFO] ============================================================ +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:05:59 +0200] [395608] [INFO] Booting worker with pid: 395608 +[2025-11-03 20:05:59 +0200] [395608] [INFO] โœจ Worker spawned successfully (pid: 395608) +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:05:59 +0200] [395609] [INFO] Booting worker with pid: 395609 +[2025-11-03 20:05:59 +0200] [395609] [INFO] โœจ Worker spawned successfully (pid: 395609) +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:05:59 +0200] [395610] [INFO] Booting worker with pid: 395610 +[2025-11-03 20:05:59 +0200] [395610] [INFO] โœจ Worker spawned successfully (pid: 395610) +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:05:59 +0200] [395611] [INFO] Booting worker with pid: 395611 +[2025-11-03 20:05:59 +0200] [395611] [INFO] โœจ Worker spawned successfully (pid: 395611) +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:05:59 +0200] [395612] [INFO] Booting worker with pid: 395612 +[2025-11-03 20:05:59 +0200] [395612] [INFO] โœจ Worker spawned successfully (pid: 395612) +[2025-11-03 20:05:59 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:05:59 +0200] [395613] [INFO] Booting worker with pid: 395613 +[2025-11-03 20:05:59 +0200] [395613] [INFO] โœจ Worker spawned successfully (pid: 395613) +[2025-11-03 20:06:00 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:06:00 +0200] [395614] [INFO] Booting worker with pid: 395614 +[2025-11-03 20:06:00 +0200] [395614] [INFO] โœจ Worker spawned successfully (pid: 395614) +[2025-11-03 20:06:00 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:06:00 +0200] [395615] [INFO] Booting worker with pid: 395615 +[2025-11-03 20:06:00 +0200] [395615] [INFO] โœจ Worker spawned successfully (pid: 395615) +[2025-11-03 20:06:00 +0200] [395583] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:06:00 +0200] [395616] [INFO] Booting worker with pid: 395616 +[2025-11-03 20:06:00 +0200] [395616] [INFO] โœจ Worker spawned successfully (pid: 395616) +[2025-11-03 20:16:00 +0200] [395610] [INFO] Worker exiting (pid: 395610) +[2025-11-03 20:16:00 +0200] [395609] [INFO] Worker exiting (pid: 395609) +[2025-11-03 20:16:00 +0200] [395608] [INFO] Worker exiting (pid: 395608) +[2025-11-03 20:16:00 +0200] [395583] [INFO] Handling signal: term +[2025-11-03 20:16:00 +0200] [395613] [INFO] Worker exiting (pid: 395613) +[2025-11-03 20:16:00 +0200] [395611] [INFO] Worker exiting (pid: 395611) +[2025-11-03 20:16:00 +0200] [395614] [INFO] Worker exiting (pid: 395614) +[2025-11-03 20:16:00 +0200] [395612] [INFO] Worker exiting (pid: 395612) +[2025-11-03 20:16:00 +0200] [395615] [INFO] Worker exiting (pid: 395615) +[2025-11-03 20:16:00 +0200] [395616] [INFO] Worker exiting (pid: 395616) +Traceback (most recent call last): + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 223, in run + handler() + ~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 256, in handle_term + raise StopIteration +StopIteration + +During handling of the above exception, another exception occurred: + +Traceback (most recent call last): + File "/srv/quality_recticel/recticel/bin/gunicorn", line 8, in + sys.exit(run()) + ~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 66, in run + WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]", prog=prog).run() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 235, in run + super().run() + ~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 71, in run + Arbiter(self).run() + ~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 226, in run + self.halt() + ~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 341, in halt + self.stop() + ~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 395, in stop + time.sleep(0.1) + ~~~~~~~~~~^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 241, in handle_chld + self.reap_workers() + ~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 559, in reap_workers + self.cfg.child_exit(self, worker) + ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^ + File "/srv/quality_app/py_app/gunicorn.conf.py", line 167, in child_exit + server.log.info("๐Ÿ‘‹ Worker %s exited", worker.pid) + +AttributeError: 'WorkerTmp' object has no attribute 'last_mtime' +[2025-11-03 20:16:05 +0200] [395971] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:16:05 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:16:05 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:16:05 +0200] [395971] [INFO] โ€ข Workers: 9 +[2025-11-03 20:16:05 +0200] [395971] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:16:05 +0200] [395971] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:16:05 +0200] [395971] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:16:05 +0200] [395971] [INFO] โ€ข Preload App: True +[2025-11-03 20:16:05 +0200] [395971] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:16:05 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:16:05 +0200] [395971] [INFO] Listening at: http://0.0.0.0:8781 (395971) +[2025-11-03 20:16:05 +0200] [395971] [INFO] Using worker: sync +[2025-11-03 20:16:05 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:16:05 +0200] [395971] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:16:05 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:05 +0200] [395976] [INFO] Booting worker with pid: 395976 +[2025-11-03 20:16:05 +0200] [395976] [INFO] โœจ Worker spawned successfully (pid: 395976) +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:05 +0200] [395977] [INFO] Booting worker with pid: 395977 +[2025-11-03 20:16:05 +0200] [395977] [INFO] โœจ Worker spawned successfully (pid: 395977) +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:05 +0200] [395978] [INFO] Booting worker with pid: 395978 +[2025-11-03 20:16:05 +0200] [395978] [INFO] โœจ Worker spawned successfully (pid: 395978) +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:05 +0200] [395979] [INFO] Booting worker with pid: 395979 +[2025-11-03 20:16:05 +0200] [395979] [INFO] โœจ Worker spawned successfully (pid: 395979) +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:05 +0200] [395980] [INFO] Booting worker with pid: 395980 +[2025-11-03 20:16:05 +0200] [395980] [INFO] โœจ Worker spawned successfully (pid: 395980) +[2025-11-03 20:16:05 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:05 +0200] [395981] [INFO] Booting worker with pid: 395981 +[2025-11-03 20:16:05 +0200] [395981] [INFO] โœจ Worker spawned successfully (pid: 395981) +[2025-11-03 20:16:06 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:06 +0200] [395982] [INFO] Booting worker with pid: 395982 +[2025-11-03 20:16:06 +0200] [395982] [INFO] โœจ Worker spawned successfully (pid: 395982) +[2025-11-03 20:16:06 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:06 +0200] [395983] [INFO] Booting worker with pid: 395983 +[2025-11-03 20:16:06 +0200] [395983] [INFO] โœจ Worker spawned successfully (pid: 395983) +[2025-11-03 20:16:06 +0200] [395971] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:16:06 +0200] [395984] [INFO] Booting worker with pid: 395984 +[2025-11-03 20:16:06 +0200] [395984] [INFO] โœจ Worker spawned successfully (pid: 395984) +Session user: superadmin superadmin +Session user: superadmin superadmin +All form data received: {'username': 'superadmin', 'password': 'Vanessa_13/05'} +Raw form input: 'superadmin' 'Vanessa_13/05' +External DB query result (with modules): ('superadmin', 'Vanessa_13/05', 'superadmin', None) +Logged in as: superadmin superadmin modules: ['quality', 'warehouse', 'labels'] +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +[2025-11-03 20:23:46 +0200] [395971] [INFO] Handling signal: term +[2025-11-03 20:23:46 +0200] [395977] [INFO] Worker exiting (pid: 395977) +[2025-11-03 20:23:46 +0200] [395976] [INFO] Worker exiting (pid: 395976) +[2025-11-03 20:23:46 +0200] [395978] [INFO] Worker exiting (pid: 395978) +[2025-11-03 20:23:46 +0200] [395979] [INFO] Worker exiting (pid: 395979) +[2025-11-03 20:23:46 +0200] [395980] [INFO] Worker exiting (pid: 395980) +[2025-11-03 20:23:46 +0200] [395981] [INFO] Worker exiting (pid: 395981) +[2025-11-03 20:23:46 +0200] [395982] [INFO] Worker exiting (pid: 395982) +[2025-11-03 20:23:46 +0200] [395983] [INFO] Worker exiting (pid: 395983) +[2025-11-03 20:23:46 +0200] [395984] [INFO] Worker exiting (pid: 395984) +[2025-11-03 20:23:47 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395980 exited +[2025-11-03 20:23:47 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395977 exited +[2025-11-03 20:23:47 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395976 exited +[2025-11-03 20:23:47 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395981 exited +[2025-11-03 20:23:47 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395982 exited +[2025-11-03 20:23:48 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395979 exited +[2025-11-03 20:23:48 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395984 exited +[2025-11-03 20:23:48 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395978 exited +[2025-11-03 20:23:48 +0200] [395971] [INFO] ๐Ÿ‘‹ Worker 395983 exited +[2025-11-03 20:23:48 +0200] [395971] [INFO] Shutting down: Master +[2025-11-03 20:23:48 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:23:48 +0200] [395971] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 20:23:48 +0200] [395971] [INFO] ============================================================ +[2025-11-03 20:23:54 +0200] [396278] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:23:54 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:23:54 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:23:54 +0200] [396278] [INFO] โ€ข Workers: 9 +[2025-11-03 20:23:54 +0200] [396278] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:23:54 +0200] [396278] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:23:54 +0200] [396278] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:23:54 +0200] [396278] [INFO] โ€ข Preload App: True +[2025-11-03 20:23:54 +0200] [396278] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:23:54 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:23:54 +0200] [396278] [INFO] Listening at: http://0.0.0.0:8781 (396278) +[2025-11-03 20:23:54 +0200] [396278] [INFO] Using worker: sync +[2025-11-03 20:23:54 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:23:54 +0200] [396278] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:23:54 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:54 +0200] [396305] [INFO] Booting worker with pid: 396305 +[2025-11-03 20:23:54 +0200] [396305] [INFO] โœจ Worker spawned successfully (pid: 396305) +[2025-11-03 20:23:54 +0200] [396306] [INFO] Booting worker with pid: 396306 +[2025-11-03 20:23:54 +0200] [396306] [INFO] โœจ Worker spawned successfully (pid: 396306) +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:54 +0200] [396307] [INFO] Booting worker with pid: 396307 +[2025-11-03 20:23:54 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:54 +0200] [396307] [INFO] โœจ Worker spawned successfully (pid: 396307) +[2025-11-03 20:23:54 +0200] [396308] [INFO] Booting worker with pid: 396308 +[2025-11-03 20:23:54 +0200] [396308] [INFO] โœจ Worker spawned successfully (pid: 396308) +[2025-11-03 20:23:55 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:55 +0200] [396309] [INFO] Booting worker with pid: 396309 +[2025-11-03 20:23:55 +0200] [396309] [INFO] โœจ Worker spawned successfully (pid: 396309) +[2025-11-03 20:23:55 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:55 +0200] [396310] [INFO] Booting worker with pid: 396310 +[2025-11-03 20:23:55 +0200] [396310] [INFO] โœจ Worker spawned successfully (pid: 396310) +[2025-11-03 20:23:55 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:55 +0200] [396311] [INFO] Booting worker with pid: 396311 +[2025-11-03 20:23:55 +0200] [396311] [INFO] โœจ Worker spawned successfully (pid: 396311) +[2025-11-03 20:23:55 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:55 +0200] [396312] [INFO] Booting worker with pid: 396312 +[2025-11-03 20:23:55 +0200] [396312] [INFO] โœจ Worker spawned successfully (pid: 396312) +[2025-11-03 20:23:55 +0200] [396278] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:23:55 +0200] [396313] [INFO] Booting worker with pid: 396313 +[2025-11-03 20:23:55 +0200] [396313] [INFO] โœจ Worker spawned successfully (pid: 396313) +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Session user: superadmin superadmin +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Session user: superadmin superadmin +[2025-11-03 20:26:06 +0200] [396278] [INFO] Handling signal: term +[2025-11-03 20:26:06 +0200] [396307] [INFO] Worker exiting (pid: 396307) +[2025-11-03 20:26:06 +0200] [396305] [INFO] Worker exiting (pid: 396305) +[2025-11-03 20:26:06 +0200] [396306] [INFO] Worker exiting (pid: 396306) +[2025-11-03 20:26:06 +0200] [396308] [INFO] Worker exiting (pid: 396308) +[2025-11-03 20:26:06 +0200] [396309] [INFO] Worker exiting (pid: 396309) +[2025-11-03 20:26:06 +0200] [396310] [INFO] Worker exiting (pid: 396310) +[2025-11-03 20:26:06 +0200] [396311] [INFO] Worker exiting (pid: 396311) +[2025-11-03 20:26:06 +0200] [396312] [INFO] Worker exiting (pid: 396312) +[2025-11-03 20:26:06 +0200] [396313] [INFO] Worker exiting (pid: 396313) +[2025-11-03 20:26:06 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396305 exited +[2025-11-03 20:26:06 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396306 exited +[2025-11-03 20:26:06 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396308 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396312 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396309 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396307 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396310 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396311 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Worker 396313 exited +[2025-11-03 20:26:07 +0200] [396278] [INFO] Shutting down: Master +[2025-11-03 20:26:07 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:26:07 +0200] [396278] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 20:26:07 +0200] [396278] [INFO] ============================================================ +[2025-11-03 20:26:14 +0200] [396699] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:26:14 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:26:14 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:26:14 +0200] [396699] [INFO] โ€ข Workers: 9 +[2025-11-03 20:26:14 +0200] [396699] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:26:14 +0200] [396699] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:26:14 +0200] [396699] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:26:14 +0200] [396699] [INFO] โ€ข Preload App: True +[2025-11-03 20:26:14 +0200] [396699] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:26:14 +0200] [396699] [INFO] Listening at: http://0.0.0.0:8781 (396699) +[2025-11-03 20:26:14 +0200] [396699] [INFO] Using worker: sync +[2025-11-03 20:26:14 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:26:14 +0200] [396699] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:26:14 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396708] [INFO] Booting worker with pid: 396708 +[2025-11-03 20:26:14 +0200] [396708] [INFO] โœจ Worker spawned successfully (pid: 396708) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396709] [INFO] Booting worker with pid: 396709 +[2025-11-03 20:26:14 +0200] [396709] [INFO] โœจ Worker spawned successfully (pid: 396709) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396710] [INFO] Booting worker with pid: 396710 +[2025-11-03 20:26:14 +0200] [396710] [INFO] โœจ Worker spawned successfully (pid: 396710) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396711] [INFO] Booting worker with pid: 396711 +[2025-11-03 20:26:14 +0200] [396711] [INFO] โœจ Worker spawned successfully (pid: 396711) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396712] [INFO] Booting worker with pid: 396712 +[2025-11-03 20:26:14 +0200] [396712] [INFO] โœจ Worker spawned successfully (pid: 396712) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396713] [INFO] Booting worker with pid: 396713 +[2025-11-03 20:26:14 +0200] [396713] [INFO] โœจ Worker spawned successfully (pid: 396713) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396714] [INFO] Booting worker with pid: 396714 +[2025-11-03 20:26:14 +0200] [396714] [INFO] โœจ Worker spawned successfully (pid: 396714) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396719] [INFO] Booting worker with pid: 396719 +[2025-11-03 20:26:14 +0200] [396719] [INFO] โœจ Worker spawned successfully (pid: 396719) +[2025-11-03 20:26:14 +0200] [396699] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:26:14 +0200] [396725] [INFO] Booting worker with pid: 396725 +[2025-11-03 20:26:14 +0200] [396725] [INFO] โœจ Worker spawned successfully (pid: 396725) +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +[2025-11-03 20:29:44 +0200] [396699] [INFO] Handling signal: term +[2025-11-03 20:29:44 +0200] [396710] [INFO] Worker exiting (pid: 396710) +[2025-11-03 20:29:44 +0200] [396709] [INFO] Worker exiting (pid: 396709) +[2025-11-03 20:29:44 +0200] [396708] [INFO] Worker exiting (pid: 396708) +[2025-11-03 20:29:44 +0200] [396711] [INFO] Worker exiting (pid: 396711) +[2025-11-03 20:29:44 +0200] [396712] [INFO] Worker exiting (pid: 396712) +[2025-11-03 20:29:44 +0200] [396713] [INFO] Worker exiting (pid: 396713) +[2025-11-03 20:29:44 +0200] [396714] [INFO] Worker exiting (pid: 396714) +[2025-11-03 20:29:44 +0200] [396719] [INFO] Worker exiting (pid: 396719) +[2025-11-03 20:29:44 +0200] [396725] [INFO] Worker exiting (pid: 396725) +[2025-11-03 20:29:45 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396709 exited +[2025-11-03 20:29:45 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396708 exited +[2025-11-03 20:29:45 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396711 exited +[2025-11-03 20:29:45 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396713 exited +[2025-11-03 20:29:45 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396714 exited +[2025-11-03 20:29:45 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396719 exited +[2025-11-03 20:29:46 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396710 exited +[2025-11-03 20:29:46 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396725 exited +[2025-11-03 20:29:46 +0200] [396699] [INFO] ๐Ÿ‘‹ Worker 396712 exited +[2025-11-03 20:29:46 +0200] [396699] [INFO] Shutting down: Master +[2025-11-03 20:29:46 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:29:46 +0200] [396699] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 20:29:46 +0200] [396699] [INFO] ============================================================ +[2025-11-03 20:29:52 +0200] [397053] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:29:52 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:29:52 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:29:52 +0200] [397053] [INFO] โ€ข Workers: 9 +[2025-11-03 20:29:52 +0200] [397053] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:29:52 +0200] [397053] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:29:52 +0200] [397053] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:29:52 +0200] [397053] [INFO] โ€ข Preload App: True +[2025-11-03 20:29:52 +0200] [397053] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:29:52 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:29:52 +0200] [397053] [INFO] Listening at: http://0.0.0.0:8781 (397053) +[2025-11-03 20:29:52 +0200] [397053] [INFO] Using worker: sync +[2025-11-03 20:29:52 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:29:52 +0200] [397053] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:29:52 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:52 +0200] [397076] [INFO] Booting worker with pid: 397076 +[2025-11-03 20:29:52 +0200] [397076] [INFO] โœจ Worker spawned successfully (pid: 397076) +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:52 +0200] [397077] [INFO] Booting worker with pid: 397077 +[2025-11-03 20:29:52 +0200] [397077] [INFO] โœจ Worker spawned successfully (pid: 397077) +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:52 +0200] [397078] [INFO] Booting worker with pid: 397078 +[2025-11-03 20:29:52 +0200] [397078] [INFO] โœจ Worker spawned successfully (pid: 397078) +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:52 +0200] [397079] [INFO] Booting worker with pid: 397079 +[2025-11-03 20:29:52 +0200] [397079] [INFO] โœจ Worker spawned successfully (pid: 397079) +[2025-11-03 20:29:52 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:52 +0200] [397080] [INFO] Booting worker with pid: 397080 +[2025-11-03 20:29:52 +0200] [397080] [INFO] โœจ Worker spawned successfully (pid: 397080) +[2025-11-03 20:29:53 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:53 +0200] [397081] [INFO] Booting worker with pid: 397081 +[2025-11-03 20:29:53 +0200] [397081] [INFO] โœจ Worker spawned successfully (pid: 397081) +[2025-11-03 20:29:53 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:53 +0200] [397082] [INFO] Booting worker with pid: 397082 +[2025-11-03 20:29:53 +0200] [397082] [INFO] โœจ Worker spawned successfully (pid: 397082) +[2025-11-03 20:29:53 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:53 +0200] [397083] [INFO] Booting worker with pid: 397083 +[2025-11-03 20:29:53 +0200] [397083] [INFO] โœจ Worker spawned successfully (pid: 397083) +[2025-11-03 20:29:53 +0200] [397053] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:29:53 +0200] [397084] [INFO] Booting worker with pid: 397084 +[2025-11-03 20:29:53 +0200] [397084] [INFO] โœจ Worker spawned successfully (pid: 397084) +Session user: superadmin superadmin +Session user: superadmin superadmin +[2025-11-03 20:35:12 +0200] [397053] [INFO] Handling signal: term +[2025-11-03 20:35:12 +0200] [397078] [INFO] Worker exiting (pid: 397078) +[2025-11-03 20:35:12 +0200] [397076] [INFO] Worker exiting (pid: 397076) +[2025-11-03 20:35:12 +0200] [397079] [INFO] Worker exiting (pid: 397079) +[2025-11-03 20:35:12 +0200] [397081] [INFO] Worker exiting (pid: 397081) +[2025-11-03 20:35:12 +0200] [397077] [INFO] Worker exiting (pid: 397077) +[2025-11-03 20:35:12 +0200] [397082] [INFO] Worker exiting (pid: 397082) +[2025-11-03 20:35:12 +0200] [397083] [INFO] Worker exiting (pid: 397083) +[2025-11-03 20:35:12 +0200] [397080] [INFO] Worker exiting (pid: 397080) +[2025-11-03 20:35:12 +0200] [397084] [INFO] Worker exiting (pid: 397084) +[2025-11-03 20:35:13 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397081 exited +[2025-11-03 20:35:13 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397079 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397076 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397083 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397084 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397077 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397078 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397082 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Worker 397080 exited +[2025-11-03 20:35:14 +0200] [397053] [INFO] Shutting down: Master +[2025-11-03 20:35:14 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:35:14 +0200] [397053] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 20:35:14 +0200] [397053] [INFO] ============================================================ +[2025-11-03 20:35:21 +0200] [397553] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:35:21 +0200] [397553] [INFO] ============================================================ +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:35:21 +0200] [397553] [INFO] ============================================================ +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:35:21 +0200] [397553] [INFO] โ€ข Workers: 9 +[2025-11-03 20:35:21 +0200] [397553] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:35:21 +0200] [397553] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:35:21 +0200] [397553] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:35:21 +0200] [397553] [INFO] โ€ข Preload App: True +[2025-11-03 20:35:21 +0200] [397553] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ============================================================ +[2025-11-03 20:35:21 +0200] [397553] [INFO] Listening at: http://0.0.0.0:8781 (397553) +[2025-11-03 20:35:21 +0200] [397553] [INFO] Using worker: sync +[2025-11-03 20:35:21 +0200] [397553] [INFO] ============================================================ +[2025-11-03 20:35:21 +0200] [397553] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:35:21 +0200] [397553] [INFO] ============================================================ +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397580] [INFO] Booting worker with pid: 397580 +[2025-11-03 20:35:21 +0200] [397580] [INFO] โœจ Worker spawned successfully (pid: 397580) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397581] [INFO] Booting worker with pid: 397581 +[2025-11-03 20:35:21 +0200] [397581] [INFO] โœจ Worker spawned successfully (pid: 397581) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397582] [INFO] Booting worker with pid: 397582 +[2025-11-03 20:35:21 +0200] [397582] [INFO] โœจ Worker spawned successfully (pid: 397582) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397583] [INFO] Booting worker with pid: 397583 +[2025-11-03 20:35:21 +0200] [397583] [INFO] โœจ Worker spawned successfully (pid: 397583) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397584] [INFO] Booting worker with pid: 397584 +[2025-11-03 20:35:21 +0200] [397584] [INFO] โœจ Worker spawned successfully (pid: 397584) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397585] [INFO] Booting worker with pid: 397585 +[2025-11-03 20:35:21 +0200] [397585] [INFO] โœจ Worker spawned successfully (pid: 397585) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397586] [INFO] Booting worker with pid: 397586 +[2025-11-03 20:35:21 +0200] [397586] [INFO] โœจ Worker spawned successfully (pid: 397586) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397587] [INFO] Booting worker with pid: 397587 +[2025-11-03 20:35:21 +0200] [397587] [INFO] โœจ Worker spawned successfully (pid: 397587) +[2025-11-03 20:35:21 +0200] [397553] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:35:21 +0200] [397588] [INFO] Booting worker with pid: 397588 +[2025-11-03 20:35:21 +0200] [397588] [INFO] โœจ Worker spawned successfully (pid: 397588) +[2025-11-03 20:39:58 +0200] [397553] [INFO] Handling signal: term +[2025-11-03 20:39:58 +0200] [397580] [INFO] Worker exiting (pid: 397580) +[2025-11-03 20:39:58 +0200] [397581] [INFO] Worker exiting (pid: 397581) +[2025-11-03 20:39:58 +0200] [397584] [INFO] Worker exiting (pid: 397584) +[2025-11-03 20:39:58 +0200] [397582] [INFO] Worker exiting (pid: 397582) +[2025-11-03 20:39:58 +0200] [397585] [INFO] Worker exiting (pid: 397585) +[2025-11-03 20:39:58 +0200] [397587] [INFO] Worker exiting (pid: 397587) +[2025-11-03 20:39:58 +0200] [397583] [INFO] Worker exiting (pid: 397583) +[2025-11-03 20:39:58 +0200] [397586] [INFO] Worker exiting (pid: 397586) +[2025-11-03 20:39:58 +0200] [397588] [INFO] Worker exiting (pid: 397588) +[2025-11-03 20:39:59 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397581 exited +[2025-11-03 20:39:59 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397585 exited +[2025-11-03 20:39:59 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397580 exited +[2025-11-03 20:39:59 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397588 exited +[2025-11-03 20:39:59 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397583 exited +[2025-11-03 20:39:59 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397586 exited +[2025-11-03 20:40:00 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397582 exited +[2025-11-03 20:40:00 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397584 exited +[2025-11-03 20:40:00 +0200] [397553] [INFO] ๐Ÿ‘‹ Worker 397587 exited +[2025-11-03 20:40:00 +0200] [397553] [INFO] Shutting down: Master +[2025-11-03 20:40:00 +0200] [397553] [INFO] ============================================================ +[2025-11-03 20:40:00 +0200] [397553] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 20:40:00 +0200] [397553] [INFO] ============================================================ +Traceback (most recent call last): + File "/srv/quality_recticel/recticel/bin/gunicorn", line 8, in + sys.exit(run()) + ~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 66, in run + WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]", prog=prog).run() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 235, in run + super().run() + ~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 71, in run + Arbiter(self).run() + ~~~~~~~^^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 57, in __init__ + self.setup(app) + ~~~~~~~~~~^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 117, in setup + self.app.wsgi() + ~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 66, in wsgi + self.callable = self.load() + ~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 57, in load + return self.load_wsgiapp() + ~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 47, in load_wsgiapp + return util.import_app(self.app_uri) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/util.py", line 370, in import_app + mod = importlib.import_module(module) + File "/usr/lib/python3.13/importlib/__init__.py", line 88, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1387, in _gcd_import + File "", line 1360, in _find_and_load + File "", line 1331, in _find_and_load_unlocked + File "", line 935, in _load_unlocked + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "/srv/quality_app/py_app/wsgi.py", line 15, in + application = create_app() + File "/srv/quality_app/py_app/app/__init__.py", line 11, in create_app + from app.routes import bp as main_bp, warehouse_bp + File "/srv/quality_app/py_app/app/routes.py", line 6, in + from .models import User + File "/srv/quality_app/py_app/app/models.py", line 1, in + from . import db +ImportError: cannot import name 'db' from 'app' (/srv/quality_app/py_app/app/__init__.py) +Traceback (most recent call last): + File "/srv/quality_recticel/recticel/bin/gunicorn", line 8, in + sys.exit(run()) + ~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 66, in run + WSGIApplication("%(prog)s [OPTIONS] [APP_MODULE]", prog=prog).run() + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 235, in run + super().run() + ~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 71, in run + Arbiter(self).run() + ~~~~~~~^^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 57, in __init__ + self.setup(app) + ~~~~~~~~~~^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/arbiter.py", line 117, in setup + self.app.wsgi() + ~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/base.py", line 66, in wsgi + self.callable = self.load() + ~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 57, in load + return self.load_wsgiapp() + ~~~~~~~~~~~~~~~~~^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/app/wsgiapp.py", line 47, in load_wsgiapp + return util.import_app(self.app_uri) + ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^ + File "/srv/quality_recticel/recticel/lib/python3.13/site-packages/gunicorn/util.py", line 370, in import_app + mod = importlib.import_module(module) + File "/usr/lib/python3.13/importlib/__init__.py", line 88, in import_module + return _bootstrap._gcd_import(name[level:], package, level) + ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + File "", line 1387, in _gcd_import + File "", line 1360, in _find_and_load + File "", line 1331, in _find_and_load_unlocked + File "", line 935, in _load_unlocked + File "", line 1026, in exec_module + File "", line 488, in _call_with_frames_removed + File "/srv/quality_app/py_app/wsgi.py", line 15, in + application = create_app() + File "/srv/quality_app/py_app/app/__init__.py", line 11, in create_app + from app.routes import bp as main_bp, warehouse_bp + File "/srv/quality_app/py_app/app/routes.py", line 10, in + from app.settings import ( + ...<10 lines>... + ) + File "/srv/quality_app/py_app/app/settings.py", line 2, in + from .models import User + File "/srv/quality_app/py_app/app/models.py", line 1, in + from . import db +ImportError: cannot import name 'db' from 'app' (/srv/quality_app/py_app/app/__init__.py) +[2025-11-03 20:41:50 +0200] [398202] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:41:50 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:41:50 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:41:50 +0200] [398202] [INFO] โ€ข Workers: 9 +[2025-11-03 20:41:50 +0200] [398202] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:41:50 +0200] [398202] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:41:50 +0200] [398202] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:41:50 +0200] [398202] [INFO] โ€ข Preload App: True +[2025-11-03 20:41:50 +0200] [398202] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:41:50 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:41:50 +0200] [398202] [INFO] Listening at: http://0.0.0.0:8781 (398202) +[2025-11-03 20:41:50 +0200] [398202] [INFO] Using worker: sync +[2025-11-03 20:41:50 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:41:50 +0200] [398202] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:41:50 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:50 +0200] [398224] [INFO] Booting worker with pid: 398224 +[2025-11-03 20:41:50 +0200] [398224] [INFO] โœจ Worker spawned successfully (pid: 398224) +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:50 +0200] [398225] [INFO] Booting worker with pid: 398225 +[2025-11-03 20:41:50 +0200] [398225] [INFO] โœจ Worker spawned successfully (pid: 398225) +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:50 +0200] [398226] [INFO] Booting worker with pid: 398226 +[2025-11-03 20:41:50 +0200] [398226] [INFO] โœจ Worker spawned successfully (pid: 398226) +[2025-11-03 20:41:50 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:50 +0200] [398227] [INFO] Booting worker with pid: 398227 +[2025-11-03 20:41:50 +0200] [398227] [INFO] โœจ Worker spawned successfully (pid: 398227) +[2025-11-03 20:41:51 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:51 +0200] [398228] [INFO] Booting worker with pid: 398228 +[2025-11-03 20:41:51 +0200] [398228] [INFO] โœจ Worker spawned successfully (pid: 398228) +[2025-11-03 20:41:51 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:51 +0200] [398235] [INFO] Booting worker with pid: 398235 +[2025-11-03 20:41:51 +0200] [398235] [INFO] โœจ Worker spawned successfully (pid: 398235) +[2025-11-03 20:41:51 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:51 +0200] [398236] [INFO] Booting worker with pid: 398236 +[2025-11-03 20:41:51 +0200] [398236] [INFO] โœจ Worker spawned successfully (pid: 398236) +[2025-11-03 20:41:51 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:51 +0200] [398237] [INFO] Booting worker with pid: 398237 +[2025-11-03 20:41:51 +0200] [398237] [INFO] โœจ Worker spawned successfully (pid: 398237) +[2025-11-03 20:41:51 +0200] [398202] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:41:51 +0200] [398238] [INFO] Booting worker with pid: 398238 +[2025-11-03 20:41:51 +0200] [398238] [INFO] โœจ Worker spawned successfully (pid: 398238) +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +All form data received: {'username': 'superadmin', 'password': 'Vanessa_13/05'} +Raw form input: 'superadmin' 'Vanessa_13/05' +External DB query result (with modules): ('superadmin', 'Vanessa_13/05', 'superadmin', 'quality,warehouse,labels,daily_mirror') +Logged in as: superadmin superadmin modules: ['quality', 'warehouse', 'labels'] +Session user: superadmin superadmin +Error loading Daily Mirror main page: daily_mirror_main.html +Session user: superadmin superadmin +Session user: superadmin superadmin +Session user: superadmin superadmin +[2025-11-03 20:45:46 +0200] [398202] [INFO] Handling signal: term +[2025-11-03 20:45:46 +0200] [398226] [INFO] Worker exiting (pid: 398226) +[2025-11-03 20:45:46 +0200] [398225] [INFO] Worker exiting (pid: 398225) +[2025-11-03 20:45:46 +0200] [398224] [INFO] Worker exiting (pid: 398224) +[2025-11-03 20:45:46 +0200] [398237] [INFO] Worker exiting (pid: 398237) +[2025-11-03 20:45:46 +0200] [398228] [INFO] Worker exiting (pid: 398228) +[2025-11-03 20:45:46 +0200] [398236] [INFO] Worker exiting (pid: 398236) +[2025-11-03 20:45:46 +0200] [398227] [INFO] Worker exiting (pid: 398227) +[2025-11-03 20:45:46 +0200] [398235] [INFO] Worker exiting (pid: 398235) +[2025-11-03 20:45:46 +0200] [398238] [INFO] Worker exiting (pid: 398238) +[2025-11-03 20:45:46 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398237 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398238 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398226 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398235 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398225 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398236 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398228 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398224 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Worker 398227 exited +[2025-11-03 20:45:47 +0200] [398202] [INFO] Shutting down: Master +[2025-11-03 20:45:47 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:45:47 +0200] [398202] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 20:45:47 +0200] [398202] [INFO] ============================================================ +[2025-11-03 20:45:53 +0200] [398661] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 20:45:53 +0200] [398661] [INFO] ============================================================ +[2025-11-03 20:45:53 +0200] [398661] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 20:45:53 +0200] [398661] [INFO] ============================================================ +[2025-11-03 20:45:53 +0200] [398661] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 20:45:53 +0200] [398661] [INFO] โ€ข Workers: 9 +[2025-11-03 20:45:53 +0200] [398661] [INFO] โ€ข Worker Class: sync +[2025-11-03 20:45:53 +0200] [398661] [INFO] โ€ข Timeout: 120s +[2025-11-03 20:45:53 +0200] [398661] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 20:45:53 +0200] [398661] [INFO] โ€ข Preload App: True +[2025-11-03 20:45:53 +0200] [398661] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 20:45:53 +0200] [398661] [INFO] ============================================================ +[2025-11-03 20:45:53 +0200] [398661] [INFO] Listening at: http://0.0.0.0:8781 (398661) +[2025-11-03 20:45:53 +0200] [398661] [INFO] Using worker: sync +[2025-11-03 20:45:53 +0200] [398661] [INFO] ============================================================ +[2025-11-03 20:45:53 +0200] [398661] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 20:45:53 +0200] [398661] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 20:45:53 +0200] [398661] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 20:45:53 +0200] [398661] [INFO] ============================================================ +[2025-11-03 20:45:53 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:53 +0200] [398683] [INFO] Booting worker with pid: 398683 +[2025-11-03 20:45:53 +0200] [398683] [INFO] โœจ Worker spawned successfully (pid: 398683) +[2025-11-03 20:45:53 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:53 +0200] [398684] [INFO] Booting worker with pid: 398684 +[2025-11-03 20:45:53 +0200] [398684] [INFO] โœจ Worker spawned successfully (pid: 398684) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398685] [INFO] Booting worker with pid: 398685 +[2025-11-03 20:45:54 +0200] [398685] [INFO] โœจ Worker spawned successfully (pid: 398685) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398686] [INFO] Booting worker with pid: 398686 +[2025-11-03 20:45:54 +0200] [398686] [INFO] โœจ Worker spawned successfully (pid: 398686) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398687] [INFO] Booting worker with pid: 398687 +[2025-11-03 20:45:54 +0200] [398687] [INFO] โœจ Worker spawned successfully (pid: 398687) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398690] [INFO] Booting worker with pid: 398690 +[2025-11-03 20:45:54 +0200] [398690] [INFO] โœจ Worker spawned successfully (pid: 398690) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398695] [INFO] Booting worker with pid: 398695 +[2025-11-03 20:45:54 +0200] [398695] [INFO] โœจ Worker spawned successfully (pid: 398695) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398696] [INFO] Booting worker with pid: 398696 +[2025-11-03 20:45:54 +0200] [398696] [INFO] โœจ Worker spawned successfully (pid: 398696) +[2025-11-03 20:45:54 +0200] [398661] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 20:45:54 +0200] [398697] [INFO] Booting worker with pid: 398697 +[2025-11-03 20:45:54 +0200] [398697] [INFO] โœจ Worker spawned successfully (pid: 398697) +[2025-11-03 21:06:13 +0200] [398661] [INFO] Handling signal: term +[2025-11-03 21:06:13 +0200] [398683] [INFO] Worker exiting (pid: 398683) +[2025-11-03 21:06:13 +0200] [398684] [INFO] Worker exiting (pid: 398684) +[2025-11-03 21:06:13 +0200] [398685] [INFO] Worker exiting (pid: 398685) +[2025-11-03 21:06:13 +0200] [398686] [INFO] Worker exiting (pid: 398686) +[2025-11-03 21:06:13 +0200] [398687] [INFO] Worker exiting (pid: 398687) +[2025-11-03 21:06:13 +0200] [398695] [INFO] Worker exiting (pid: 398695) +[2025-11-03 21:06:13 +0200] [398690] [INFO] Worker exiting (pid: 398690) +[2025-11-03 21:06:13 +0200] [398696] [INFO] Worker exiting (pid: 398696) +[2025-11-03 21:06:13 +0200] [398697] [INFO] Worker exiting (pid: 398697) +[2025-11-03 21:06:13 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398683 exited +[2025-11-03 21:06:13 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398686 exited +[2025-11-03 21:06:13 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398697 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398685 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398695 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398684 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398690 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398696 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Worker 398687 exited +[2025-11-03 21:06:14 +0200] [398661] [INFO] Shutting down: Master +[2025-11-03 21:06:14 +0200] [398661] [INFO] ============================================================ +[2025-11-03 21:06:14 +0200] [398661] [INFO] ๐Ÿ‘‹ Trasabilitate Application - Shutting Down +[2025-11-03 21:06:14 +0200] [398661] [INFO] ============================================================ +[2025-11-03 21:06:20 +0200] [399048] [INFO] Starting gunicorn 23.0.0 +[2025-11-03 21:06:20 +0200] [399048] [INFO] ============================================================ +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿš€ Trasabilitate Application - Starting Server +[2025-11-03 21:06:20 +0200] [399048] [INFO] ============================================================ +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ“ Configuration: +[2025-11-03 21:06:20 +0200] [399048] [INFO] โ€ข Workers: 9 +[2025-11-03 21:06:20 +0200] [399048] [INFO] โ€ข Worker Class: sync +[2025-11-03 21:06:20 +0200] [399048] [INFO] โ€ข Timeout: 120s +[2025-11-03 21:06:20 +0200] [399048] [INFO] โ€ข Bind: 0.0.0.0:8781 +[2025-11-03 21:06:20 +0200] [399048] [INFO] โ€ข Preload App: True +[2025-11-03 21:06:20 +0200] [399048] [INFO] โ€ข Max Requests: 1000 (+/- 100) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ============================================================ +[2025-11-03 21:06:20 +0200] [399048] [INFO] Listening at: http://0.0.0.0:8781 (399048) +[2025-11-03 21:06:20 +0200] [399048] [INFO] Using worker: sync +[2025-11-03 21:06:20 +0200] [399048] [INFO] ============================================================ +[2025-11-03 21:06:20 +0200] [399048] [INFO] โœ… Trasabilitate Application Server is READY! +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ“ก Listening on: [('0.0.0.0', 8781)] +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐ŸŒ Access the application at: http://0.0.0.0:8781 +[2025-11-03 21:06:20 +0200] [399048] [INFO] ============================================================ +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:20 +0200] [399070] [INFO] Booting worker with pid: 399070 +[2025-11-03 21:06:20 +0200] [399070] [INFO] โœจ Worker spawned successfully (pid: 399070) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:20 +0200] [399071] [INFO] Booting worker with pid: 399071 +[2025-11-03 21:06:20 +0200] [399071] [INFO] โœจ Worker spawned successfully (pid: 399071) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:20 +0200] [399072] [INFO] Booting worker with pid: 399072 +[2025-11-03 21:06:20 +0200] [399072] [INFO] โœจ Worker spawned successfully (pid: 399072) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:20 +0200] [399073] [INFO] Booting worker with pid: 399073 +[2025-11-03 21:06:20 +0200] [399073] [INFO] โœจ Worker spawned successfully (pid: 399073) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:20 +0200] [399076] [INFO] Booting worker with pid: 399076 +[2025-11-03 21:06:20 +0200] [399076] [INFO] โœจ Worker spawned successfully (pid: 399076) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:20 +0200] [399080] [INFO] Booting worker with pid: 399080 +[2025-11-03 21:06:20 +0200] [399080] [INFO] โœจ Worker spawned successfully (pid: 399080) +[2025-11-03 21:06:20 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:21 +0200] [399082] [INFO] Booting worker with pid: 399082 +[2025-11-03 21:06:21 +0200] [399082] [INFO] โœจ Worker spawned successfully (pid: 399082) +[2025-11-03 21:06:21 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:21 +0200] [399083] [INFO] Booting worker with pid: 399083 +[2025-11-03 21:06:21 +0200] [399083] [INFO] โœจ Worker spawned successfully (pid: 399083) +[2025-11-03 21:06:21 +0200] [399048] [INFO] ๐Ÿ”„ Forking new worker (pid: [booting]) +[2025-11-03 21:06:21 +0200] [399084] [INFO] Booting worker with pid: 399084 +[2025-11-03 21:06:21 +0200] [399084] [INFO] โœจ Worker spawned successfully (pid: 399084) +Backup directory ensured: /srv/quality_app/backups +Backup directory ensured: /srv/quality_app/backups diff --git a/py_app/app/__init__.py b/py_app/app/__init__.py index ef843d6..fdf8c32 100755 --- a/py_app/app/__init__.py +++ b/py_app/app/__init__.py @@ -1,16 +1,12 @@ from flask import Flask -from flask_sqlalchemy import SQLAlchemy from datetime import datetime -db = SQLAlchemy() - def create_app(): app = Flask(__name__) app.config['SECRET_KEY'] = 'your_secret_key' - app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db' - app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False - - db.init_app(app) + + # Application uses direct MariaDB connections via external_server.conf + # No SQLAlchemy ORM needed - all database operations use raw SQL from app.routes import bp as main_bp, warehouse_bp from app.daily_mirror import daily_mirror_bp @@ -21,7 +17,4 @@ def create_app(): # Add 'now' function to Jinja2 globals app.jinja_env.globals['now'] = datetime.now - with app.app_context(): - db.create_all() # Create database tables if they don't exist - return app \ No newline at end of file diff --git a/py_app/app/access_control.py b/py_app/app/access_control.py index 806a70c..3396470 100644 --- a/py_app/app/access_control.py +++ b/py_app/app/access_control.py @@ -41,8 +41,8 @@ def requires_role(min_role_level=None, required_modules=None, page=None): # Module requirement checking if required_modules: - if user_role in ['superadmin', 'admin']: - # Superadmin and admin have access to all modules + if user_role == 'superadmin': + # Superadmin has access to all modules pass else: if not any(module in user_modules for module in required_modules): @@ -77,6 +77,10 @@ def requires_labels_module(f): """Decorator for labels module access""" return requires_role(required_modules=['labels'])(f) +def requires_daily_mirror_module(f): + """Decorator for daily mirror module access""" + return requires_role(required_modules=['daily_mirror'])(f) + def quality_manager_plus(f): """Decorator for quality module manager+ access""" return requires_role(min_role_level=70, required_modules=['quality'])(f) @@ -87,4 +91,8 @@ def warehouse_manager_plus(f): def labels_manager_plus(f): """Decorator for labels module manager+ access""" - return requires_role(min_role_level=70, required_modules=['labels'])(f) \ No newline at end of file + return requires_role(min_role_level=70, required_modules=['labels'])(f) + +def daily_mirror_manager_plus(f): + """Decorator for daily mirror module manager+ access""" + return requires_role(min_role_level=70, required_modules=['daily_mirror'])(f) \ No newline at end of file diff --git a/py_app/app/daily_mirror.py b/py_app/app/daily_mirror.py index 603ba3a..c4364bb 100644 --- a/py_app/app/daily_mirror.py +++ b/py_app/app/daily_mirror.py @@ -26,10 +26,15 @@ def check_daily_mirror_access(): flash('Please log in to access this page.') return redirect(url_for('main.login')) - # Check if user has admin+ access + # Superadmin has access to everything user_role = session.get('role', '') - if user_role not in ['superadmin', 'admin']: - flash('Access denied: Admin privileges required for Daily Mirror.') + if user_role == 'superadmin': + return None # Access granted + + # Check if user has daily_mirror module access + user_modules = session.get('modules', []) + if 'daily_mirror' not in user_modules: + flash('Access denied: Daily Mirror module access required.') return redirect(url_for('main.dashboard')) return None # Access granted @@ -37,13 +42,19 @@ def check_daily_mirror_access(): def check_daily_mirror_api_access(): """Helper function to check API access for Daily Mirror""" - # Check if user is logged in and has admin+ access + # Check if user is logged in if 'user' not in session: return jsonify({'error': 'Authentication required'}), 401 + # Superadmin has access to everything user_role = session.get('role', '') - if user_role not in ['superadmin', 'admin']: - return jsonify({'error': 'Admin privileges required'}), 403 + if user_role == 'superadmin': + return None # Access granted + + # Check if user has daily_mirror module access + user_modules = session.get('modules', []) + if 'daily_mirror' not in user_modules: + return jsonify({'error': 'Daily Mirror module access required'}), 403 return None # Access granted diff --git a/py_app/app/database_backup.py b/py_app/app/database_backup.py new file mode 100644 index 0000000..689545f --- /dev/null +++ b/py_app/app/database_backup.py @@ -0,0 +1,431 @@ +""" +Database Backup Management Module +Quality Recticel Application + +This module provides functionality for backing up and restoring the MariaDB database, +including scheduled backups, manual backups, and backup file management. +""" + +import os +import subprocess +import json +from datetime import datetime, timedelta +from pathlib import Path +import configparser +from flask import current_app +import mariadb + +class DatabaseBackupManager: + """Manages database backup operations""" + + def __init__(self): + """Initialize the backup manager with configuration from external_server.conf""" + self.config = self._load_database_config() + self.backup_path = self._get_backup_path() + self._ensure_backup_directory() + + def _load_database_config(self): + """Load database configuration from external_server.conf""" + try: + settings_file = os.path.join(current_app.instance_path, 'external_server.conf') + config = {} + + if os.path.exists(settings_file): + with open(settings_file, 'r') as f: + for line in f: + if '=' in line: + key, value = line.strip().split('=', 1) + config[key] = value + + return { + 'host': config.get('server_domain', 'localhost'), + 'port': config.get('port', '3306'), + 'database': config.get('database_name', 'trasabilitate'), + 'user': config.get('username', 'trasabilitate'), + 'password': config.get('password', '') + } + except Exception as e: + print(f"Error loading database config: {e}") + return None + + def _get_backup_path(self): + """Get backup path from environment or use default""" + # Check environment variable (set in docker-compose) + backup_path = os.environ.get('BACKUP_PATH', '/srv/quality_app/backups') + + # Check if custom path is set in config + try: + settings_file = os.path.join(current_app.instance_path, 'external_server.conf') + if os.path.exists(settings_file): + with open(settings_file, 'r') as f: + for line in f: + if line.startswith('backup_path='): + backup_path = line.strip().split('=', 1)[1] + break + except Exception as e: + print(f"Error reading backup path from config: {e}") + + return backup_path + + def _ensure_backup_directory(self): + """Ensure backup directory exists""" + try: + Path(self.backup_path).mkdir(parents=True, exist_ok=True) + print(f"Backup directory ensured: {self.backup_path}") + except Exception as e: + print(f"Error creating backup directory: {e}") + + def create_backup(self, backup_name=None): + """ + Create a complete backup of the database + + Args: + backup_name (str, optional): Custom name for the backup file + + Returns: + dict: Result with success status, message, and backup file path + """ + try: + if not self.config: + return { + 'success': False, + 'message': 'Database configuration not loaded' + } + + # Generate backup filename + timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') + if backup_name: + filename = f"{backup_name}_{timestamp}.sql" + else: + filename = f"backup_{self.config['database']}_{timestamp}.sql" + + backup_file = os.path.join(self.backup_path, filename) + + # Build mysqldump command + cmd = [ + 'mysqldump', + f"--host={self.config['host']}", + f"--port={self.config['port']}", + f"--user={self.config['user']}", + f"--password={self.config['password']}", + '--single-transaction', + '--routines', + '--triggers', + '--events', + '--add-drop-database', + '--databases', + self.config['database'] + ] + + # Execute mysqldump and save to file + with open(backup_file, 'w') as f: + result = subprocess.run( + cmd, + stdout=f, + stderr=subprocess.PIPE, + text=True + ) + + if result.returncode == 0: + # Get file size + file_size = os.path.getsize(backup_file) + file_size_mb = file_size / (1024 * 1024) + + # Save backup metadata + self._save_backup_metadata(filename, file_size) + + return { + 'success': True, + 'message': f'Backup created successfully', + 'filename': filename, + 'file_path': backup_file, + 'size': f"{file_size_mb:.2f} MB", + 'timestamp': timestamp + } + else: + error_msg = result.stderr + print(f"Backup error: {error_msg}") + return { + 'success': False, + 'message': f'Backup failed: {error_msg}' + } + + except Exception as e: + print(f"Exception during backup: {e}") + return { + 'success': False, + 'message': f'Backup failed: {str(e)}' + } + + def _save_backup_metadata(self, filename, file_size): + """Save metadata about the backup""" + try: + metadata_file = os.path.join(self.backup_path, 'backups_metadata.json') + + # Load existing metadata + metadata = [] + if os.path.exists(metadata_file): + with open(metadata_file, 'r') as f: + metadata = json.load(f) + + # Add new backup metadata + metadata.append({ + 'filename': filename, + 'size': file_size, + 'timestamp': datetime.now().isoformat(), + 'database': self.config['database'] + }) + + # Save updated metadata + with open(metadata_file, 'w') as f: + json.dump(metadata, f, indent=2) + + except Exception as e: + print(f"Error saving backup metadata: {e}") + + def list_backups(self): + """ + List all available backups + + Returns: + list: List of backup information dictionaries + """ + try: + backups = [] + + # Get all .sql files in backup directory + if os.path.exists(self.backup_path): + for filename in os.listdir(self.backup_path): + if filename.endswith('.sql'): + file_path = os.path.join(self.backup_path, filename) + file_stat = os.stat(file_path) + + backups.append({ + 'filename': filename, + 'size': file_stat.st_size, + 'size_mb': f"{file_stat.st_size / (1024 * 1024):.2f}", + 'created': datetime.fromtimestamp(file_stat.st_ctime).strftime('%Y-%m-%d %H:%M:%S'), + 'timestamp': file_stat.st_ctime + }) + + # Sort by timestamp (newest first) + backups.sort(key=lambda x: x['timestamp'], reverse=True) + + return backups + + except Exception as e: + print(f"Error listing backups: {e}") + return [] + + def delete_backup(self, filename): + """ + Delete a backup file + + Args: + filename (str): Name of the backup file to delete + + Returns: + dict: Result with success status and message + """ + try: + # Security: ensure filename doesn't contain path traversal + if '..' in filename or '/' in filename: + return { + 'success': False, + 'message': 'Invalid filename' + } + + file_path = os.path.join(self.backup_path, filename) + + if os.path.exists(file_path): + os.remove(file_path) + + # Update metadata + self._remove_backup_metadata(filename) + + return { + 'success': True, + 'message': f'Backup {filename} deleted successfully' + } + else: + return { + 'success': False, + 'message': 'Backup file not found' + } + + except Exception as e: + print(f"Error deleting backup: {e}") + return { + 'success': False, + 'message': f'Failed to delete backup: {str(e)}' + } + + def _remove_backup_metadata(self, filename): + """Remove metadata entry for deleted backup""" + try: + metadata_file = os.path.join(self.backup_path, 'backups_metadata.json') + + if os.path.exists(metadata_file): + with open(metadata_file, 'r') as f: + metadata = json.load(f) + + # Filter out the deleted backup + metadata = [m for m in metadata if m['filename'] != filename] + + with open(metadata_file, 'w') as f: + json.dump(metadata, f, indent=2) + + except Exception as e: + print(f"Error removing backup metadata: {e}") + + def restore_backup(self, filename): + """ + Restore database from a backup file + + Args: + filename (str): Name of the backup file to restore + + Returns: + dict: Result with success status and message + """ + try: + # Security: ensure filename doesn't contain path traversal + if '..' in filename or '/' in filename: + return { + 'success': False, + 'message': 'Invalid filename' + } + + file_path = os.path.join(self.backup_path, filename) + + if not os.path.exists(file_path): + return { + 'success': False, + 'message': 'Backup file not found' + } + + # Build mysql restore command + cmd = [ + 'mysql', + f"--host={self.config['host']}", + f"--port={self.config['port']}", + f"--user={self.config['user']}", + f"--password={self.config['password']}" + ] + + # Execute mysql restore + with open(file_path, 'r') as f: + result = subprocess.run( + cmd, + stdin=f, + stderr=subprocess.PIPE, + text=True + ) + + if result.returncode == 0: + return { + 'success': True, + 'message': f'Database restored successfully from {filename}' + } + else: + error_msg = result.stderr + print(f"Restore error: {error_msg}") + return { + 'success': False, + 'message': f'Restore failed: {error_msg}' + } + + except Exception as e: + print(f"Exception during restore: {e}") + return { + 'success': False, + 'message': f'Restore failed: {str(e)}' + } + + def get_backup_schedule(self): + """Get current backup schedule configuration""" + try: + schedule_file = os.path.join(self.backup_path, 'backup_schedule.json') + + if os.path.exists(schedule_file): + with open(schedule_file, 'r') as f: + return json.load(f) + + # Default schedule + return { + 'enabled': False, + 'time': '02:00', # 2 AM + 'frequency': 'daily', # daily, weekly, monthly + 'retention_days': 30 # Keep backups for 30 days + } + + except Exception as e: + print(f"Error loading backup schedule: {e}") + return None + + def save_backup_schedule(self, schedule): + """ + Save backup schedule configuration + + Args: + schedule (dict): Schedule configuration + + Returns: + dict: Result with success status and message + """ + try: + schedule_file = os.path.join(self.backup_path, 'backup_schedule.json') + + with open(schedule_file, 'w') as f: + json.dump(schedule, f, indent=2) + + return { + 'success': True, + 'message': 'Backup schedule saved successfully' + } + + except Exception as e: + print(f"Error saving backup schedule: {e}") + return { + 'success': False, + 'message': f'Failed to save schedule: {str(e)}' + } + + def cleanup_old_backups(self, retention_days=30): + """ + Delete backups older than retention_days + + Args: + retention_days (int): Number of days to keep backups + + Returns: + dict: Result with count of deleted backups + """ + try: + deleted_count = 0 + cutoff_time = datetime.now() - timedelta(days=retention_days) + + if os.path.exists(self.backup_path): + for filename in os.listdir(self.backup_path): + if filename.endswith('.sql'): + file_path = os.path.join(self.backup_path, filename) + file_time = datetime.fromtimestamp(os.path.getctime(file_path)) + + if file_time < cutoff_time: + os.remove(file_path) + self._remove_backup_metadata(filename) + deleted_count += 1 + print(f"Deleted old backup: {filename}") + + return { + 'success': True, + 'deleted_count': deleted_count, + 'message': f'Cleaned up {deleted_count} old backup(s)' + } + + except Exception as e: + print(f"Error cleaning up old backups: {e}") + return { + 'success': False, + 'message': f'Cleanup failed: {str(e)}' + } diff --git a/py_app/app/routes.py b/py_app/app/routes.py index ed2ff1a..14d51bb 100755 --- a/py_app/app/routes.py +++ b/py_app/app/routes.py @@ -3,8 +3,6 @@ import os import mariadb from datetime import datetime, timedelta from flask import Blueprint, render_template, redirect, url_for, request, flash, session, current_app, jsonify, send_from_directory -from .models import User -from . import db from reportlab.lib.pagesizes import letter from reportlab.pdfgen import canvas import csv @@ -94,9 +92,9 @@ def login(): except: user_modules = [] - # Superadmin and admin have access to all modules - if user['role'] in ['superadmin', 'admin']: - user_modules = ['quality', 'warehouse', 'labels'] + # Superadmin has access to all modules + if user['role'] == 'superadmin': + user_modules = ['quality', 'warehouse', 'labels', 'daily_mirror'] session['modules'] = user_modules print("Logged in as:", session.get('user'), session.get('role'), "modules:", user_modules) @@ -3580,4 +3578,137 @@ def help(page='index'): # "pdf_url": "https://your-linux-server/generate_labels_pdf/15", # "printer_name": "default", # "copies": 1 -# } \ No newline at end of file +# } + +# ======================================== +# DATABASE BACKUP MANAGEMENT ROUTES +# ======================================== + +@bp.route('/api/backup/create', methods=['POST']) +@admin_plus +def api_backup_create(): + """Create a new database backup""" + try: + from app.database_backup import DatabaseBackupManager + + backup_manager = DatabaseBackupManager() + result = backup_manager.create_backup() + + return jsonify(result) + except Exception as e: + return jsonify({ + 'success': False, + 'message': f'Backup failed: {str(e)}' + }), 500 + +@bp.route('/api/backup/list', methods=['GET']) +@admin_plus +def api_backup_list(): + """List all available backups""" + try: + from app.database_backup import DatabaseBackupManager + + backup_manager = DatabaseBackupManager() + backups = backup_manager.list_backups() + + return jsonify({ + 'success': True, + 'backups': backups + }) + except Exception as e: + return jsonify({ + 'success': False, + 'message': f'Failed to list backups: {str(e)}' + }), 500 + +@bp.route('/api/backup/download/', methods=['GET']) +@admin_plus +def api_backup_download(filename): + """Download a backup file""" + try: + from app.database_backup import DatabaseBackupManager + from flask import send_file + import os + + backup_manager = DatabaseBackupManager() + backup_path = backup_manager.backup_path + file_path = os.path.join(backup_path, filename) + + # Security: ensure filename doesn't contain path traversal + if '..' in filename or '/' in filename: + return jsonify({ + 'success': False, + 'message': 'Invalid filename' + }), 400 + + if os.path.exists(file_path): + return send_file(file_path, as_attachment=True, download_name=filename) + else: + return jsonify({ + 'success': False, + 'message': 'Backup file not found' + }), 404 + except Exception as e: + return jsonify({ + 'success': False, + 'message': f'Download failed: {str(e)}' + }), 500 + +@bp.route('/api/backup/delete/', methods=['DELETE']) +@admin_plus +def api_backup_delete(filename): + """Delete a backup file""" + try: + from app.database_backup import DatabaseBackupManager + + backup_manager = DatabaseBackupManager() + result = backup_manager.delete_backup(filename) + + return jsonify(result) + except Exception as e: + return jsonify({ + 'success': False, + 'message': f'Delete failed: {str(e)}' + }), 500 + +@bp.route('/api/backup/schedule', methods=['GET', 'POST']) +@admin_plus +def api_backup_schedule(): + """Get or save backup schedule configuration""" + try: + from app.database_backup import DatabaseBackupManager + + backup_manager = DatabaseBackupManager() + + if request.method == 'POST': + schedule = request.json + result = backup_manager.save_backup_schedule(schedule) + return jsonify(result) + else: + schedule = backup_manager.get_backup_schedule() + return jsonify({ + 'success': True, + 'schedule': schedule + }) + except Exception as e: + return jsonify({ + 'success': False, + 'message': f'Schedule operation failed: {str(e)}' + }), 500 + +@bp.route('/api/backup/restore/', methods=['POST']) +@superadmin_only +def api_backup_restore(filename): + """Restore database from a backup file (superadmin only)""" + try: + from app.database_backup import DatabaseBackupManager + + backup_manager = DatabaseBackupManager() + result = backup_manager.restore_backup(filename) + + return jsonify(result) + except Exception as e: + return jsonify({ + 'success': False, + 'message': f'Restore failed: {str(e)}' + }), 500 \ No newline at end of file diff --git a/py_app/app/settings.py b/py_app/app/settings.py index 73d92e8..9f7083e 100755 --- a/py_app/app/settings.py +++ b/py_app/app/settings.py @@ -1,6 +1,4 @@ from flask import render_template, request, session, redirect, url_for, flash, current_app, jsonify -from .models import User -from . import db from .permissions import APP_PERMISSIONS, ROLE_HIERARCHY, ACTIONS, get_all_permissions, get_default_permissions_for_role import mariadb import os diff --git a/py_app/app/templates/daily_mirror.html b/py_app/app/templates/daily_mirror.html new file mode 100644 index 0000000..aa1bd7d --- /dev/null +++ b/py_app/app/templates/daily_mirror.html @@ -0,0 +1,447 @@ +{% extends "base.html" %} + +{% block title %}Daily Mirror - Quality Recticel{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿ“ˆ Daily Mirror

+

Generate comprehensive daily production reports

+
+ +
+
+
+ + +
+
+
+
+
+ Select Report Date +
+
+
+
+
+ + +
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + +
+ + + + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/daily_mirror_build_database.html b/py_app/app/templates/daily_mirror_build_database.html new file mode 100644 index 0000000..e69e239 --- /dev/null +++ b/py_app/app/templates/daily_mirror_build_database.html @@ -0,0 +1,760 @@ +{% extends "base.html" %} + +{% block title %}Build Database - Daily Mirror{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿ”จ Build Database

+

Upload Excel files to populate Daily Mirror database tables

+
+
+
+
+ +
+
+ +
+
+
+ Upload Excel File +
+
+
+
+ +
+ + + + Select a table to see its description. + +
+ + +
+ + + + Accepted formats: .xlsx, .xls (Maximum file size: 10MB) + +
+ + +
+ +
+
+
+
+
+ +
+ +
+
+
+ Excel File Format Instructions +
+
+
+
+ +
+

+ +

+
+
+

Expected columns for Production Data:

+
+
+
    +
  • Production Order ID Unique identifier
  • +
  • Customer Code Customer code
  • +
  • Customer Name Customer name
  • +
  • Article Code Article code
  • +
+
+
+
    +
  • Article Description Description
  • +
  • Quantity To produce
  • +
  • Production Date Date
  • +
  • Status Production status
  • +
+
+
+
+
+
+ + +
+

+ +

+
+
+

Expected columns for Orders Data:

+
+
+
    +
  • Order ID Unique identifier
  • +
  • Customer Code Customer code
  • +
  • Customer Name Customer name
  • +
  • Article Code Article code
  • +
+
+
+
    +
  • Article Description Description
  • +
  • Quantity Ordered Ordered
  • +
  • Order Date Date
  • +
  • Status Order status
  • +
+
+
+
+
+
+ + +
+

+ +

+
+
+

Expected columns for Delivery Data:

+
+
+
    +
  • Shipment ID Unique shipment identifier
  • +
  • Order ID Related order
  • +
  • Customer Customer info
  • +
  • Article Code/description
  • +
+
+
+
    +
  • Quantity Delivered Delivered quantity
  • +
  • Delivery Date Date
  • +
  • Status Delivery status
  • +
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + +{% endblock %} diff --git a/py_app/app/templates/daily_mirror_history.html b/py_app/app/templates/daily_mirror_history.html new file mode 100644 index 0000000..b5a0518 --- /dev/null +++ b/py_app/app/templates/daily_mirror_history.html @@ -0,0 +1,449 @@ +{% extends "base.html" %} + +{% block title %}Daily Mirror History - Quality Recticel{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿ“‹ Daily Mirror History

+

Analyze historical daily production reports and trends

+
+ +
+
+
+ + +
+
+
+
+
+ Select Date Range +
+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+
+
+
+
+
+
+ + + + + + + + + + + + + + + +
+ + + + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/daily_mirror_main.html b/py_app/app/templates/daily_mirror_main.html new file mode 100644 index 0000000..8f3c39c --- /dev/null +++ b/py_app/app/templates/daily_mirror_main.html @@ -0,0 +1,262 @@ +{% extends "base.html" %} + +{% block title %}Daily Mirror - Quality Recticel{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿ“Š Daily Mirror

+

Business Intelligence and Production Reporting

+
+
+ +
+
+
+
+ + +
+ +
+
+
+
+
+ +
+
+
Build Database
+

+ Upload Excel files to create and populate tables. +

+ +
+
+
+ + +
+
+
+
+
+ +
+
+
Tune Database
+

+ Edit and update records after import. +

+ +
+
+
+ + +
+
+
+
+
+ +
+
+
Daily Mirror
+

+ Generate daily production reports. +

+ +
+
+
+ + +
+
+
+
+
+ +
+
+
Daily Mirror History
+

+ View historical production reports. +

+ +
+
+
+
+
+ + + + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/daily_mirror_tune_delivery.html b/py_app/app/templates/daily_mirror_tune_delivery.html new file mode 100644 index 0000000..3df9d4a --- /dev/null +++ b/py_app/app/templates/daily_mirror_tune_delivery.html @@ -0,0 +1,549 @@ +{% extends "base.html" %} + +{% block title %}Tune Delivery Data - Daily Mirror{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿšš Tune Delivery Data

+

Edit and update delivery records information

+
+
+ +
+
+
+
+ + +
+
+
+
+
+ Filters and Search +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+ Delivery Records Data +
+
+ + {% if session.get('role') == 'superadmin' %} + + {% endif %} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + +
Shipment IDCustomerOrder IDArticle CodeDescriptionQuantityShipment DateDelivery DateStatusTotal ValueActions
+
+ + + + + + +
+ + + +
+
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/daily_mirror_tune_orders.html b/py_app/app/templates/daily_mirror_tune_orders.html new file mode 100644 index 0000000..bde0494 --- /dev/null +++ b/py_app/app/templates/daily_mirror_tune_orders.html @@ -0,0 +1,753 @@ +{% extends "base.html" %} + +{% block title %}Tune Orders Data - Daily Mirror{% endblock %} + +{% block extra_css %} + + +{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿ›’ Tune Orders Data

+

Edit and update customer orders information

+
+
+ +
+
+
+
+ + +
+
+
+
+
+ Filters and Search +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+ Customer Orders Data +
+
+ + {% if session.get('role') == 'superadmin' %} + + {% endif %} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
Order LineCustomerClient Order LineArticle CodeDescriptionQuantityDelivery DateStatusPriorityProduct GroupOrder DateActions
+
+ + + + + + +
+ + + +
+
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/daily_mirror_tune_production.html b/py_app/app/templates/daily_mirror_tune_production.html new file mode 100644 index 0000000..937bad2 --- /dev/null +++ b/py_app/app/templates/daily_mirror_tune_production.html @@ -0,0 +1,683 @@ +{% extends "base.html" %} + +{% block title %}Tune Production Data - Daily Mirror{% endblock %} + +{% block extra_css %} + + +{% endblock %} + +{% block content %} +
+ +
+
+
+
+

๐Ÿญ Tune Production Data

+

Edit and update production orders information

+
+
+ +
+
+
+
+ + +
+
+
+
+
+ Filters and Search +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ + +
+
+
+
+
+ Production Orders Data +
+
+ + {% if session.get('role') == 'superadmin' %} + + {% endif %} + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + +
Production OrderOpened for OrderClient Order-LineCustomerArticle CodeDescriptionQuantityDelivery DateStatusMachinePlanning DateActions
+
+ + + + + + +
+ + + +
+
+
+
+ + + + + +{% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/settings.html b/py_app/app/templates/settings.html index b73f197..588c38b 100755 --- a/py_app/app/templates/settings.html +++ b/py_app/app/templates/settings.html @@ -50,6 +50,72 @@ Recommended: Use the simplified user management for easier administration + + {% if session.role in ['superadmin', 'admin'] %} +
+

๐Ÿ’พ Database Backup Management

+

Automated Backup System: Schedule and manage database backups

+ + +
+

Quick Actions

+ + +
+ + +
+

Backup Schedule

+
+
+ +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+ + +
+

Available Backups

+
+

Loading backups...

+
+
+ + +
+ โ„น๏ธ Backup Location: /srv/quality_app/backups +
+ Configure backup path in docker-compose.yml (BACKUP_PATH environment variable) +
+
+ {% endif %} @@ -136,5 +202,162 @@ Array.from(document.getElementsByClassName('delete-user-btn')).forEach(function( document.getElementById('close-delete-popup-btn').onclick = function() { document.getElementById('delete-user-popup').style.display = 'none'; }; + +// ======================================== +// Database Backup Management Functions +// ======================================== + +// Load backup schedule on page load +function loadBackupSchedule() { + fetch('/api/backup/schedule') + .then(response => response.json()) + .then(data => { + if (data.success) { + document.getElementById('schedule-enabled').checked = data.schedule.enabled; + document.getElementById('schedule-time').value = data.schedule.time; + document.getElementById('schedule-frequency').value = data.schedule.frequency; + document.getElementById('retention-days').value = data.schedule.retention_days; + } + }) + .catch(error => console.error('Error loading schedule:', error)); +} + +// Load backup list +function loadBackupList() { + const backupList = document.getElementById('backup-list'); + if (!backupList) return; + + backupList.innerHTML = '

Loading backups...

'; + + fetch('/api/backup/list') + .then(response => response.json()) + .then(data => { + if (data.success && data.backups.length > 0) { + let html = ''; + html += ''; + html += ''; + + data.backups.forEach(backup => { + html += ` + + + + + `; + }); + + html += '
FilenameSizeCreatedActions
${backup.filename}${backup.size_mb} MB${backup.created} + + +
'; + backupList.innerHTML = html; + } else { + backupList.innerHTML = '

No backups available

'; + } + }) + .catch(error => { + console.error('Error loading backups:', error); + backupList.innerHTML = '

Error loading backups

'; + }); +} + +// Backup now button +document.getElementById('backup-now-btn')?.addEventListener('click', function() { + const btn = this; + btn.disabled = true; + btn.innerHTML = 'โณ Creating backup...'; + + fetch('/api/backup/create', { + method: 'POST' + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + alert('โœ… ' + data.message + '\nFile: ' + data.filename + '\nSize: ' + data.size); + loadBackupList(); + } else { + alert('โŒ ' + data.message); + } + btn.disabled = false; + btn.innerHTML = 'โšก Backup Now'; + }) + .catch(error => { + console.error('Error creating backup:', error); + alert('โŒ Failed to create backup'); + btn.disabled = false; + btn.innerHTML = 'โšก Backup Now'; + }); +}); + +// Refresh backups button +document.getElementById('refresh-backups-btn')?.addEventListener('click', function() { + loadBackupList(); +}); + +// Save schedule form +document.getElementById('backup-schedule-form')?.addEventListener('submit', function(e) { + e.preventDefault(); + + const formData = { + enabled: document.getElementById('schedule-enabled').checked, + time: document.getElementById('schedule-time').value, + frequency: document.getElementById('schedule-frequency').value, + retention_days: parseInt(document.getElementById('retention-days').value) + }; + + fetch('/api/backup/schedule', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(formData) + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + alert('โœ… ' + data.message); + } else { + alert('โŒ ' + data.message); + } + }) + .catch(error => { + console.error('Error saving schedule:', error); + alert('โŒ Failed to save schedule'); + }); +}); + +// Download backup function +function downloadBackup(filename) { + window.location.href = `/api/backup/download/${filename}`; +} + +// Delete backup function +function deleteBackup(filename) { + if (confirm(`Are you sure you want to delete backup: ${filename}?`)) { + fetch(`/api/backup/delete/${filename}`, { + method: 'DELETE' + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + alert('โœ… ' + data.message); + loadBackupList(); + } else { + alert('โŒ ' + data.message); + } + }) + .catch(error => { + console.error('Error deleting backup:', error); + alert('โŒ Failed to delete backup'); + }); + } +} + +// Load backup data on page load +if (document.getElementById('backup-list')) { + loadBackupSchedule(); + loadBackupList(); +} + {% endblock %} \ No newline at end of file diff --git a/py_app/app/templates/user_management_simple.html b/py_app/app/templates/user_management_simple.html index 8c5aa78..bcd86a1 100644 --- a/py_app/app/templates/user_management_simple.html +++ b/py_app/app/templates/user_management_simple.html @@ -355,6 +355,100 @@ background-color: #2a2a2a !important; color: #fff !important; } + + /* Modal fixes for accessibility */ + .modal { + z-index: 1050 !important; + } + + .modal-backdrop { + z-index: 1040 !important; + } + + /* Theme-aware modal styling */ + body.light-mode .modal-content { + background-color: #fff !important; + color: #000 !important; + } + + body.dark-mode .modal-content { + background-color: #2a2a2a !important; + color: #fff !important; + border: 1px solid #444 !important; + } + + body.light-mode .modal-header { + background-color: #f8f9fa !important; + border-bottom: 1px solid #dee2e6 !important; + } + + body.dark-mode .modal-header { + background-color: #1e1e1e !important; + border-bottom: 1px solid #444 !important; + } + + body.light-mode .modal-title { + color: #000 !important; + } + + body.dark-mode .modal-title { + color: #fff !important; + } + + body.light-mode .modal-body { + background-color: #fff !important; + color: #000 !important; + } + + body.dark-mode .modal-body { + background-color: #2a2a2a !important; + color: #fff !important; + } + + body.light-mode .modal-footer { + background-color: #f8f9fa !important; + border-top: 1px solid #dee2e6 !important; + } + + body.dark-mode .modal-footer { + background-color: #1e1e1e !important; + border-top: 1px solid #444 !important; + } + + /* Modal form elements */ + body.light-mode .modal .form-control { + background-color: #fff !important; + color: #000 !important; + border-color: #ced4da !important; + } + + body.dark-mode .modal .form-control { + background-color: #1a1a1a !important; + color: #fff !important; + border-color: #444 !important; + } + + body.light-mode .modal label { + color: #000 !important; + } + + body.dark-mode .modal label { + color: #ccc !important; + } + + /* Ensure modal is clickable and not greyed out */ + .modal.show { + display: block !important; + pointer-events: auto !important; + } + + .modal-dialog { + pointer-events: auto !important; + } + + .modal-content { + pointer-events: auto !important; + } {% endblock %} @@ -408,6 +502,10 @@ +
+ + +
@@ -454,6 +552,10 @@ +
+ + +
@@ -621,6 +723,10 @@ +
+ + +
diff --git a/py_app/gunicorn.conf.py b/py_app/gunicorn.conf.py index 6fe7fe3..84d9f1c 100644 --- a/py_app/gunicorn.conf.py +++ b/py_app/gunicorn.conf.py @@ -46,8 +46,10 @@ max_requests_jitter = int(os.getenv("GUNICORN_MAX_REQUESTS_JITTER", "100")) # LOGGING CONFIGURATION # ============================================================================ # Docker-friendly: logs to stdout/stderr by default, but allow file logging -accesslog = os.getenv("GUNICORN_ACCESS_LOG", "/srv/quality_recticel/logs/access.log") -errorlog = os.getenv("GUNICORN_ERROR_LOG", "/srv/quality_recticel/logs/error.log") +# Automatically detect the correct log path based on current directory +default_log_dir = "/srv/quality_app/logs" if "/srv/quality_app" in os.getcwd() else "/srv/quality_recticel/logs" +accesslog = os.getenv("GUNICORN_ACCESS_LOG", f"{default_log_dir}/access.log") +errorlog = os.getenv("GUNICORN_ERROR_LOG", f"{default_log_dir}/error.log") # For pure Docker logging (12-factor app), use: # accesslog = "-" # stdout @@ -162,4 +164,4 @@ def worker_abort(worker): def child_exit(server, worker): """Called just after a worker has been exited, in the master process.""" - server.log.info("๐Ÿ‘‹ Worker %s exited (exit code: %s)", worker.pid, worker.tmp.last_mtime) \ No newline at end of file + server.log.info("๐Ÿ‘‹ Worker %s exited", worker.pid) \ No newline at end of file diff --git a/py_app/instance/external_server.conf b/py_app/instance/external_server.conf index 7644f16..badbb73 100755 --- a/py_app/instance/external_server.conf +++ b/py_app/instance/external_server.conf @@ -3,3 +3,4 @@ port=3306 database_name=trasabilitate username=trasabilitate password=Initial01! +backup_path=/srv/quality_app/backups diff --git a/py_app/requirements.txt b/py_app/requirements.txt index 6e03e7a..1e2dc61 100755 --- a/py_app/requirements.txt +++ b/py_app/requirements.txt @@ -2,7 +2,6 @@ Flask Flask-SSLify Werkzeug gunicorn -flask-sqlalchemy pyodbc mariadb reportlab diff --git a/run/trasabilitate.pid b/run/trasabilitate.pid index 804d1fd..5458dd0 100644 --- a/run/trasabilitate.pid +++ b/run/trasabilitate.pid @@ -1 +1 @@ -394337 +399048