diff --git a/requirements.txt b/requirements.txt index 11f8e53..3e26261 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,5 +18,5 @@ flask-session==0.8.0 # Server-side session handling for Flask # File system monitoring and watching watchdog==5.0.2 -# -requests \ No newline at end of file +# python requests library for making HTTP requests +requests==2.32.3 \ No newline at end of file diff --git a/setup.sh b/setup.sh index e43d738..9fe4473 100644 --- a/setup.sh +++ b/setup.sh @@ -300,10 +300,7 @@ add_cron_job() { local cron_job="* * * * * /bin/bash $script_path >> $log_dir/$APP_NAME_LOWER-cron.log 2>&1" # Create log directory with error handling - if [ $? -ne 0 ]; then - log "CRITICAL" "Failed to create log directory: $log_dir" - exit 1 - fi + create_dir "$log_dir" # Temporarily store current crontab to avoid overwriting on error @@ -314,10 +311,16 @@ add_cron_job() { fi # List the current crontab - if ! $crontab_cmd -l 2>/dev/null > "$temp_cron"; then - log "CRITICAL" "Unable to list current crontab." - rm "$temp_cron" - exit 1 + if ! $crontab_cmd -l > "$temp_cron" 2>&1; then + # If no crontab exists, create an empty file + if grep -q "no crontab for" "$temp_cron" 2>/dev/null; then + : > "$temp_cron" # Create an empty file + log "No crontab for user $USER_NAME. Creating new crontab." + else + log "CRITICAL" "Unable to list current crontab." + rm "$temp_cron" + exit 1 + fi fi # Ensure the cron job does not already exist diff --git a/src/config.py b/src/config.py index 16868818..73328e9 100644 --- a/src/config.py +++ b/src/config.py @@ -13,7 +13,7 @@ DESCRIPTION = f"{APP_NAME} is a web application that allows you to monitor your system resources." AUTHOR = "Deepak Raj" YEAR = "2024" -VERSION = "v1.0.4-pre-release" +VERSION = "v1.0.4" PROJECT_URL = f"https://github.com/codeperfectplus/{APP_NAME}" CONTACT_EMAIL = "" SYSTEM_NAME = get_system_node_name() diff --git a/src/docs/installation.md b/src/docs/installation.md index fd947c0..d7bbaf7 100644 --- a/src/docs/installation.md +++ b/src/docs/installation.md @@ -150,7 +150,7 @@ systemguard-installer --help | Version | Release Date | Status | Key Features | | ---------- | ------------ | ----------- | --------------------------------------------------- | -| v1.0.4 | - | In Testing | Stable version of pre-release features | +| v1.0.4 | 13/09/2024 | Stable | Stable version of pre-release features, stability | | v1.0.4-pre | 10/09/2024 | Pre-release | Auto-update, website monitoring, graph improvements | | v1.0.3 | 05/09/2024 | Stable | Performance optimization, CPU graphs, bug fixes | | v1.0.2 | 02/09/2024 | Deprecated | Minor fixes, initial graph improvements | diff --git a/src/routes/settings.py b/src/routes/settings.py index 970fe63..a989e86 100644 --- a/src/routes/settings.py +++ b/src/routes/settings.py @@ -1,6 +1,8 @@ import os +import time import datetime -from flask import render_template, request, flash, blueprints, redirect, url_for +import subprocess +from flask import render_template, request, flash, blueprints, redirect, url_for, Response, session from src.config import app, db from src.models import UserCardSettings, UserDashboardSettings, UserProfile, GeneralSettings, PageToggleSettings @@ -112,3 +114,54 @@ def settings(): return render_template("error/403.html") return render_template("settings/settings.html", settings=settings) + +def check_sudo_password(sudo_password): + """ + Verify the given sudo password by executing a harmless sudo command. + If the password is correct, it returns True. Otherwise, returns False. + + :param sudo_password: The user's sudo password to validate. + :return: True if the password is correct, otherwise False. + """ + try: + # Test if the sudo password is valid by running a safe sudo command + result = subprocess.run( + ['sudo', '-S', 'true'], + input=f'{sudo_password}\n', + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE + ) + return result.returncode == 0 + + except Exception as e: + # Log any exception that occurs while validating the sudo password + return False, str(e) + +@app.route('/control', methods=['GET', 'POST']) +def control(): + if request.method == 'POST': + action = request.form.get('action') + sudo_password = request.form.get('sudo_password', '') + + if action == 'shutdown': + command = ['sudo', '-S', 'shutdown', '-h', 'now'] + success_message = "Server is shutting down..." + error_message = "Failed to shutdown: {}" + elif action == 'reboot': + command = ['sudo', '-S', 'reboot'] + success_message = "Server is rebooting..." + error_message = "Failed to reboot: {}" + else: + flash("Invalid action!", 'danger') + return redirect(url_for('control')) + + try: + # Execute the command with the sudo password + result = subprocess.run(command, input=sudo_password.encode(), check=True, capture_output=True, text=True) + flash(success_message, 'info') + except subprocess.CalledProcessError as e: + flash(error_message.format(e), 'danger') + + # Render the control form on GET request + return render_template("settings/control.html") \ No newline at end of file diff --git a/src/static/css/control.css b/src/static/css/control.css new file mode 100644 index 0000000..af981ea --- /dev/null +++ b/src/static/css/control.css @@ -0,0 +1,59 @@ +.title { + text-align: center; + margin-top: 50px; + font-size: 36px; + color: #333; +} + +.container { + margin-top: 50px; + padding: 20px; + max-width: 500px; + background-color: #ffffff; + border-radius: 8px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); +} + +h1 { + font-size: 24px; + margin-bottom: 20px; + color: #333; +} + +.form-group { + margin-bottom: 20px; +} + +.form-control { + padding: 10px; + font-size: 16px; + border-radius: 4px; + border: 1px solid #ced4da; +} + +.btn { + padding: 10px 15px; + font-size: 16px; + border-radius: 4px; + margin-right: 10px; + transition: background-color 0.3s; +} + +.btn-danger { + background-color: #dc3545; + border: none; +} + +.btn-danger:hover { + background-color: #c82333; +} + +.btn-warning { + background-color: #ffc107; + border: none; + color: #212529; +} + +.btn-warning:hover { + background-color: #e0a800; +} diff --git a/src/static/css/settings.css b/src/static/css/settings.css index 0105874..fd8488a 100644 --- a/src/static/css/settings.css +++ b/src/static/css/settings.css @@ -1,29 +1,39 @@ +/* styles.css */ + +/* Container and layout */ .settings-container { - max-width: 800px; + max-width: 900px; margin: 0 auto; - padding: 20px; + padding: 25px; background-color: #f8f9fa; - border-radius: 8px; - box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + border-radius: 12px; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1); margin-top: 50px; } +/* Title styling */ .settings-title { - font-size: 32px; + font-size: 28px; color: #343a40; - margin-bottom: 30px; + margin-bottom: 20px; text-align: center; + display: flex; + align-items: center; + justify-content: center; + gap: 10px; } +/* Buttons grid layout */ .settings-buttons { display: grid; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - gap: 15px; - margin-bottom: 30px; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 20px; + margin-bottom: 25px; } +/* Button styling */ .settings-buttons .btn { - padding: 15px; + padding: 14px 20px; text-align: center; background-color: #007bff; color: #ffffff; @@ -35,185 +45,32 @@ display: flex; align-items: center; justify-content: center; - gap: 10px; + gap: 8px; } +/* Button icon styling */ .settings-buttons .btn i { - font-size: 20px; + font-size: 18px; } +/* Button hover effect */ .settings-buttons .btn:hover { background-color: #0056b3; transform: scale(1.05); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); } -@media (max-width: 768px) { - .settings-buttons { - grid-template-columns: 1fr; - } -} - - -.settings-form { - display: flex; - flex-direction: column; - gap: 20px; -} - -.settings-section { - border: 1px solid #dee2e6; - border-radius: 8px; - padding: 15px; - background-color: #ffffff; -} - -.section-title { - font-size: 24px; - color: #495057; - margin-bottom: 15px; -} - -.form-group { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - margin-bottom: 15px; -} - -.form-group label { - font-weight: 600; - color: #495057; -} - -.form-group input[type="checkbox"] { - width: 20px; - height: 20px; - cursor: pointer; - margin: 0; -} - -.form-group input[type="number"], -.form-group select { - padding: 8px; - border: 1px solid #ced4da; - border-radius: 4px; - background-color: #ffffff; - font-size: 16px; - width: 100%; -} - -.btn-primary { - padding: 12px 25px; - background-color: #007bff; - color: #ffffff; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 16px; - text-align: center; - transition: background-color 0.3s ease, transform 0.2s ease; -} - -.btn-primary:hover { - background-color: #0056b3; - transform: scale(1.05); -} - +/* Responsive adjustments */ @media (max-width: 768px) { .settings-container { - padding: 15px; + padding: 20px; } .settings-title { - font-size: 28px; + font-size: 24px; } .settings-buttons { grid-template-columns: 1fr; } - - .btn-primary { - width: 100%; - } -} - - -.settings-form { - display: flex; - flex-direction: column; - gap: 20px; -} - -.settings-section { - border: 1px solid #dee2e6; - border-radius: 8px; - padding: 15px; - background-color: #ffffff; -} - -.section-title { - font-size: 24px; - color: #495057; - margin-bottom: 15px; -} - -.form-group { - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - margin-bottom: 15px; -} - -.form-group label { - font-weight: 600; - color: #495057; -} - -.form-group input[type="checkbox"] { - width: 20px; - height: 20px; - cursor: pointer; - margin: 0; -} - -.form-group input[type="number"], -.form-group select { - padding: 8px; - border: 1px solid #ced4da; - border-radius: 4px; - background-color: #ffffff; - font-size: 16px; - width: 100%; -} - -.btn-primary { - padding: 12px 25px; - background-color: #007bff; - color: #ffffff; - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 16px; - text-align: center; - transition: background-color 0.3s ease; -} - -.btn-primary:hover { - background-color: #0056b3; -} - -@media (max-width: 768px) { - .settings-container { - padding: 15px; - } - - .settings-title { - font-size: 28px; - } - - .btn-primary { - width: 100%; - } } diff --git a/src/templates/settings/control.html b/src/templates/settings/control.html new file mode 100644 index 0000000..bc04916 --- /dev/null +++ b/src/templates/settings/control.html @@ -0,0 +1,38 @@ +{% extends 'base/base.html' %} +{% block title %}Control Panel{% endblock %} +{% block extra_head %} + +{% endblock %} +{% block content %} +