OPTIMIZED: Combined fast endpoint that returns all essential dashboard data in one call. This eliminates multiple round-trips and significantly speeds up dashboard loading on Pi Zero.
()
| 18604 | # Check if directory exists |
| 18605 | if not os.path.exists(actual_path): |
| 18606 | return jsonify([]) |
| 18607 | |
| 18608 | # List files in directory |
| 18609 | files = [] |
| 18610 | for entry in os.scandir(actual_path): |
| 18611 | files.append({ |
| 18612 | 'name': entry.name, |
| 18613 | 'is_directory': entry.is_dir(), |
| 18614 | 'path': os.path.join(path, entry.name), |
| 18615 | 'size': entry.stat().st_size if entry.is_file() else 0, |
| 18616 | 'modified': entry.stat().st_mtime |
| 18617 | }) |
| 18618 | |
| 18619 | # Sort files - directories first, then by name |
| 18620 | files.sort(key=lambda x: (not x['is_directory'], x['name'].lower())) |
| 18621 | |
| 18622 | return jsonify(files) |
| 18623 | |
| 18624 | except Exception as e: |
| 18625 | logger.error(f"Error listing files: {e}") |
| 18626 | return jsonify({'error': str(e)}), 500 |
| 18627 | |
| 18628 | |
| 18629 | @app.route('/api/files/preview') |
| 18630 | def preview_file_api(): |
| 18631 | """Preview file contents inline — text, CSV, images""" |
| 18632 | import mimetypes, base64 |
| 18633 | try: |
| 18634 | file_path = request.args.get('path') |
| 18635 | if not file_path: |
| 18636 | return jsonify({'error': 'File path required'}), 400 |
| 18637 | |
| 18638 | # Map virtual path to actual path |
| 18639 | actual_path = '' |
| 18640 | if file_path.startswith('/data_stolen'): |
| 18641 | resolved = _resolve_loot_path('/data_stolen', 'loot/data_stolen', file_path) |
| 18642 | if resolved is None: |
| 18643 | return jsonify({'error': 'Invalid path'}), 400 |
| 18644 | actual_path = resolved |
| 18645 | elif file_path.startswith('/scan_results'): |
| 18646 | resolved = _resolve_loot_path('/scan_results', 'output/scan_results', file_path) |
| 18647 | if resolved is None: |
| 18648 | return jsonify({'error': 'Invalid path'}), 400 |
| 18649 | actual_path = resolved |
| 18650 | elif file_path.startswith('/crackedpwd'): |
| 18651 | resolved = _resolve_loot_path('/crackedpwd', 'loot/credentials', file_path) |
| 18652 | if resolved is None: |
| 18653 | return jsonify({'error': 'Invalid path'}), 400 |
| 18654 | actual_path = resolved |
| 18655 | elif file_path.startswith('/vulnerabilities'): |
| 18656 | resolved = _resolve_loot_path('/vulnerabilities', 'output/vulnerabilities', file_path) |
| 18657 | if resolved is None: |
| 18658 | return jsonify({'error': 'Invalid path'}), 400 |
| 18659 | actual_path = resolved |
| 18660 | elif file_path == '/logs' or file_path.startswith('/logs/'): |
| 18661 | try: |
| 18662 | actual_path = _resolve_legacy_path('/logs', os.path.join(shared_data.datadir, 'logs'), file_path) |
| 18663 | except ValueError: |
nothing calls this directly
no test coverage detected