diff --git a/app.py b/app.py index 50f3fa5..0bab288 100644 --- a/app.py +++ b/app.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, request, redirect, url_for, flash, session, Response +from flask import Flask, render_template, request, redirect, url_for, flash, session, Response, jsonify from flask_sqlalchemy import SQLAlchemy from werkzeug.security import generate_password_hash, check_password_hash import os, paramiko, threading, time, io, tempfile, csv @@ -159,7 +159,11 @@ def ensure_local_defaults(content, user_id): return final_content def format_host(host): + if not host: + return "Unknown Host" + resolved_name = None + # Priorytet dla Linux Daemon if host.use_daemon and host.type == 'linux' and host.daemon_url: resolved_name = host.resolved_daemon or host.hostname @@ -175,12 +179,8 @@ def format_host(host): except (socket.herror, socket.gaierror): pass # Jeśli nie można rozwiązać, pozostaw IP - return f"{resolved_name} ({host.raw_ip})" + return f"{resolved_name} ({host.raw_ip})" if resolved_name else f"Unknown ({host.raw_ip})" - # Automatyczne dodanie wartości systemowych - variables["hostname"] = socket.gethostname() # Nazwa hosta - variables["resolved_hostname"] = socket.getfqdn() # Pełna nazwa hosta - return variables def wrap_content_with_comments(content): @@ -679,48 +679,54 @@ def clear_server(): def clear_single_server(host_id): if 'user_id' not in session: return redirect(url_for('login')) + host = db.session.get(Host, host_id) if not host or host.user_id != session['user_id']: flash('Host not found or unauthorized', 'danger') - return redirect(url_for('clear_servers')) + return redirect(url_for('clear_server')) + + default_content = ensure_local_defaults("", session['user_id']) - default_content = ensure_local_defaults("") try: + host_name = format_host(host) + if host.use_daemon and host.type == 'linux': import requests url = host.daemon_url.rstrip('/') + '/hosts' headers = {"Authorization": host.daemon_token} - # Zakładamy, że demon potrafi zastąpić /etc/hosts treścią "default_content" - resp = requests.post(url, json={"hosts": default_content}, - headers=headers, verify=False, timeout=10) + resp = requests.post(url, json={"hosts": default_content}, headers=headers, verify=False, timeout=10) if resp.status_code != 200: raise Exception(f"Daemon update error: {resp.status_code} - {resp.text}") elif host.type == 'mikrotik': clear_mikrotik(host) else: - # standard linux clear_linux(host, default_content) - flash(f'Cleared host: {host.hostname}', 'success') + flash(f'Cleared host: {host_name}', 'success') except Exception as e: - flash(f'Error clearing host {host.hostname}: {str(e)}', 'danger') + flash(f'Error clearing host {host_name}: {str(e)}', 'danger') + + return redirect(url_for('clear_server')) - return redirect(url_for('clear_all_server')) @app.route('/clear-all-server', methods=['GET', 'POST']) def clear_all_server(): if 'user_id' not in session: return redirect(url_for('login')) + hosts = Host.query.filter_by(user_id=session['user_id']).all() if request.method == 'POST': linux_clear = request.form.get('linux') mikrotik_clear = request.form.get('mikrotik') - default_content = ensure_local_defaults("") + default_content = ensure_local_defaults("", session['user_id']) for h in hosts: try: + host_name = format_host(h) if h else "Unknown Host" + print(f"DEBUG: Czyszczenie hosta {host_name}") + if h.type == 'linux' and linux_clear: if h.use_daemon: import requests @@ -732,18 +738,18 @@ def clear_all_server(): raise Exception(f"Daemon update error: {resp.status_code} - {resp.text}") else: clear_linux(h, default_content) - flash(f'Cleared Linux host: {h.hostname}', 'success') + flash(f'Cleared Linux host: {host_name}', 'success') elif h.type == 'mikrotik' and mikrotik_clear: clear_mikrotik(h) - flash(f'Cleared Mikrotik host: {h.hostname}', 'success') + flash(f'Cleared Mikrotik host: {host_name}', 'success') except Exception as e: - flash(f'Error clearing host {h.hostname}: {str(e)}', 'danger') + flash(f'Error clearing host {host_name}: {str(e)}', 'danger') return redirect(url_for('clear_all_server')) - return render_template('clear_servers.html', hosts=hosts) + return render_template('clear_servers.html', hosts=hosts, format_host=format_host) # ------------------- # ZARZĄDZANIE PLIKAMI HOSTS (WIELOKROTNE PLIKI) @@ -1246,35 +1252,24 @@ def deploy_user(user_id): if not h.auto_deploy_enabled: continue - # Pobranie pliku hosts wybranego dla serwera - if h.preferred_hostfile_id: - chosen_file = HostFile.query.filter_by(id=h.preferred_hostfile_id, user_id=user_id).first() - if not chosen_file: - chosen_file = default_file - else: - chosen_file = default_file - - # Wstępna zawartość - pobrany plik hosts + chosen_file = HostFile.query.filter_by(id=h.preferred_hostfile_id, user_id=user_id).first() if h.preferred_hostfile_id else default_file final_content = chosen_file.content.strip() - # Jeśli disable_local_default jest włączone → usuwamy local-defaults, ale reszta pozostaje if h.disable_local_default: final_content = remove_local_defaults(final_content, user_id) else: - # Dodaj regex, jeśli nie jest wyłączony if not h.disable_regex_deploy and regex_lines.strip(): final_content = regex_lines + "\n" + final_content - final_content = ensure_local_defaults(final_content, user_id) try: - # 🖥 Wgrywanie na Mikrotik + host_name = format_host(h) if h else "Unknown Host" + if h.type == 'mikrotik': wrapped_content = wrap_mikrotik_content(final_content) deploy_mikrotik(h, wrapped_content) - log_details = f'[MIKROTIK] Updated {format_host(h)} for user {user_id}' + log_details = f'[MIKROTIK] Updated {host_name} for user {user_id}' - # 🖥 Wgrywanie na Linux Daemon elif h.use_daemon and h.type == 'linux': import requests wrapped_content = wrap_content_with_comments(final_content) @@ -1283,9 +1278,8 @@ def deploy_user(user_id): resp = requests.post(url, json={"hosts": wrapped_content}, headers=headers, timeout=10, verify=False) if resp.status_code != 200: raise Exception(f"Daemon POST error: {resp.status_code} - {resp.text}") - log_details = f'[LINUX/DAEMON] Updated {format_host(h)} for user {user_id}' + log_details = f'[LINUX/DAEMON] Updated {host_name} for user {user_id}' - # 🖥 Wgrywanie na standardowy Linux przez SSH else: ssh = open_ssh_connection(h) wrapped_content = wrap_content_with_comments(final_content) @@ -1297,14 +1291,13 @@ def deploy_user(user_id): sftp.close() ssh.close() os.remove(tmp_file_path) - log_details = f'[LINUX] Updated {format_host(h)} for user {user_id}' + log_details = f'[LINUX] Updated {host_name} for user {user_id}' - # Logowanie wdrożenia db.session.add(DeployLog(details=log_details, user_id=user_id)) db.session.commit() except Exception as e: - error_log = f'Failed to update {format_host(h)}: {str(e)} for user {user_id}' + error_log = f'Failed to update {host_name}: {str(e)} for user {user_id}' db.session.add(DeployLog(details=error_log, user_id=user_id)) db.session.commit() @@ -1751,7 +1744,9 @@ def local_defaults(): ip_address = request.form.get('ip_address', '').strip() if hostname and ip_address: - new_entry = LocalDefaultEntry(user_id=user_id, hostname=hostname, ip_address=ip_address or None, entry=entry_content) + entry_content = f"{ip_address} {hostname}" + + new_entry = LocalDefaultEntry(user_id=user_id, hostname=hostname, ip_address=ip_address, entry=entry_content) db.session.add(new_entry) db.session.commit() flash('Dodano nowy wpis.', 'success') @@ -1764,19 +1759,66 @@ def local_defaults(): return render_template('local_defaults.html', entries=entries) @app.route('/local-defaults/delete/', methods=['POST']) -def delete_local_default(entry_id): +@app.route('/local-defaults/delete', methods=['POST']) +def delete_local_default(entry_id=None): if 'user_id' not in session: - return redirect(url_for('login')) + return jsonify({'status': 'error', 'message': 'Unauthorized'}), 403 - entry = LocalDefaultEntry.query.get(entry_id) - if not entry or entry.user_id != session['user_id']: - flash('Wpis nie istnieje lub brak uprawnień.', 'danger') + if request.is_json: + data = request.get_json() + entry_ids = data.get('entry_ids', []) + + if not entry_ids: + return jsonify({'status': 'error', 'message': 'Brak wpisów do usunięcia'}), 400 + + entries_to_delete = LocalDefaultEntry.query.filter( + LocalDefaultEntry.id.in_(entry_ids), + LocalDefaultEntry.user_id == session['user_id'] + ).all() + + if not entries_to_delete: + return jsonify({'status': 'error', 'message': 'Nie znaleziono wpisów do usunięcia'}), 404 + + for entry in entries_to_delete: + db.session.delete(entry) + + db.session.commit() + return jsonify({'status': 'success', 'message': 'Wpisy usunięte pomyślnie'}) + + else: + # Obsługa usuwania pojedynczego wpisu + entry = LocalDefaultEntry.query.get(entry_id) + if not entry or entry.user_id != session['user_id']: + flash('Wpis nie istnieje lub brak uprawnień.', 'danger') + return redirect(url_for('local_defaults')) + + db.session.delete(entry) + db.session.commit() + flash('Wpis został usunięty.', 'info') return redirect(url_for('local_defaults')) - db.session.delete(entry) - db.session.commit() - flash('Wpis został usunięty.', 'info') - return redirect(url_for('local_defaults')) + +@app.route('/local-defaults/update/', methods=['POST']) +def update_local_default(entry_id): + if 'user_id' not in session: + return jsonify({'status': 'error', 'message': 'Unauthorized'}), 403 + + entry = db.session.get(LocalDefaultEntry, entry_id) + if not entry or entry.user_id != session['user_id']: + return jsonify({'status': 'error', 'message': 'Entry not found'}), 404 + + data = request.get_json() + new_ip = data.get('ip_address', '').strip() + new_hostname = data.get('hostname', '').strip() + + if new_ip and new_hostname: + entry.ip_address = new_ip + entry.hostname = new_hostname + entry.entry = f"{new_ip} {new_hostname}" + db.session.commit() + return jsonify({'status': 'success', 'message': 'Updated successfully', 'ip_address': new_ip, 'hostname': new_hostname}) + else: + return jsonify({'status': 'error', 'message': 'IP and hostname are required'}), 400 scheduler = BackgroundScheduler(timezone=get_localzone()) scheduler.add_job(func=scheduled_deployments, trigger="interval", minutes=1, next_run_time=datetime.now()) diff --git a/templates/base.html b/templates/base.html index 912d216..915a954 100644 --- a/templates/base.html +++ b/templates/base.html @@ -40,10 +40,6 @@
  • Importuj serwery z CSV
  • - -
  • Utwórz nowy /etc/hosts
  • + + + + + + + -