git status! RFACTOR TEMPLATE git status!
This commit is contained in:
		
							
								
								
									
										13
									
								
								app.py
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								app.py
									
									
									
									
									
								
							| @@ -691,6 +691,19 @@ def get_data_folder_size(): | ||||
|                 pass | ||||
|     return total_size | ||||
|  | ||||
| @app.template_global() | ||||
| def bootstrap_alert_category(cat): | ||||
|     mapping = { | ||||
|         "error": "danger", | ||||
|         "fail": "danger", | ||||
|         "warn": "warning", | ||||
|         "warning": "warning", | ||||
|         "ok": "success", | ||||
|         "success": "success", | ||||
|         "info": "info" | ||||
|     } | ||||
|     return mapping.get(cat.lower(), "info") | ||||
|  | ||||
| ############################################################################### | ||||
| # ROUTES | ||||
| ############################################################################### | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container mt-5"> | ||||
|   <div class="card shadow-sm"> | ||||
|     <div class="card-header"> | ||||
|       <h2 class="mb-0">Dodaj nowe urządzenie</h2> | ||||
| <div class="container my-4"> | ||||
|   <div class="card border-0 shadow-sm"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0">Dodaj nowe urządzenie</h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <form method="POST"> | ||||
| @@ -24,18 +24,16 @@ | ||||
|           <input type="text" class="form-control" id="ssh_user" name="ssh_user" required> | ||||
|         </div> | ||||
|         <div class="mb-3"> | ||||
|           <label for="ssh_key" class="form-label"> | ||||
|             <b>Klucz prywatny</b> | Wklej wraz z <code>-----BEGIN RSA PRIVATE KEY-----</code> i <code>-----END RSA PRIVATE KEY-----</code><br> | ||||
|             Pozostaw puste jeśli ten RouterOS będzie używał <a href="{{ url_for('settings_view') }}">klucza globalnego</a> | ||||
|           </label> | ||||
|           <textarea class="form-control" id="ssh_key" name="ssh_key" rows="4"></textarea> | ||||
|           <label for="ssh_key" class="form-label"><b>Klucz prywatny</b></label><br> | ||||
|           <small>Wklej wraz z <code>-----BEGIN RSA PRIVATE KEY-----</code> i <code>-----END RSA PRIVATE KEY-----</code>. Jeśli pusty – użyje klucza globalnego.</small> | ||||
|           <textarea class="form-control mt-2" id="ssh_key" name="ssh_key" rows="4"></textarea> | ||||
|         </div> | ||||
|         <div class="mb-3"> | ||||
|           <label for="ssh_password" class="form-label"><b>Hasło SSH</b></label><br> | ||||
|           Jeśli podajesz klucz SSH lub zdefiniowany jest <a href="{{ url_for('settings_view') }}">klucz globalny</a>, to logowanie hasłem jest nieaktywne. | ||||
|           <input type="password" class="form-control" id="ssh_password" name="ssh_password"> | ||||
|           <small>Jeśli jest klucz SSH lub klucz globalny, hasło może być ignorowane.</small> | ||||
|           <input type="password" class="form-control mt-2" id="ssh_password" name="ssh_password"> | ||||
|         </div> | ||||
|         <button type="submit" class="btn btn-primary">Dodaj</button> | ||||
|         <button type="submit" class="btn btn-primary">Dodaj urządzenie</button> | ||||
|       </form> | ||||
|     </div> | ||||
|   </div> | ||||
|   | ||||
| @@ -1,52 +1,61 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container mt-5"> | ||||
|   <div class="card shadow-sm"> | ||||
|     <div class="card-header"> | ||||
|       <h2 class="mb-0">Zaawansowane ustawienia harmonogramu</h2> | ||||
| <div class="container my-4"> | ||||
|   <div class="card border-0 shadow-sm"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0">Zaawansowane ustawienia harmonogramu</h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <form action="{{ url_for('advanced_schedule') }}" method="POST"> | ||||
|         <div class="mb-3"> | ||||
|           <div class="mb-3"> | ||||
|             <label for="backup_retention_days" class="form-label">Próg retencji backupów (dni)</label> | ||||
|             <small>Usuwanie danych starszych niż ustawione w progu</small> | ||||
|             <input type="number" class="form-control" id="backup_retention_days" name="backup_retention_days" value="{{ settings.backup_retention_days }}"> | ||||
|           </div> | ||||
|           <div class="mb-3"> | ||||
|             <label for="log_retention_days" class="form-label">Próg retencji logów (dni)</label> | ||||
|             <input type="number" class="form-control" id="log_retention_days" name="log_retention_days" value="{{ settings.log_retention_days }}"> | ||||
|           </div> | ||||
|           <label for="retention_cron" class="form-label">Harmonogram retencji</label> <code>cron</code> | ||||
|           <label for="backup_retention_days" class="form-label">Próg retencji backupów (dni)</label> | ||||
|           <small class="text-muted d-block mb-2">Usuwanie danych starszych niż ustawione w progu.</small> | ||||
|           <input type="number" class="form-control" id="backup_retention_days" name="backup_retention_days" value="{{ settings.backup_retention_days }}"> | ||||
|         </div> | ||||
|         <div class="mb-3"> | ||||
|           <label for="log_retention_days" class="form-label">Próg retencji logów (dni)</label> | ||||
|           <input type="number" class="form-control" id="log_retention_days" name="log_retention_days" value="{{ settings.log_retention_days }}"> | ||||
|         </div> | ||||
|          | ||||
|         <div class="mb-3"> | ||||
|           <label for="retention_cron" class="form-label">Harmonogram retencji <code>cron</code></label> | ||||
|           <div class="input-group"> | ||||
|             <input type="text" class="form-control" id="retention_cron" name="retention_cron" value="{{ settings.retention_cron }}"> | ||||
|             <button type="button" class="btn btn-outline-secondary" onclick="openCronModal('retention_cron')">Generuj cron</button> | ||||
|           </div> | ||||
|           <div class="form-text">Np. <code>0 */12 * * *</code> – co 12 godzin</div> | ||||
|           <small class="text-muted">Np. <code>0 */12 * * *</code> – co 12 godzin</small> | ||||
|         </div> | ||||
|          | ||||
|         <div class="mb-3"> | ||||
|           <label for="binary_cron" class="form-label">Harmonogram kopii zapasowych binarnych</label> <code>cron</code> | ||||
|           <label for="binary_cron" class="form-label">Harmonogram kopii zapasowych binarnych <code>cron</code></label> | ||||
|           <div class="input-group"> | ||||
|             <input type="text" class="form-control" id="binary_cron" name="binary_cron" value="{{ settings.binary_cron|default('') }}"> | ||||
|             <button type="button" class="btn btn-outline-secondary" onclick="openCronModal('binary_cron')">Generuj cron</button> | ||||
|           </div> | ||||
|           <div class="form-text">Np. <code>15 2 * * *</code> – codziennie o 2:15</div> | ||||
|           <small class="text-muted">Np. <code>15 2 * * *</code> – codziennie o 2:15</small> | ||||
|         </div> | ||||
|          | ||||
|         <div class="mb-3"> | ||||
|           <label for="export_cron" class="form-label">Harmonogram exportów (poleceń /export) <code>cron</code></label> | ||||
|           <label for="export_cron" class="form-label">Harmonogram exportów <code>cron</code></label> | ||||
|           <div class="input-group"> | ||||
|             <input type="text" class="form-control" id="export_cron" name="export_cron" value="{{ settings.export_cron }}"> | ||||
|             <button type="button" class="btn btn-outline-secondary" onclick="openCronModal('export_cron')">Generuj cron</button> | ||||
|           </div> | ||||
|           <div class="form-text">Np. <code>0 */12 * * *</code> – co 12 godzin</div> | ||||
|           <small class="text-muted">Np. <code>0 */12 * * *</code> – co 12 godzin</small> | ||||
|         </div> | ||||
|          | ||||
|         <div class="mb-3 form-check"> | ||||
|           <input type="checkbox" class="form-check-input" id="enable_auto_export" name="enable_auto_export" {% if settings.enable_auto_export %}checked{% endif %}> | ||||
|           <label class="form-check-label" for="enable_auto_export">Włącz automatyczny export</label> | ||||
|         </div> | ||||
|          | ||||
|         <button type="submit" class="btn btn-primary">Zapisz ustawienia</button> | ||||
|       </form> | ||||
|     </div> | ||||
|      | ||||
|     <div class="card-footer text-center"> | ||||
|       <small class="text-muted">Ustawienia zostaną zapisane i użyte przez cron.</small> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
|   | ||||
| @@ -3,8 +3,8 @@ | ||||
| <div class="container my-4"> | ||||
|   <h2 class="text-center mb-4">Lista wszystkich backupów</h2> | ||||
|    | ||||
|   <!-- Formularz filtrowania --> | ||||
|   <div class="card mb-4 shadow-sm"> | ||||
|   <!-- Karta filtra --> | ||||
|   <div class="card mb-4 shadow-sm border-0"> | ||||
|     <div class="card-body"> | ||||
|       <form method="GET" action="{{ url_for('all_files') }}" class="row g-2"> | ||||
|         <div class="col-md-4"> | ||||
| @@ -29,11 +29,11 @@ | ||||
|     </div> | ||||
|   </div> | ||||
|    | ||||
|   <!-- Tabela z backupami --> | ||||
|   <div class="card shadow-sm mb-4"> | ||||
|   <!-- Karta tabeli backupów --> | ||||
|   <div class="card shadow-sm border-0 mb-4"> | ||||
|     <div class="card-body"> | ||||
|       <div class="table-responsive"> | ||||
|         <table class="table table-striped table-hover mb-0"> | ||||
|         <table class="table table-striped table-hover align-middle"> | ||||
|           <thead class="table-dark"> | ||||
|             <tr> | ||||
|               <th style="width: 2%;"><input type="checkbox" id="select_all"></th> | ||||
| @@ -73,14 +73,13 @@ | ||||
|               <td>{{ file.created_at.strftime("%Y-%m-%d %H:%M:%S") }}</td> | ||||
|               <td>{{ file.file_path|filesize }}</td> | ||||
|               <td> | ||||
|                 <a href="{{ url_for('download_file', filename=file.file_path|basename) }}" class="btn btn-lg btn-info"> | ||||
|                 <a href="{{ url_for('download_file', filename=file.file_path|basename) }}" class="btn btn-sm btn-info"> | ||||
|                   <i class="bi bi-download"></i> | ||||
|                 </a> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <form action="{{ url_for('send_by_email', backup_id=file.id) }}" method="POST" class="d-inline"> | ||||
|                   <input type="hidden" name="next" value="{{ url_for('all_files') }}"> | ||||
|                   <button type="submit" class="btn btn-lg btn-warning"> | ||||
|                   <button type="submit" class="btn btn-sm btn-warning"> | ||||
|                     <i class="bi bi-envelope"></i> | ||||
|                   </button> | ||||
|                 </form> | ||||
| @@ -88,8 +87,7 @@ | ||||
|               <td> | ||||
|                 {% if file.backup_type == 'binary' %} | ||||
|                   <form action="{{ url_for('upload_backup', router_id=file.router.id, backup_id=file.id) }}" method="POST" class="d-inline"> | ||||
|                     <input type="hidden" name="next" value="{{ url_for('all_files') }}"> | ||||
|                     <button type="submit" class="btn btn-lg btn-secondary"> | ||||
|                     <button type="submit" class="btn btn-sm btn-secondary"> | ||||
|                       <i class="bi bi-upload"></i> | ||||
|                     </button> | ||||
|                   </form> | ||||
| @@ -99,7 +97,7 @@ | ||||
|               </td> | ||||
|               <td> | ||||
|                 {% if file.backup_type == 'export' %} | ||||
|                   <a href="{{ url_for('view_export', backup_id=file.id) }}" class="btn btn-lg btn-outline-primary"> | ||||
|                   <a href="{{ url_for('view_export', backup_id=file.id) }}" class="btn btn-sm btn-outline-primary"> | ||||
|                     <i class="bi bi-eye"></i> | ||||
|                   </a> | ||||
|                 {% else %} | ||||
| @@ -108,8 +106,7 @@ | ||||
|               </td> | ||||
|               <td> | ||||
|                 <form action="{{ url_for('delete_backup', backup_id=file.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Na pewno usunąć backup?');"> | ||||
|                   <input type="hidden" name="next" value="{{ url_for('all_files') }}"> | ||||
|                   <button type="submit" class="btn btn-lg btn-danger"> | ||||
|                   <button type="submit" class="btn btn-sm btn-danger"> | ||||
|                     <i class="bi bi-trash"></i> | ||||
|                   </button> | ||||
|                 </form> | ||||
| @@ -123,16 +120,17 @@ | ||||
|     </div> | ||||
|   </div> | ||||
|    | ||||
|   <!-- Formularz dla masowych akcji (jeden formularz) --> | ||||
|   <!-- Formularz dla masowych akcji (zaznaczone pliki) --> | ||||
|   <form id="mass_actions_form" action="{{ url_for('mass_actions') }}" method="POST" class="d-flex justify-content-end mb-4"> | ||||
|     <button type="submit" name="action" value="download" class="btn btn-lg btn-success me-2"> | ||||
|       <i class="bi bi-file-earmark-zip"></i> Pobierz zip zaznaczonych | ||||
|     <button type="submit" name="action" value="download" class="btn btn-success me-2"> | ||||
|       <i class="bi bi-file-earmark-zip"></i> Pobierz zip | ||||
|     </button> | ||||
|     <button type="submit" name="action" value="delete" class="btn btn-lg btn-danger" onclick="return confirm('Na pewno usunąć zaznaczone pliki?');"> | ||||
|       <i class="bi bi-trash"></i> Usuń zaznaczone pliki | ||||
|     <button type="submit" name="action" value="delete" class="btn btn-danger" onclick="return confirm('Na pewno usunąć zaznaczone pliki?');"> | ||||
|       <i class="bi bi-trash"></i> Usuń zaznaczone | ||||
|     </button> | ||||
|   </form> | ||||
| </div> | ||||
|  | ||||
| <script> | ||||
| document.getElementById('select_all').addEventListener('change', function(e) { | ||||
|   var checkboxes = document.querySelectorAll('input[name="backup_id"]'); | ||||
|   | ||||
| @@ -8,11 +8,12 @@ | ||||
|   <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> | ||||
|    | ||||
|   <style> | ||||
|     /* Poprawa kontrastu dla form-text w trybie ciemnym */ | ||||
|     /* 1) Poprawa kontrastu dla form-text w trybie ciemnym */ | ||||
|     .dark-mode .form-text { | ||||
|       color: #ccc !important; | ||||
|     } | ||||
|     /* Ogólne style trybu ciemnego */ | ||||
|  | ||||
|     /* 2) Ogólne style trybu ciemnego */ | ||||
|     .dark-mode body { | ||||
|       background-color: #121212; | ||||
|       color: #ffffff; | ||||
| @@ -21,8 +22,8 @@ | ||||
|     .dark-mode a:hover { | ||||
|       color: #ddd; | ||||
|     } | ||||
|      | ||||
|     /* Nawigacja i menu */ | ||||
|  | ||||
|     /* 3) Nawigacja i menu w trybie ciemnym */ | ||||
|     .dark-mode .navbar, | ||||
|     .dark-mode .navbar-nav, | ||||
|     .dark-mode .dropdown-menu { | ||||
| @@ -35,15 +36,15 @@ | ||||
|     .dark-mode .navbar-nav .nav-link:hover { | ||||
|       color: #fff !important; | ||||
|     } | ||||
|     /* Nowa reguła dla elementów dropdown po najechaniu */ | ||||
|     /* Dropdown :hover, :focus, active */ | ||||
|     .dark-mode .dropdown-item:hover, | ||||
|     .dark-mode .dropdown-item:focus, | ||||
|     .dark-mode .dropdown-item.active { | ||||
|       background-color: #444 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|      | ||||
|     /* Tabele */ | ||||
|  | ||||
|     /* 4) Tabele w trybie ciemnym */ | ||||
|     .dark-mode .table { | ||||
|       background-color: #333 !important; | ||||
|       border-color: #444; | ||||
| @@ -67,7 +68,8 @@ | ||||
|       color: #ddd !important; | ||||
|       border: 1px solid #555 !important; | ||||
|     } | ||||
|     /* Pola formularzy */ | ||||
|  | ||||
|     /* 5) Pola formularzy (input, select, textarea) w trybie ciemnym */ | ||||
|     .dark-mode input, | ||||
|     .dark-mode textarea, | ||||
|     .dark-mode select { | ||||
| @@ -78,26 +80,42 @@ | ||||
|     .dark-mode ::placeholder { | ||||
|       color: #ccc; | ||||
|     } | ||||
|      | ||||
|     /* Przyciski - poprawiony kontrast dla btn-warning */ | ||||
|  | ||||
|     /* 6) Przyciski w trybie ciemnym: .btn-warning, .btn-secondary, .btn-outline-dark */ | ||||
|     .dark-mode .btn-warning { | ||||
|       background-color: #d39e00; | ||||
|       border-color: #b38600; | ||||
|       color: #fff; | ||||
|     } | ||||
|     .dark-mode .btn-warning:hover { | ||||
|       background-color: #e6aa00 !important; | ||||
|       border-color: #c98f00 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|     .dark-mode .btn-secondary { | ||||
|       background-color: #444; | ||||
|       border-color: #555; | ||||
|       color: #fff; | ||||
|     } | ||||
|      | ||||
|     /* Bloki (np. zawartość kontenera, karty) */ | ||||
|     .dark-mode .block, .dark-mode .card { | ||||
|     .dark-mode .btn-secondary:hover { | ||||
|       background-color: #555 !important; | ||||
|       border-color: #888888 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|     .dark-mode .btn-outline-dark:hover { | ||||
|       background-color: #444 !important; | ||||
|       border-color: #888888 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|  | ||||
|     /* 7) Karty i bloki w trybie ciemnym */ | ||||
|     .dark-mode .block, | ||||
|     .dark-mode .card { | ||||
|       background-color: #171717; | ||||
|       color: #fff; | ||||
|     } | ||||
|      | ||||
|     /* Stopka */ | ||||
|  | ||||
|     /* 8) Stopka */ | ||||
|     .dark-mode footer { | ||||
|       background-color: #1e1e1e !important; | ||||
|       color: #fff !important; | ||||
| @@ -106,12 +124,14 @@ | ||||
|       background-color: #f8f9fa; | ||||
|       color: #212529; | ||||
|     } | ||||
|      | ||||
|     /* Alerty – pozostają bez zmian */ | ||||
|     .diff-add { color: green; } | ||||
|     .diff-rem { color: red; } | ||||
|  | ||||
|     /* Dodatkowe nadpisanie styli diff2html w trybie ciemnym */ | ||||
|     /* 9) Nadpisanie .card-header.bg-light w trybie ciemnym */ | ||||
|     .dark-mode .card-header.bg-light { | ||||
|       background-color: #333 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|  | ||||
|     /* 10) Style diff2html w trybie ciemnym */ | ||||
|     .dark-mode .d2h-wrapper, | ||||
|     .dark-mode .d2h-file-header, | ||||
|     .dark-mode .d2h-file-info, | ||||
| @@ -125,7 +145,7 @@ | ||||
|       border-color: #444 !important; | ||||
|     } | ||||
|  | ||||
|     /* Style trybu ciemnego dla modala */ | ||||
|     /* 11) Modal w trybie ciemnym */ | ||||
|     .dark-mode .modal-content { | ||||
|       background-color: #333; | ||||
|       color: #ddd; | ||||
| @@ -142,11 +162,12 @@ | ||||
|       filter: invert(1); | ||||
|     } | ||||
|  | ||||
|     /* Niestandardowy styl dla trybu jasnego – ciemniejsze, szare menu */ | ||||
|     /* 12) Niestandardowy styl trybu jasnego – navbar */ | ||||
|     .navbar-light.bg-custom-light { | ||||
|       background-color: #dcdcdc !important; | ||||
|     } | ||||
|  | ||||
|     /* 13) DataTables w trybie ciemnym */ | ||||
|     .dark-mode .dataTables_wrapper .dataTables_paginate .paginate_button { | ||||
|       background-color: #333 !important; | ||||
|       color: #fff !important; | ||||
| @@ -180,48 +201,46 @@ | ||||
|       border-color: #555 !important; | ||||
|       box-shadow: none !important; | ||||
|     } | ||||
|     .dark-mode .btn-secondary:hover { | ||||
|       background-color: #555 !important; | ||||
|       border-color: #888888 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|     .dark-mode .btn-warning:hover { | ||||
|       background-color: #e6aa00 !important; | ||||
|       border-color: #c98f00 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|     .dark-mode .btn-outline-dark:hover { | ||||
|       background-color: #444 !important; | ||||
|       border-color: #888888 !important; | ||||
|       color: #fff !important; | ||||
|     } | ||||
|  | ||||
|     /* 14) Drobne poprawki przycisków wylogowania */ | ||||
|     .btn-logout { | ||||
|       color: #fff; | ||||
|     } | ||||
|      | ||||
|     /* Ustawienia globalne */ | ||||
|  | ||||
|     /* 15) Główne ustawienia, flex layout */ | ||||
|     html, body { | ||||
|       height: 100%; | ||||
|       margin: 0; | ||||
|       padding: 0; | ||||
|     } | ||||
|     body { | ||||
|       display: flex; | ||||
|       flex-direction: column; | ||||
|       min-height: 100vh; | ||||
|     } | ||||
|     /* Wrapper głównej zawartości, który rośnie, aby wypełnić przestrzeń */ | ||||
|     .wrapper { | ||||
|     main.container { | ||||
|       flex: 1; | ||||
|     } | ||||
|  | ||||
|     /* 16) Alerty (diff-add, diff-rem) – pozostawione bez zmian */ | ||||
|     .diff-add { color: green; } | ||||
|     .diff-rem { color: red; } | ||||
|  | ||||
|     .dark-mode .text-muted { | ||||
|       color: #aaa !important; /* zamiast #aaa możesz wybrać #bbb, #ccc itp. */ | ||||
|     } | ||||
|  | ||||
|   </style> | ||||
|  | ||||
|   {% block head %}{% endblock %} | ||||
| </head> | ||||
| <body> | ||||
|   <nav class="navbar navbar-expand-lg {% if session.get('dark_mode', True) %}navbar-dark bg-dark{% else %}navbar-light bg-custom-light{% endif %} mb-4"> | ||||
| <body class="d-flex flex-column"> | ||||
|   <!-- Navbar --> | ||||
|   <nav class="navbar navbar-expand-lg  | ||||
|               {% if session.get('dark_mode', True) %}navbar-dark bg-dark{% else %}navbar-light bg-custom-light{% endif %}  | ||||
|               mb-4"> | ||||
|     <div class="container-fluid"> | ||||
|      <!-- <a href="{{ url_for('dashboard') }}" class="navbar-brand">Backup RouterOS</a>  --> | ||||
|      <a href="/" class="navbar-brand">Backup RouterOS</a> | ||||
|       <a href="/" class="navbar-brand">Backup RouterOS</a> | ||||
|       <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavDropdown"  | ||||
|               aria-controls="navbarNavDropdown" aria-expanded="false" aria-label="Toggle navigation"> | ||||
|         <span class="navbar-toggler-icon"></span> | ||||
| @@ -267,18 +286,22 @@ | ||||
|           </li> | ||||
|         </ul> | ||||
|         {% endif %} | ||||
|  | ||||
|         <!-- Prawa strona navbaru --> | ||||
|         <ul class="navbar-nav ms-auto align-items-center"> | ||||
|           <!-- Przełącznik trybu ciemnego --> | ||||
|           <li class="nav-item me-2"> | ||||
|             <form action="{{ url_for('toggle_dark_mode') }}" method="GET" class="d-flex align-items-center"> | ||||
|               <div class="form-check form-switch mb-0"> | ||||
|                 <input class="form-check-input" type="checkbox" id="darkModeSwitch" onchange="this.form.submit()" {% if session.get('dark_mode', True) %}checked{% endif %}> | ||||
|                 <label class="form-check-label" for="darkModeSwitch">Tryb ciemny</label> | ||||
|                 <input class="form-check-input" type="checkbox" id="darkModeSwitch" | ||||
|                        onchange="this.form.submit()"  | ||||
|                        {% if session.get('dark_mode', True) %}checked{% endif %}> | ||||
|                 <label class="form-check-label ms-1" for="darkModeSwitch" style="user-select:none;">Ciemny</label> | ||||
|               </div> | ||||
|             </form> | ||||
|           </li> | ||||
|            | ||||
|           {% if session.get('user_id') %} | ||||
|           <!-- Opcje dla zalogowanego użytkownika --> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link btn btn-alert ms-2 btn-logout" href="{{ url_for('change_password') }}">Zmień hasło</a> | ||||
|           </li> | ||||
| @@ -286,7 +309,6 @@ | ||||
|             <a class="nav-link btn btn-danger ms-2 btn-logout" href="{{ url_for('logout') }}">Wyloguj</a> | ||||
|           </li> | ||||
|           {% else %} | ||||
|           <!-- Opcje logowania i rejestracji dla niezalogowanych --> | ||||
|           <li class="nav-item"> | ||||
|             <a class="nav-link btn btn-success ms-2" href="{{ url_for('login') }}">Zaloguj się</a> | ||||
|           </li> | ||||
| @@ -298,20 +320,29 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|   </nav> | ||||
|    | ||||
|   <div class="container"> | ||||
|     {% with messages = get_flashed_messages() %} | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
|   <!-- Główna zawartość --> | ||||
|   <main class="container mb-5"> | ||||
|  | ||||
|  | ||||
|     {% with messages = get_flashed_messages(with_categories=true) %} | ||||
|       {% if messages %} | ||||
|         <div class="alert alert-info"> | ||||
|           {% for msg in messages %} | ||||
|             <div>{{ msg }}</div> | ||||
|           {% endfor %} | ||||
|         </div> | ||||
|         {% for category, msg in messages %} | ||||
|           {% set bs_cat = bootstrap_alert_category(category) %} | ||||
|           <div class="alert alert-{{ bs_cat }} alert-dismissible fade show" role="alert"> | ||||
|             {{ msg }} | ||||
|             <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button> | ||||
|           </div> | ||||
|         {% endfor %} | ||||
|       {% endif %} | ||||
|     {% endwith %} | ||||
|  | ||||
|  | ||||
|     {% block content %}{% endblock %} | ||||
|   </div> | ||||
|   </main> | ||||
|  | ||||
|   <!-- Modal Test Połączenia --> | ||||
|   <div class="modal fade" id="testConnectionModal" tabindex="-1" aria-labelledby="testConnectionModalLabel" aria-hidden="true"> | ||||
| @@ -322,40 +353,25 @@ | ||||
|           <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button> | ||||
|         </div> | ||||
|         <div class="modal-body" id="testConnectionModalBody"> | ||||
|           <!-- Zawartość zostanie załadowana przez AJAX --> | ||||
|           <!-- Zawartość ładowana przez AJAX --> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|  | ||||
|   <!-- Stopka --> | ||||
|   <footer class="footer py-3 mt-auto"> | ||||
|     <div class="container text-center"> | ||||
|       <span>© 2025 Mateusz Gruszczyński, linuxiarz.pl</span> | ||||
|     </div> | ||||
|   </footer> | ||||
|  | ||||
|   <!-- Bootstrap Bundle JS --> | ||||
|   <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> | ||||
|    | ||||
|   <!-- Dodatkowe skrypty --> | ||||
|   <script> | ||||
|     function ajaxExport(router_id) { | ||||
|       fetch("/router/" + router_id + "/export", { | ||||
|         method: "POST", | ||||
|         headers: {"X-Requested-With": "XMLHttpRequest"} | ||||
|       }) | ||||
|       .then(response => response.json()) | ||||
|       .then(data => { | ||||
|         if(data.status === "success"){ | ||||
|           alert("Eksport wykonany: " + data.message); | ||||
|         } else { | ||||
|           alert("Błąd eksportu: " + data.message); | ||||
|         } | ||||
|       }) | ||||
|       .catch(error => { | ||||
|         console.error("Błąd AJAX:", error); | ||||
|         alert("Wystąpił błąd."); | ||||
|       }); | ||||
|     } | ||||
|   </script> | ||||
|   <script> | ||||
|     // Funkcja do wczytywania modalu testu połączenia | ||||
|     function openTestConnectionModal(routerId) { | ||||
|       fetch('/router/' + routerId + '/test_connection?modal=1') | ||||
|         .then(response => response.text()) | ||||
| @@ -370,6 +386,7 @@ | ||||
|         }); | ||||
|     } | ||||
|   </script> | ||||
|    | ||||
|   {% block scripts %}{% endblock %} | ||||
| </body> | ||||
| </html> | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <div class="row justify-content-center align-items-center" style="min-height: 100vh;"> | ||||
| <div class="container my-5"> | ||||
|   <div class="row justify-content-center"> | ||||
|     <div class="col-md-6"> | ||||
|       <div class="card shadow"> | ||||
|         <div class="card-header text-center"> | ||||
|           <h2>Zmień hasło</h2> | ||||
|       <div class="card border-0 shadow-sm"> | ||||
|         <div class="card-header bg-light text-center"> | ||||
|           <h4 class="mb-0">Zmień hasło</h4> | ||||
|         </div> | ||||
|         <div class="card-body"> | ||||
|           <form method="POST"> | ||||
|   | ||||
| @@ -5,7 +5,7 @@ | ||||
|    | ||||
|   <!-- Wiersz akcji ogólnych --> | ||||
|   <div class="row mb-4"> | ||||
|     <div class="col-md-12 text-center"> | ||||
|     <div class="col text-center"> | ||||
|       <a href="{{ url_for('routers_list') }}" class="btn btn-lg btn-outline-primary"> | ||||
|         <i class="bi bi-hdd-network"></i> Zobacz routery | ||||
|       </a> | ||||
| @@ -53,14 +53,14 @@ | ||||
|     <div class="col-md-6 d-flex justify-content-center"> | ||||
|       <form action="{{ url_for('export_all_routers') }}" method="POST"> | ||||
|         <button type="submit" class="btn btn-lg btn-outline-success"> | ||||
|           <i class="bi bi-arrow-down-circle"></i> Eksport dla wszystkich routerów | ||||
|           <i class="bi bi-arrow-down-circle"></i> Eksport wszystkich routerów | ||||
|         </button> | ||||
|       </form> | ||||
|     </div> | ||||
|     <div class="col-md-6 d-flex justify-content-center"> | ||||
|       <form action="{{ url_for('backup_all_routers') }}" method="POST"> | ||||
|         <button type="submit" class="btn btn-lg btn-outline-secondary"> | ||||
|           <i class="bi bi-cloud-download"></i> Backup binarny dla wszystkich routerów | ||||
|           <i class="bi bi-cloud-download"></i> Backup binarny wszystkich routerów | ||||
|         </button> | ||||
|       </form> | ||||
|     </div> | ||||
| @@ -73,11 +73,11 @@ | ||||
|   {% else %} | ||||
|     {% set success_percent = 0 %} | ||||
|   {% endif %} | ||||
|   <div class="card mb-4 shadow-sm"> | ||||
|   <div class="card mb-4 shadow-sm border-0"> | ||||
|     <div class="card-body"> | ||||
|       <h5 class="card-title">Statystyki operacji</h5> | ||||
|       <p>Udane operacje: {{ success_ops }}, Nieudane operacje: {{ failure_ops }}</p> | ||||
|       <div class="progress"> | ||||
|       <div class="progress mb-2"> | ||||
|         <div class="progress-bar bg-success" role="progressbar" style="width: {{ success_percent }}%;" aria-valuenow="{{ success_percent }}" aria-valuemin="0" aria-valuemax="100"> | ||||
|           {{ success_percent }}% | ||||
|         </div> | ||||
| @@ -89,7 +89,7 @@ | ||||
|   </div> | ||||
|    | ||||
|   <!-- Log operacji --> | ||||
|   <div class="card shadow-sm mb-4"> | ||||
|   <div class="card shadow-sm border-0 mb-4"> | ||||
|     <div class="card-body"> | ||||
|       <h5 class="card-title"> | ||||
|         Log operacji  | ||||
| @@ -114,8 +114,8 @@ | ||||
|     </div> | ||||
|   </div> | ||||
|    | ||||
|   <!-- Dodatkowe statystyki przeniesione na sam dół, pod logami --> | ||||
|   <div class="card shadow-sm"> | ||||
|   <!-- Dodatkowe statystyki --> | ||||
|   <div class="card shadow-sm border-0"> | ||||
|     <div class="card-body"> | ||||
|       <h5 class="card-title">Dodatkowe statystyki</h5> | ||||
|       <div class="row"> | ||||
| @@ -131,6 +131,5 @@ | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
|    | ||||
| </div> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,41 +1,47 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container my-4"> | ||||
|   <h2>Porównanie: {{ backup1.file_path|basename }} vs {{ backup2.file_path|basename }}</h2> | ||||
|   <hr> | ||||
|   <div id="diffContainer"></div> | ||||
|   <a href="{{ url_for('router_details', router_id=backup1.router_id) }}" class="btn btn-secondary mt-3">Powrót</a> | ||||
|   <div class="card border-0 shadow-sm"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0"> | ||||
|         Porównanie: {{ backup1.file_path|basename }} vs {{ backup2.file_path|basename }} | ||||
|       </h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <div id="diffContainer"></div> | ||||
|       <a href="{{ url_for('router_details', router_id=backup1.router_id) }}" class="btn btn-secondary mt-3">Powrót</a> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <!-- Ładujemy nowszą wersję diff2html --> | ||||
| <!-- diff2html resources --> | ||||
| <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/diff2html@3.4.4/bundles/css/diff2html.min.css" /> | ||||
| <script src="https://cdn.jsdelivr.net/npm/diff2html@3.4.4/bundles/js/diff2html.min.js"></script> | ||||
|  | ||||
| <script> | ||||
|   document.addEventListener("DOMContentLoaded", function() { | ||||
|       // Escapowany diff_text z serwera | ||||
|       var diffText = `{{ diff_text|e }}`; | ||||
|       var targetElement = document.getElementById("diffContainer"); | ||||
|       var configuration = { | ||||
|           drawFileList: true, | ||||
|           matching: 'lines', | ||||
|           outputFormat: 'line-by-line' | ||||
|       }; | ||||
|       var diffHtml = Diff2Html.html(diffText, configuration); | ||||
|       targetElement.innerHTML = diffHtml; | ||||
|     var diffText = `{{ diff_text|e }}`; | ||||
|     var targetElement = document.getElementById("diffContainer"); | ||||
|     var configuration = { | ||||
|       drawFileList: true, | ||||
|       matching: 'lines', | ||||
|       outputFormat: 'line-by-line' | ||||
|     }; | ||||
|     var diffHtml = Diff2Html.html(diffText, configuration); | ||||
|     targetElement.innerHTML = diffHtml; | ||||
|  | ||||
|       // Jeśli tryb ciemny jest aktywny, dołącz dodatkowe style dla diff2html | ||||
|       if(document.documentElement.classList.contains('dark-mode')) { | ||||
|           var darkStyle = document.createElement('style'); | ||||
|           darkStyle.textContent = ` | ||||
|             .d2h-wrapper { background-color: #1e1e1e; color: #fff; } | ||||
|             .d2h-file-header { background-color: #2e2e2e; color: #fff; } | ||||
|             .d2h-diff-table { background-color: #1e1e1e; color: #fff; } | ||||
|             .d2h-code-line { background-color: #1e1e1e; color: #fff; } | ||||
|             .d2h-code-line-ctn { color: #fff; } | ||||
|           `; | ||||
|           document.head.appendChild(darkStyle); | ||||
|       } | ||||
|     // Dark mode styl dla diff2html, jeśli potrzebne | ||||
|     if(document.body.classList.contains('dark-mode')) { | ||||
|       var darkStyle = document.createElement('style'); | ||||
|       darkStyle.textContent = ` | ||||
|         .d2h-wrapper { background-color: #1e1e1e; color: #fff; } | ||||
|         .d2h-file-header { background-color: #2e2e2e; color: #fff; } | ||||
|         .d2h-diff-table { background-color: #1e1e1e; color: #fff; } | ||||
|         .d2h-code-line { background-color: #1e1e1e; color: #fff; } | ||||
|         .d2h-code-line-ctn { color: #fff; } | ||||
|       `; | ||||
|       document.head.appendChild(darkStyle); | ||||
|     } | ||||
|   }); | ||||
| </script> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,8 +1,10 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container my-4"> | ||||
|   <h2 class="text-center mb-4">Porównanie backupów (Diff)</h2> | ||||
|   <div class="card shadow-sm"> | ||||
|   <div class="card shadow-sm border-0"> | ||||
|     <div class="card-header bg-light d-flex align-items-center"> | ||||
|       <h4 class="mb-0">Porównanie backupów (Diff)</h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <form action="{{ url_for('diff_selector') }}" method="POST" id="diffForm"> | ||||
|         <div class="row mb-3"> | ||||
| @@ -29,8 +31,10 @@ | ||||
|             </select> | ||||
|           </div> | ||||
|         </div> | ||||
|         <div class="text-center"> | ||||
|           <button type="submit" class="btn btn-primary btn-lg">Porównaj backupy</button> | ||||
|         <div class="text-center mt-4"> | ||||
|           <button type="submit" class="btn btn-primary btn-lg"> | ||||
|             Porównaj backupy | ||||
|           </button> | ||||
|         </div> | ||||
|       </form> | ||||
|     </div> | ||||
| @@ -39,12 +43,12 @@ | ||||
|  | ||||
| <script> | ||||
| document.getElementById("diffForm").addEventListener("submit", function(event) { | ||||
|     var backup1 = document.getElementById("backup1").value; | ||||
|     var backup2 = document.getElementById("backup2").value; | ||||
|     if(backup1 === backup2) { | ||||
|         event.preventDefault(); | ||||
|         alert("Wybierz dwa różne backupy do porównania."); | ||||
|     } | ||||
|   var backup1 = document.getElementById("backup1").value; | ||||
|   var backup2 = document.getElementById("backup2").value; | ||||
|   if (backup1 === backup2) { | ||||
|     event.preventDefault(); | ||||
|     alert("Wybierz dwa różne backupy do porównania."); | ||||
|   } | ||||
| }); | ||||
| </script> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,9 +1,9 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container mt-5"> | ||||
|   <div class="card shadow-sm"> | ||||
|     <div class="card-header"> | ||||
|       <h2 class="mb-0">Edycja urządzenia</h2> | ||||
| <div class="container my-4"> | ||||
|   <div class="card border-0 shadow-sm"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0">Edycja urządzenia</h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <form method="POST"> | ||||
| @@ -25,15 +25,15 @@ | ||||
|         </div> | ||||
|         <div class="mb-3"> | ||||
|           <label for="ssh_key" class="form-label"> | ||||
|             <label for="ssh_password" class="form-label"><b>Klucz prywatny</b></label> | Wklej wraz z <code>-----BEGIN RSA PRIVATE KEY-----</code> i <code>-----END RSA PRIVATE KEY-----</code><br> | ||||
|             Pozostaw puste jeśli ten RouterOS będzie używał <a href="{{ url_for('settings_view') }}">klucza globalnego</a> | ||||
|           </label> | ||||
|           <textarea class="form-control" id="ssh_key" name="ssh_key" rows="4">{{ router.ssh_key }}</textarea> | ||||
|             <b>Klucz prywatny</b> | ||||
|           </label><br> | ||||
|           <small>Wklej wraz z <code>-----BEGIN RSA PRIVATE KEY-----</code> i <code>-----END RSA PRIVATE KEY-----</code>. Jeśli pusty – użyje klucza globalnego.</small> | ||||
|           <textarea class="form-control mt-2" id="ssh_key" name="ssh_key" rows="4">{{ router.ssh_key }}</textarea> | ||||
|         </div> | ||||
|         <div class="mb-3"> | ||||
|           <label for="ssh_password" class="form-label"><b>Hasło SSH</b></label><br> | ||||
|           Jeśli podajesz klucz SSH lub zdefiniowany jest <a href="{{ url_for('settings_view') }}">klucz globalny</a>, to logowanie hasłem jest nieaktywne. | ||||
|           <input type="password" class="form-control" id="ssh_password" name="ssh_password" value="{{ router.ssh_password }}"> | ||||
|           <small>Jeśli jest klucz SSH lub klucz globalny, hasło może być ignorowane.</small> | ||||
|           <input type="password" class="form-control mt-2" id="ssh_password" name="ssh_password" value="{{ router.ssh_password }}"> | ||||
|         </div> | ||||
|         <button type="submit" class="btn btn-success">Zapisz zmiany</button> | ||||
|       </form> | ||||
|   | ||||
| @@ -1,23 +1,23 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block head %} | ||||
| {% endblock %} | ||||
| {% block head %}{% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <div class="row full-height-row justify-content-center align-items-center"> | ||||
| <div class="container my-5"> | ||||
|   <div class="row justify-content-center align-items-center"> | ||||
|     <div class="col-md-6"> | ||||
|       <div class="card shadow"> | ||||
|         <div class="card-header text-center"> | ||||
|           <h2>Zaloguj się</h2> | ||||
|       <div class="card shadow border-0"> | ||||
|         <div class="card-header bg-light text-center"> | ||||
|           <h4 class="mb-0">Zaloguj się</h4> | ||||
|         </div> | ||||
|         <div class="card-body"> | ||||
|           <form action="{{ url_for('login') }}" method="POST"> | ||||
|             <div class="mb-3"> | ||||
|               <label for="username" class="form-label">Nazwa użytkownika</label> | ||||
|               <input type="text" class="form-control" id="username" name="username" placeholder="Wpisz nazwę użytkownika"> | ||||
|               <input type="text" class="form-control" id="username" name="username" placeholder="Wpisz nazwę użytkownika" required> | ||||
|             </div> | ||||
|             <div class="mb-3"> | ||||
|               <label for="password" class="form-label">Hasło</label> | ||||
|               <input type="password" class="form-control" id="password" name="password" placeholder="Wpisz hasło"> | ||||
|               <input type="password" class="form-control" id="password" name="password" placeholder="Wpisz hasło" required> | ||||
|             </div> | ||||
|             <div class="d-grid"> | ||||
|               <button type="submit" class="btn btn-primary">Zaloguj się</button> | ||||
| @@ -25,7 +25,10 @@ | ||||
|           </form> | ||||
|         </div> | ||||
|         <div class="card-footer text-center"> | ||||
|           <a href="{{ url_for('register') }}">Nie masz konta? Zarejestruj się</a> | ||||
|           <small> | ||||
|             Nie masz konta? | ||||
|             <a href="{{ url_for('register') }}">Zarejestruj się</a> | ||||
|           </small> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   | ||||
| @@ -10,12 +10,12 @@ | ||||
| <div class="container my-4"> | ||||
|   <h2 class="text-center mb-4">Historia logów operacji</h2> | ||||
|    | ||||
|   <!-- Formularz usuwania logów starszych od podanej liczby dni --> | ||||
|   <div class="card mb-4 shadow-sm"> | ||||
|   <!-- Karta z usuwaniem logów --> | ||||
|   <div class="card mb-4 shadow-sm border-0"> | ||||
|     <div class="card-body"> | ||||
|       <form action="{{ url_for('delete_old_logs') }}" method="POST" class="row g-2 align-items-center"> | ||||
|         <div class="col-auto"> | ||||
|           <label for="delete_days" class="col-form-label">Usuń logi starsze niż:</label> | ||||
|           <label for="delete_days" class="col-form-label">Usuń logi starsze niż (dni):</label> | ||||
|         </div> | ||||
|         <div class="col-auto"> | ||||
|           <input type="number" class="form-control" id="delete_days" name="delete_days" min="1" placeholder="Liczba dni" required> | ||||
| @@ -28,7 +28,7 @@ | ||||
|   </div> | ||||
|    | ||||
|   <!-- Tabela logów --> | ||||
|   <div class="card shadow-sm mb-4"> | ||||
|   <div class="card shadow-sm border-0 mb-4"> | ||||
|     <div class="card-body"> | ||||
|       <table id="logsTable" class="table table-striped table-bordered"> | ||||
|         <thead class="table-dark"> | ||||
| @@ -50,14 +50,14 @@ | ||||
|   </div> | ||||
|    | ||||
|   <div class="text-center mt-3"> | ||||
|     <a href="{{ url_for('dashboard') }}" class="btn btn-outline-primary">Powrót do Dashboard</a> | ||||
|     <a href="{{ url_for('dashboard') }}" class="btn btn-outline-primary">Powrót do Dashboardu</a> | ||||
|   </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
|  | ||||
| {% block scripts %} | ||||
|   {{ super() }} | ||||
|   <!-- jQuery (jeśli nie jest już dołączone w base.html) --> | ||||
|   <!-- jQuery --> | ||||
|   <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> | ||||
|   <!-- DataTables JS --> | ||||
|   <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script> | ||||
| @@ -66,7 +66,10 @@ | ||||
|     $(document).ready(function() { | ||||
|       $('#logsTable').DataTable({ | ||||
|         responsive: true, | ||||
|         order: [[0, 'desc']] | ||||
|         order: [[0, 'desc']], | ||||
|         language: { | ||||
|           url: '//cdn.datatables.net/plug-ins/1.13.4/i18n/pl.json' | ||||
|         } | ||||
|       }); | ||||
|     }); | ||||
|   </script> | ||||
|   | ||||
| @@ -1,23 +1,23 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block head %} | ||||
| {% endblock %} | ||||
| {% block head %}{% endblock %} | ||||
|  | ||||
| {% block content %} | ||||
| <div class="container"> | ||||
|   <div class="row full-height-row justify-content-center align-items-center"> | ||||
| <div class="container my-5"> | ||||
|   <div class="row justify-content-center align-items-center"> | ||||
|     <div class="col-md-6"> | ||||
|       <div class="card shadow"> | ||||
|         <div class="card-header text-center"> | ||||
|           <h2>Rejestracja</h2> | ||||
|       <div class="card shadow border-0"> | ||||
|         <div class="card-header bg-light text-center"> | ||||
|           <h4 class="mb-0">Rejestracja</h4> | ||||
|         </div> | ||||
|         <div class="card-body"> | ||||
|           <form action="{{ url_for('register') }}" method="POST"> | ||||
|             <div class="mb-3"> | ||||
|               <label for="username" class="form-label">Nazwa użytkownika</label> | ||||
|               <input type="text" class="form-control" id="username" name="username" placeholder="Wpisz nazwę użytkownika"> | ||||
|               <input type="text" class="form-control" id="username" name="username" placeholder="Wpisz nazwę użytkownika" required> | ||||
|             </div> | ||||
|             <div class="mb-3"> | ||||
|               <label for="password" class="form-label">Hasło</label> | ||||
|               <input type="password" class="form-control" id="password" name="password" placeholder="Wpisz hasło"> | ||||
|               <input type="password" class="form-control" id="password" name="password" placeholder="Wpisz hasło" required> | ||||
|             </div> | ||||
|             <div class="d-grid"> | ||||
|               <button type="submit" class="btn btn-primary">Zarejestruj się</button> | ||||
| @@ -25,7 +25,10 @@ | ||||
|           </form> | ||||
|         </div> | ||||
|         <div class="card-footer text-center"> | ||||
|           <a href="{{ url_for('login') }}">Masz już konto? Zaloguj się</a> | ||||
|           <small> | ||||
|             Masz już konto? | ||||
|             <a href="{{ url_for('login') }}">Zaloguj się</a> | ||||
|           </small> | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|   | ||||
| @@ -1,17 +1,17 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="text-end mb-3"> | ||||
|   {% if current_view == 'v1' %} | ||||
|     <a href="{{ url_for('router_details', router_id=router.id, view='v2') }}" class="btn btn-outline-secondary">Przełącz na widok v2</a> | ||||
|   {% else %} | ||||
|     <a href="{{ url_for('router_details', router_id=router.id, view='v1') }}" class="btn btn-outline-secondary">Przełącz na widok v1</a> | ||||
|   {% endif %} | ||||
| </div> | ||||
| <div class="container my-4"> | ||||
|   <!-- Informacje o routerze --> | ||||
|   <div class="card shadow-sm mb-4"> | ||||
|     <div class="card-header"> | ||||
|       <h2 class="mb-0">Router: {{ router.name }}</h2> | ||||
|   <div class="mb-3 text-end"> | ||||
|     {% if current_view == 'v1' %} | ||||
|       <a href="{{ url_for('router_details', router_id=router.id, view='v2') }}" class="btn btn-outline-secondary">Przełącz na widok v2</a> | ||||
|     {% else %} | ||||
|       <a href="{{ url_for('router_details', router_id=router.id, view='v1') }}" class="btn btn-outline-secondary">Przełącz na widok v1</a> | ||||
|     {% endif %} | ||||
|   </div> | ||||
|    | ||||
|   <div class="card border-0 shadow-sm mb-4"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0">Router: {{ router.name }}</h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <p> | ||||
| @@ -20,7 +20,6 @@ | ||||
|         <strong>SSH User:</strong> {{ router.ssh_user }} | ||||
|       </p> | ||||
|       <div class="d-flex flex-wrap gap-2"> | ||||
|         <!-- Mniejsze przyciski górne --> | ||||
|         <form action="{{ url_for('router_export', router_id=router.id) }}" method="POST" class="d-inline"> | ||||
|           <button type="submit" class="btn btn-primary btn-sm">Wykonaj /export</button> | ||||
|         </form> | ||||
| @@ -35,16 +34,21 @@ | ||||
|   <!-- Zakładki z backupami --> | ||||
|   <ul class="nav nav-tabs" id="routerTab" role="tablist"> | ||||
|     <li class="nav-item" role="presentation"> | ||||
|       <button class="nav-link active" id="export-tab" data-bs-toggle="tab" data-bs-target="#export" type="button" role="tab" aria-controls="export" aria-selected="true">Pliki /export</button> | ||||
|       <button class="nav-link active" id="export-tab" data-bs-toggle="tab" data-bs-target="#export" type="button" role="tab" aria-controls="export" aria-selected="true"> | ||||
|         Pliki /export | ||||
|       </button> | ||||
|     </li> | ||||
|     <li class="nav-item" role="presentation"> | ||||
|       <button class="nav-link" id="binary-tab" data-bs-toggle="tab" data-bs-target="#binary" type="button" role="tab" aria-controls="binary" aria-selected="false">Pliki binarne</button> | ||||
|       <button class="nav-link" id="binary-tab" data-bs-toggle="tab" data-bs-target="#binary" type="button" role="tab" aria-controls="binary" aria-selected="false"> | ||||
|         Pliki binarne | ||||
|       </button> | ||||
|     </li> | ||||
|   </ul> | ||||
|  | ||||
|   <div class="tab-content" id="routerTabContent"> | ||||
|     <!-- Zakładka /export --> | ||||
|     <div class="tab-pane fade show active" id="export" role="tabpanel" aria-labelledby="export-tab"> | ||||
|       <div class="card mt-3 shadow-sm"> | ||||
|       <div class="card mt-3 shadow-sm border-0"> | ||||
|         <div class="card-body"> | ||||
|           {% if export_backups %} | ||||
|           <!-- Formularz masowych akcji dla eksportów --> | ||||
| @@ -55,68 +59,68 @@ | ||||
|               </button> | ||||
|             </div> | ||||
|           </form> | ||||
|           <!-- Tabela z eksportami --> | ||||
|            | ||||
|           <div class="table-responsive"> | ||||
|           <table class="table table-bordered table-striped"> | ||||
|             <thead class="table-dark"> | ||||
|               <tr> | ||||
|                 <th style="width: 3%;"><input type="checkbox" id="select_all_export"></th> | ||||
|                 <th>Nazwa pliku</th> | ||||
|                 <th>Rozmiar</th> | ||||
|                 <th>Data</th> | ||||
|                 <th>Diff</th> | ||||
|                 <th>Pobierz</th> | ||||
|                 <th>Podgląd</th> | ||||
|                 <th>Wyślij mailem</th> | ||||
|                 <th>Usuń</th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|               {% for b in export_backups %} | ||||
|               <tr> | ||||
|                 <td> | ||||
|                   <input type="checkbox" name="backup_id" value="{{ b.id }}" form="export_mass_actions_form"> | ||||
|                 </td> | ||||
|                 <td>{{ b.file_path|basename }}</td> | ||||
|                 <td>{{ b.file_path|filesize }}</td> | ||||
|                 <td>{{ b.created_at.strftime("%Y-%m-%d %H:%M:%S") }}</td> | ||||
|                 <td> | ||||
|                   {% if loop.index0 > 0 %} | ||||
|                     <a href="{{ url_for('diff_view', backup_id1=b.id, backup_id2=export_backups[0].id) }}" class="btn btn-sm btn-info">Diff</a> | ||||
|                   {% else %} | ||||
|                     <small>Brak nowszego</small> | ||||
|                   {% endif %} | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <a href="{{ url_for('download_file', filename=b.file_path|basename) }}" class="btn btn-info btn-sm" title="Pobierz"> | ||||
|                     <i class="bi bi-download"></i> | ||||
|                   </a> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <a href="{{ url_for('view_export', backup_id=b.id) }}" class="btn btn-outline-primary btn-sm" title="Podgląd"> | ||||
|                     <i class="bi bi-eye"></i> | ||||
|                   </a> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <form action="{{ url_for('send_by_email', backup_id=b.id) }}" method="POST" class="d-inline"> | ||||
|                     <input type="hidden" name="next" value="{{ url_for('router_details', router_id=router.id) }}"> | ||||
|                     <button type="submit" class="btn btn-primary btn-sm" title="Wyślij mailem"> | ||||
|                       <i class="bi bi-envelope"></i> | ||||
|                     </button> | ||||
|                   </form> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <form action="{{ url_for('delete_backup', backup_id=b.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Na pewno usunąć backup?');"> | ||||
|                     <input type="hidden" name="next" value="{{ url_for('router_details', router_id=router.id) }}"> | ||||
|                     <button type="submit" class="btn btn-danger btn-sm" title="Usuń"> | ||||
|                       <i class="bi bi-trash"></i> | ||||
|                     </button> | ||||
|                   </form> | ||||
|                 </td> | ||||
|               </tr> | ||||
|               {% endfor %} | ||||
|             </tbody> | ||||
|           </table> | ||||
|             <table class="table table-bordered table-striped align-middle"> | ||||
|               <thead class="table-dark"> | ||||
|                 <tr> | ||||
|                   <th style="width: 3%;"><input type="checkbox" id="select_all_export"></th> | ||||
|                   <th>Nazwa pliku</th> | ||||
|                   <th>Rozmiar</th> | ||||
|                   <th>Data</th> | ||||
|                   <th>Diff</th> | ||||
|                   <th>Pobierz</th> | ||||
|                   <th>Podgląd</th> | ||||
|                   <th>Wyślij mailem</th> | ||||
|                   <th>Usuń</th> | ||||
|                 </tr> | ||||
|               </thead> | ||||
|               <tbody> | ||||
|                 {% for b in export_backups %} | ||||
|                 <tr> | ||||
|                   <td> | ||||
|                     <input type="checkbox" name="backup_id" value="{{ b.id }}" form="export_mass_actions_form"> | ||||
|                   </td> | ||||
|                   <td>{{ b.file_path|basename }}</td> | ||||
|                   <td>{{ b.file_path|filesize }}</td> | ||||
|                   <td>{{ b.created_at.strftime("%Y-%m-%d %H:%M:%S") }}</td> | ||||
|                   <td> | ||||
|                     {% if loop.index0 > 0 %} | ||||
|                       <a href="{{ url_for('diff_view', backup_id1=b.id, backup_id2=export_backups[0].id) }}" class="btn btn-sm btn-info">Diff</a> | ||||
|                     {% else %} | ||||
|                       <small>Brak nowszego</small> | ||||
|                     {% endif %} | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <a href="{{ url_for('download_file', filename=b.file_path|basename) }}" class="btn btn-info btn-sm" title="Pobierz"> | ||||
|                       <i class="bi bi-download"></i> | ||||
|                     </a> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <a href="{{ url_for('view_export', backup_id=b.id) }}" class="btn btn-outline-primary btn-sm" title="Podgląd"> | ||||
|                       <i class="bi bi-eye"></i> | ||||
|                     </a> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <form action="{{ url_for('send_by_email', backup_id=b.id) }}" method="POST" class="d-inline"> | ||||
|                       <input type="hidden" name="next" value="{{ url_for('router_details', router_id=router.id) }}"> | ||||
|                       <button type="submit" class="btn btn-primary btn-sm" title="Wyślij mailem"> | ||||
|                         <i class="bi bi-envelope"></i> | ||||
|                       </button> | ||||
|                     </form> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <form action="{{ url_for('delete_backup', backup_id=b.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Na pewno usunąć backup?');"> | ||||
|                       <input type="hidden" name="next" value="{{ url_for('router_details', router_id=router.id) }}"> | ||||
|                       <button type="submit" class="btn btn-danger btn-sm" title="Usuń"> | ||||
|                         <i class="bi bi-trash"></i> | ||||
|                       </button> | ||||
|                     </form> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|                 {% endfor %} | ||||
|               </tbody> | ||||
|             </table> | ||||
|           </div> | ||||
|           {% else %} | ||||
|           <p class="text-muted">Brak plików /export.</p> | ||||
| @@ -124,9 +128,10 @@ | ||||
|         </div> | ||||
|       </div> | ||||
|     </div> | ||||
|      | ||||
|     <!-- Zakładka backupów binarnych --> | ||||
|     <div class="tab-pane fade" id="binary" role="tabpanel" aria-labelledby="binary-tab"> | ||||
|       <div class="card mt-3 shadow-sm"> | ||||
|       <div class="card mt-3 shadow-sm border-0"> | ||||
|         <div class="card-body"> | ||||
|           {% if binary_backups %} | ||||
|           <!-- Formularz masowych akcji dla backupów binarnych --> | ||||
| @@ -137,64 +142,63 @@ | ||||
|               </button> | ||||
|             </div> | ||||
|           </form> | ||||
|           <!-- Tabela z backupami binarnymi --> | ||||
|            | ||||
|           <div class="table-responsive"> | ||||
|           <table class="table table-bordered table-striped"> | ||||
|             <thead class="table-dark"> | ||||
|               <tr> | ||||
|                 <th style="width: 3%;"><input type="checkbox" id="select_all_binary"></th> | ||||
|                 <th>Nazwa pliku</th> | ||||
|                 <th>Rozmiar</th> | ||||
|                 <th>Data</th> | ||||
|                 <th>Pobierz</th> | ||||
|                 <th>Wgraj do routera</th> | ||||
|                 <th>Wyślij mailem</th> | ||||
|                 <th>Usuń</th> | ||||
|               </tr> | ||||
|             </thead> | ||||
|             <tbody> | ||||
|               {% for b in binary_backups %} | ||||
|               <tr> | ||||
|                 <td> | ||||
|                   <input type="checkbox" name="backup_id" value="{{ b.id }}" form="binary_mass_actions_form"> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <!-- Dodaj tooltip z sumą kontrolną --> | ||||
|                   <span data-bs-toggle="tooltip" title="Checksum: {{ b.checksum }}">{{ b.file_path|basename }}</span> | ||||
|                 </td> | ||||
|                 <td>{{ b.file_path|filesize }}</td> | ||||
|                 <td>{{ b.created_at.strftime("%Y-%m-%d %H:%M:%S") }}</td> | ||||
|                 <td> | ||||
|                   <a href="{{ url_for('download_file', filename=b.file_path|basename) }}" class="btn btn-info btn-sm" title="Pobierz"> | ||||
|                     <i class="bi bi-download"></i> | ||||
|                   </a> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <form action="{{ url_for('upload_backup', router_id=router.id, backup_id=b.id) }}" method="POST" class="d-inline"> | ||||
|                     <button type="submit" class="btn btn-secondary btn-sm" title="Wgraj do routera"> | ||||
|                       <i class="bi bi-upload"></i> | ||||
|                     </button> | ||||
|                   </form> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <form action="{{ url_for('send_by_email', backup_id=b.id) }}" method="POST" class="d-inline"> | ||||
|                     <button type="submit" class="btn btn-primary btn-sm" title="Wyślij mailem"> | ||||
|                       <i class="bi bi-envelope"></i> | ||||
|                     </button> | ||||
|                   </form> | ||||
|                 </td> | ||||
|                 <td> | ||||
|                   <form action="{{ url_for('delete_backup', backup_id=b.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Na pewno usunąć backup?');"> | ||||
|                     <input type="hidden" name="next" value="{{ url_for('router_details', router_id=router.id) }}"> | ||||
|                     <button type="submit" class="btn btn-danger btn-sm" title="Usuń"> | ||||
|                       <i class="bi bi-trash"></i> | ||||
|                     </button> | ||||
|                   </form> | ||||
|                 </td> | ||||
|               </tr> | ||||
|               {% endfor %} | ||||
|             </tbody> | ||||
|           </table> | ||||
|             <table class="table table-bordered table-striped align-middle"> | ||||
|               <thead class="table-dark"> | ||||
|                 <tr> | ||||
|                   <th style="width: 3%;"><input type="checkbox" id="select_all_binary"></th> | ||||
|                   <th>Nazwa pliku</th> | ||||
|                   <th>Rozmiar</th> | ||||
|                   <th>Data</th> | ||||
|                   <th>Pobierz</th> | ||||
|                   <th>Wgraj do routera</th> | ||||
|                   <th>Wyślij mailem</th> | ||||
|                   <th>Usuń</th> | ||||
|                 </tr> | ||||
|               </thead> | ||||
|               <tbody> | ||||
|                 {% for b in binary_backups %} | ||||
|                 <tr> | ||||
|                   <td> | ||||
|                     <input type="checkbox" name="backup_id" value="{{ b.id }}" form="binary_mass_actions_form"> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <span data-bs-toggle="tooltip" title="Checksum: {{ b.checksum }}">{{ b.file_path|basename }}</span> | ||||
|                   </td> | ||||
|                   <td>{{ b.file_path|filesize }}</td> | ||||
|                   <td>{{ b.created_at.strftime("%Y-%m-%d %H:%M:%S") }}</td> | ||||
|                   <td> | ||||
|                     <a href="{{ url_for('download_file', filename=b.file_path|basename) }}" class="btn btn-info btn-sm" title="Pobierz"> | ||||
|                       <i class="bi bi-download"></i> | ||||
|                     </a> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <form action="{{ url_for('upload_backup', router_id=router.id, backup_id=b.id) }}" method="POST" class="d-inline"> | ||||
|                       <button type="submit" class="btn btn-secondary btn-sm" title="Wgraj do routera"> | ||||
|                         <i class="bi bi-upload"></i> | ||||
|                       </button> | ||||
|                     </form> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <form action="{{ url_for('send_by_email', backup_id=b.id) }}" method="POST" class="d-inline"> | ||||
|                       <button type="submit" class="btn btn-primary btn-sm" title="Wyślij mailem"> | ||||
|                         <i class="bi bi-envelope"></i> | ||||
|                       </button> | ||||
|                     </form> | ||||
|                   </td> | ||||
|                   <td> | ||||
|                     <form action="{{ url_for('delete_backup', backup_id=b.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Na pewno usunąć backup?');"> | ||||
|                       <input type="hidden" name="next" value="{{ url_for('router_details', router_id=router.id) }}"> | ||||
|                       <button type="submit" class="btn btn-danger btn-sm" title="Usuń"> | ||||
|                         <i class="bi bi-trash"></i> | ||||
|                       </button> | ||||
|                     </form> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|                 {% endfor %} | ||||
|               </tbody> | ||||
|             </table> | ||||
|           </div> | ||||
|           {% else %} | ||||
|           <p class="text-muted">Brak plików binarnych.</p> | ||||
| @@ -216,7 +220,7 @@ document.getElementById('select_all_binary').addEventListener('change', function | ||||
|   checkboxes.forEach(cb => cb.checked = e.target.checked); | ||||
| }); | ||||
|  | ||||
| // Inicjalizacja zakładek Bootstrap (jeśli nie są już inicjowane globalnie) | ||||
| // Inicjalizacja zakładek Bootstrap | ||||
| var triggerTabList = [].slice.call(document.querySelectorAll('#routerTab button')); | ||||
| triggerTabList.forEach(function (triggerEl) { | ||||
|   var tabTrigger = new bootstrap.Tab(triggerEl); | ||||
| @@ -226,7 +230,7 @@ triggerTabList.forEach(function (triggerEl) { | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| // Inicjalizacja tooltipów Bootstrap | ||||
| // Inicjalizacja tooltipów | ||||
| var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')); | ||||
| tooltipTriggerList.forEach(function (tooltipTriggerEl) { | ||||
|   new bootstrap.Tooltip(tooltipTriggerEl); | ||||
|   | ||||
| @@ -1,63 +1,67 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container my-4"> | ||||
|   <div class="d-flex justify-content-between align-items-center mb-3"> | ||||
|     <h2>Lista urządzeń</h2> | ||||
|     <a href="{{ url_for('add_router') }}" class="btn btn-success"> | ||||
|       <i class="bi bi-plus-lg"></i> Dodaj nowe urządzenie | ||||
|     </a> | ||||
|   </div> | ||||
|   <div class="table-responsive-sm"> | ||||
|     <table class="table table-striped table-hover"> | ||||
|       <thead class="table-primary"> | ||||
|         <tr> | ||||
|           <th>Nazwa</th> | ||||
|           <th>Host</th> | ||||
|           <th>Port</th> | ||||
|           <th>Exporty</th> | ||||
|           <th>Backupy binarne</th> | ||||
|           <th>Test Połączenia</th> | ||||
|           <th>Akcje</th> | ||||
|         </tr> | ||||
|       </thead> | ||||
|       <tbody> | ||||
|         {% for router in routers %} | ||||
|         <tr> | ||||
|           <td>{{ router.name }}</td> | ||||
|           <td>{{ router.host }}</td> | ||||
|           <td>{{ router.port }}</td> | ||||
|           <td> | ||||
|             <span class="badge bg-success"> | ||||
|               {{ router.backups|selectattr("backup_type", "equalto", "export")|list|length }} | ||||
|             </span> | ||||
|           </td> | ||||
|           <td> | ||||
|             <span class="badge bg-info"> | ||||
|               {{ router.backups|selectattr("backup_type", "equalto", "binary")|list|length }} | ||||
|             </span> | ||||
|           </td> | ||||
|           <td> | ||||
|             <button type="button" class="btn btn-sm btn-info" onclick="openTestConnectionModal({{ router.id }})"> | ||||
|               <i class="bi bi-wifi"></i> Test | ||||
|             </button> | ||||
|           </td> | ||||
|           <td> | ||||
|             <a href="{{ url_for('router_details', router_id=router.id) }}" class="btn btn-sm btn-primary"> | ||||
|               <i class="bi bi-eye"></i> Szczegóły | ||||
|             </a> | ||||
|             <a href="{{ url_for('edit_router', router_id=router.id) }}" class="btn btn-sm btn-warning"> | ||||
|               <i class="bi bi-pencil"></i> Edytuj | ||||
|             </a> | ||||
|             <form action="{{ url_for('delete_router', router_id=router.id) }}" method="POST" class="d-inline"> | ||||
|               <button type="submit" class="btn btn-sm btn-danger" onclick="return confirm('Na pewno usunąć urządzenie?');"> | ||||
|                 <i class="bi bi-trash"></i> Usuń | ||||
|               </button> | ||||
|             </form> | ||||
|           </td> | ||||
|         </tr> | ||||
|         {% endfor %} | ||||
|       </tbody> | ||||
|     </table> | ||||
|   <div class="card border-0 shadow-sm"> | ||||
|     <div class="card-header bg-light d-flex justify-content-between align-items-center"> | ||||
|       <h4 class="mb-0">Lista urządzeń</h4> | ||||
|       <a href="{{ url_for('add_router') }}" class="btn btn-success"> | ||||
|         <i class="bi bi-plus-lg"></i> Dodaj nowe urządzenie | ||||
|       </a> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <div class="table-responsive-sm"> | ||||
|         <table class="table table-striped table-hover align-middle"> | ||||
|           <thead class="table-primary"> | ||||
|             <tr> | ||||
|               <th>Nazwa</th> | ||||
|               <th>Host</th> | ||||
|               <th>Port</th> | ||||
|               <th>Exporty</th> | ||||
|               <th>Backupy binarne</th> | ||||
|               <th>Test Połączenia</th> | ||||
|               <th>Akcje</th> | ||||
|             </tr> | ||||
|           </thead> | ||||
|           <tbody> | ||||
|             {% for router in routers %} | ||||
|             <tr> | ||||
|               <td>{{ router.name }}</td> | ||||
|               <td>{{ router.host }}</td> | ||||
|               <td>{{ router.port }}</td> | ||||
|               <td> | ||||
|                 <span class="badge bg-success"> | ||||
|                   {{ router.backups|selectattr("backup_type", "equalto", "export")|list|length }} | ||||
|                 </span> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <span class="badge bg-info"> | ||||
|                   {{ router.backups|selectattr("backup_type", "equalto", "binary")|list|length }} | ||||
|                 </span> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <button type="button" class="btn btn-sm btn-info" onclick="openTestConnectionModal({{ router.id }})"> | ||||
|                   <i class="bi bi-wifi"></i> Test | ||||
|                 </button> | ||||
|               </td> | ||||
|               <td> | ||||
|                 <a href="{{ url_for('router_details', router_id=router.id) }}" class="btn btn-sm btn-primary"> | ||||
|                   <i class="bi bi-eye"></i> Szczegóły | ||||
|                 </a> | ||||
|                 <a href="{{ url_for('edit_router', router_id=router.id) }}" class="btn btn-sm btn-warning"> | ||||
|                   <i class="bi bi-pencil"></i> Edytuj | ||||
|                 </a> | ||||
|                 <form action="{{ url_for('delete_router', router_id=router.id) }}" method="POST" class="d-inline" onsubmit="return confirm('Na pewno usunąć urządzenie?');"> | ||||
|                   <button type="submit" class="btn btn-sm btn-danger"> | ||||
|                     <i class="bi bi-trash"></i> Usuń | ||||
|                   </button> | ||||
|                 </form> | ||||
|               </td> | ||||
|             </tr> | ||||
|             {% endfor %} | ||||
|           </tbody> | ||||
|         </table> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
| {% endblock %} | ||||
|   | ||||
| @@ -1,15 +1,16 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container my-5"> | ||||
|   <div class="card shadow-sm"> | ||||
|     <div class="card-header"> | ||||
|       <h2 class="mb-0">Ustawienia globalne</h2> | ||||
| <div class="container my-4"> | ||||
|   <div class="card border-0 shadow-sm"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0">Ustawienia globalne</h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <form method="POST"> | ||||
|          | ||||
|         <!-- Sekcja Pushover --> | ||||
|         <div class="mb-4"> | ||||
|           <h4 class="mb-3">Powiadomienia - Pushover</h4> | ||||
|           <h5 class="mb-3">Powiadomienia - Pushover</h5> | ||||
|           <div class="mb-3"> | ||||
|             <label for="pushover_token" class="form-label">Pushover Token</label> | ||||
|             <input type="text" class="form-control" id="pushover_token" name="pushover_token" value="{{ settings.pushover_token }}"> | ||||
| @@ -24,10 +25,11 @@ | ||||
|           </div> | ||||
|         </div> | ||||
|         <hr> | ||||
|  | ||||
|         <!-- Sekcja SMTP --> | ||||
|         <div class="mb-4"> | ||||
|           <h4 class="mb-3">Powiadomienia - SMTP (e-mail)</h4> | ||||
|           <div class="mb-3 form-check"> | ||||
|           <h5 class="mb-3">Powiadomienia - SMTP (e-mail)</h5> | ||||
|           <div class="form-check mb-3"> | ||||
|             <input type="checkbox" class="form-check-input" id="smtp_notifications_enabled" name="smtp_notifications_enabled" {% if settings.smtp_notifications_enabled %}checked{% endif %}> | ||||
|             <label class="form-check-label" for="smtp_notifications_enabled">Włącz powiadomienia SMTP</label> | ||||
|           </div> | ||||
| @@ -47,27 +49,27 @@ | ||||
|             <label for="smtp_password" class="form-label">SMTP Hasło</label> | ||||
|             <input type="password" class="form-control" id="smtp_password" name="smtp_password" value="{{ settings.smtp_password }}"> | ||||
|           </div> | ||||
|           <!-- Nowe pole: docelowy adres e-mail --> | ||||
|           <div class="mb-3"> | ||||
|             <label for="recipient_email" class="form-label">Adres e-mail docelowy</label> | ||||
|             <input type="email" class="form-control" id="recipient_email" name="recipient_email" value="{{ settings.recipient_email }}"> | ||||
|           </div> | ||||
|         </div> | ||||
|         <hr> | ||||
|  | ||||
|         <!-- Sekcja globalnego klucza SSH --> | ||||
|         <div class="mb-4"> | ||||
|           <h4 class="mb-3">Globalny klucz SSH</h4> | ||||
|           <div class="mb-3"> | ||||
|             <label for="global_ssh_key" class="form-label"> | ||||
|               Wklej wraz z <code>-----BEGIN RSA PRIVATE KEY-----</code> i <code>-----END RSA PRIVATE KEY-----</code> | ||||
|             </label> | ||||
|             <textarea class="form-control" id="global_ssh_key" name="global_ssh_key" rows="4">{{ settings.global_ssh_key }}</textarea> | ||||
|           </div> | ||||
|           <h5 class="mb-3">Globalny klucz SSH</h5> | ||||
|           <label for="global_ssh_key" class="form-label"> | ||||
|             Wklej wraz z <code>-----BEGIN RSA PRIVATE KEY-----</code> i <code>-----END RSA PRIVATE KEY-----</code> | ||||
|           </label> | ||||
|           <textarea class="form-control" id="global_ssh_key" name="global_ssh_key" rows="4">{{ settings.global_ssh_key }}</textarea> | ||||
|         </div> | ||||
|          | ||||
|         <div class="d-grid"> | ||||
|           <button type="submit" class="btn btn-primary btn-lg">Zapisz ustawienia</button> | ||||
|         </div> | ||||
|       </form> | ||||
|  | ||||
|       <!-- Przycisk do testowania powiadomień --> | ||||
|       <div class="mt-4 text-center"> | ||||
|         <form method="POST" action="{{ url_for('test_email') }}" class="d-inline"> | ||||
| @@ -78,8 +80,12 @@ | ||||
|         </form> | ||||
|       </div> | ||||
|     </div> | ||||
|      | ||||
|     <div class="card-footer text-center"> | ||||
|       <p>Ustawienia dotyczące backupu oraz harmonogramu CRON znajdują się na <a href="{{ url_for('advanced_schedule') }}">zaawansowanych ustawieniach harmonogramu</a>.</p> | ||||
|       <p class="mb-0"> | ||||
|         Ustawienia harmonogramu i retencji:  | ||||
|         <a href="{{ url_for('advanced_schedule') }}">Zaawansowane ustawienia</a> | ||||
|       </p> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|   | ||||
| @@ -1,11 +1,19 @@ | ||||
| {% extends "base.html" %} | ||||
| {% block content %} | ||||
| <div class="container my-4"> | ||||
|   <h2>Podgląd eksportu: {{ backup.file_path|basename }}</h2> | ||||
|   <hr> | ||||
|   <textarea id="exportEditor" readonly>{{ content|e }}</textarea> | ||||
|   <br> | ||||
|   <a href="{{ next_url }}" class="btn btn-secondary">Powrót</a> | ||||
|   <div class="card border-0 shadow"> | ||||
|     <div class="card-header bg-light"> | ||||
|       <h4 class="mb-0"> | ||||
|         Podgląd eksportu: {{ backup.file_path|basename }} | ||||
|       </h4> | ||||
|     </div> | ||||
|     <div class="card-body"> | ||||
|       <textarea id="exportEditor" readonly>{{ content|e }}</textarea> | ||||
|       <div class="mt-3"> | ||||
|         <a href="{{ next_url }}" class="btn btn-secondary">Powrót</a> | ||||
|       </div> | ||||
|     </div> | ||||
|   </div> | ||||
| </div> | ||||
|  | ||||
| <!-- CodeMirror CSS --> | ||||
| @@ -28,8 +36,7 @@ | ||||
|       lineNumbers: true, | ||||
|       readOnly: true | ||||
|     }); | ||||
|     // Dopasowanie rozmiaru edytora do zawartości | ||||
|     editor.setSize("100%", "588px"); | ||||
|     editor.setSize("100%", "600px"); | ||||
|   }); | ||||
| </script> | ||||
| {% endblock %} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Mateusz Gruszczyński
					Mateusz Gruszczyński