Skip to content

Commit 1e7af33

Browse files
chore: option added to shutdown and reboot server
1 parent ce3c728 commit 1e7af33

File tree

6 files changed

+194
-179
lines changed

6 files changed

+194
-179
lines changed

requirements.txt

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,5 @@ flask-session==0.8.0 # Server-side session handling for Flask
1818
# File system monitoring and watching
1919
watchdog==5.0.2
2020

21-
#
22-
requests
21+
# python requests library for making HTTP requests
22+
requests==2.32.3

src/routes/settings.py

+54-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import os
2+
import time
23
import datetime
3-
from flask import render_template, request, flash, blueprints, redirect, url_for
4+
import subprocess
5+
from flask import render_template, request, flash, blueprints, redirect, url_for, Response, session
46

57
from src.config import app, db
68
from src.models import UserCardSettings, UserDashboardSettings, UserProfile, GeneralSettings, PageToggleSettings
@@ -112,3 +114,54 @@ def settings():
112114
return render_template("error/403.html")
113115

114116
return render_template("settings/settings.html", settings=settings)
117+
118+
def check_sudo_password(sudo_password):
119+
"""
120+
Verify the given sudo password by executing a harmless sudo command.
121+
If the password is correct, it returns True. Otherwise, returns False.
122+
123+
:param sudo_password: The user's sudo password to validate.
124+
:return: True if the password is correct, otherwise False.
125+
"""
126+
try:
127+
# Test if the sudo password is valid by running a safe sudo command
128+
result = subprocess.run(
129+
['sudo', '-S', 'true'],
130+
input=f'{sudo_password}\n',
131+
text=True,
132+
stdout=subprocess.PIPE,
133+
stderr=subprocess.PIPE
134+
)
135+
return result.returncode == 0
136+
137+
except Exception as e:
138+
# Log any exception that occurs while validating the sudo password
139+
return False, str(e)
140+
141+
@app.route('/control', methods=['GET', 'POST'])
142+
def control():
143+
if request.method == 'POST':
144+
action = request.form.get('action')
145+
sudo_password = request.form.get('sudo_password', '')
146+
147+
if action == 'shutdown':
148+
command = ['sudo', '-S', 'shutdown', '-h', 'now']
149+
success_message = "Server is shutting down..."
150+
error_message = "Failed to shutdown: {}"
151+
elif action == 'reboot':
152+
command = ['sudo', '-S', 'reboot']
153+
success_message = "Server is rebooting..."
154+
error_message = "Failed to reboot: {}"
155+
else:
156+
flash("Invalid action!", 'danger')
157+
return redirect(url_for('control'))
158+
159+
try:
160+
# Execute the command with the sudo password
161+
result = subprocess.run(command, input=sudo_password.encode(), check=True, capture_output=True, text=True)
162+
flash(success_message, 'info')
163+
except subprocess.CalledProcessError as e:
164+
flash(error_message.format(e), 'danger')
165+
166+
# Render the control form on GET request
167+
return render_template("settings/control.html")

src/static/css/control.css

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
.title {
2+
text-align: center;
3+
margin-top: 50px;
4+
font-size: 36px;
5+
color: #333;
6+
}
7+
8+
.container {
9+
margin-top: 50px;
10+
padding: 20px;
11+
max-width: 500px;
12+
background-color: #ffffff;
13+
border-radius: 8px;
14+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
15+
}
16+
17+
h1 {
18+
font-size: 24px;
19+
margin-bottom: 20px;
20+
color: #333;
21+
}
22+
23+
.form-group {
24+
margin-bottom: 20px;
25+
}
26+
27+
.form-control {
28+
padding: 10px;
29+
font-size: 16px;
30+
border-radius: 4px;
31+
border: 1px solid #ced4da;
32+
}
33+
34+
.btn {
35+
padding: 10px 15px;
36+
font-size: 16px;
37+
border-radius: 4px;
38+
margin-right: 10px;
39+
transition: background-color 0.3s;
40+
}
41+
42+
.btn-danger {
43+
background-color: #dc3545;
44+
border: none;
45+
}
46+
47+
.btn-danger:hover {
48+
background-color: #c82333;
49+
}
50+
51+
.btn-warning {
52+
background-color: #ffc107;
53+
border: none;
54+
color: #212529;
55+
}
56+
57+
.btn-warning:hover {
58+
background-color: #e0a800;
59+
}

src/static/css/settings.css

+28-171
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,39 @@
1+
/* styles.css */
2+
3+
/* Container and layout */
14
.settings-container {
2-
max-width: 800px;
5+
max-width: 900px;
36
margin: 0 auto;
4-
padding: 20px;
7+
padding: 25px;
58
background-color: #f8f9fa;
6-
border-radius: 8px;
7-
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
9+
border-radius: 12px;
10+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
811
margin-top: 50px;
912
}
1013

14+
/* Title styling */
1115
.settings-title {
12-
font-size: 32px;
16+
font-size: 28px;
1317
color: #343a40;
14-
margin-bottom: 30px;
18+
margin-bottom: 20px;
1519
text-align: center;
20+
display: flex;
21+
align-items: center;
22+
justify-content: center;
23+
gap: 10px;
1624
}
1725

26+
/* Buttons grid layout */
1827
.settings-buttons {
1928
display: grid;
20-
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
21-
gap: 15px;
22-
margin-bottom: 30px;
29+
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
30+
gap: 20px;
31+
margin-bottom: 25px;
2332
}
2433

34+
/* Button styling */
2535
.settings-buttons .btn {
26-
padding: 15px;
36+
padding: 14px 20px;
2737
text-align: center;
2838
background-color: #007bff;
2939
color: #ffffff;
@@ -35,185 +45,32 @@
3545
display: flex;
3646
align-items: center;
3747
justify-content: center;
38-
gap: 10px;
48+
gap: 8px;
3949
}
4050

51+
/* Button icon styling */
4152
.settings-buttons .btn i {
42-
font-size: 20px;
53+
font-size: 18px;
4354
}
4455

56+
/* Button hover effect */
4557
.settings-buttons .btn:hover {
4658
background-color: #0056b3;
4759
transform: scale(1.05);
60+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
4861
}
4962

50-
@media (max-width: 768px) {
51-
.settings-buttons {
52-
grid-template-columns: 1fr;
53-
}
54-
}
55-
56-
57-
.settings-form {
58-
display: flex;
59-
flex-direction: column;
60-
gap: 20px;
61-
}
62-
63-
.settings-section {
64-
border: 1px solid #dee2e6;
65-
border-radius: 8px;
66-
padding: 15px;
67-
background-color: #ffffff;
68-
}
69-
70-
.section-title {
71-
font-size: 24px;
72-
color: #495057;
73-
margin-bottom: 15px;
74-
}
75-
76-
.form-group {
77-
display: flex;
78-
flex-direction: row;
79-
align-items: center;
80-
justify-content: space-between;
81-
margin-bottom: 15px;
82-
}
83-
84-
.form-group label {
85-
font-weight: 600;
86-
color: #495057;
87-
}
88-
89-
.form-group input[type="checkbox"] {
90-
width: 20px;
91-
height: 20px;
92-
cursor: pointer;
93-
margin: 0;
94-
}
95-
96-
.form-group input[type="number"],
97-
.form-group select {
98-
padding: 8px;
99-
border: 1px solid #ced4da;
100-
border-radius: 4px;
101-
background-color: #ffffff;
102-
font-size: 16px;
103-
width: 100%;
104-
}
105-
106-
.btn-primary {
107-
padding: 12px 25px;
108-
background-color: #007bff;
109-
color: #ffffff;
110-
border: none;
111-
border-radius: 4px;
112-
cursor: pointer;
113-
font-size: 16px;
114-
text-align: center;
115-
transition: background-color 0.3s ease, transform 0.2s ease;
116-
}
117-
118-
.btn-primary:hover {
119-
background-color: #0056b3;
120-
transform: scale(1.05);
121-
}
122-
63+
/* Responsive adjustments */
12364
@media (max-width: 768px) {
12465
.settings-container {
125-
padding: 15px;
66+
padding: 20px;
12667
}
12768

12869
.settings-title {
129-
font-size: 28px;
70+
font-size: 24px;
13071
}
13172

13273
.settings-buttons {
13374
grid-template-columns: 1fr;
13475
}
135-
136-
.btn-primary {
137-
width: 100%;
138-
}
139-
}
140-
141-
142-
.settings-form {
143-
display: flex;
144-
flex-direction: column;
145-
gap: 20px;
146-
}
147-
148-
.settings-section {
149-
border: 1px solid #dee2e6;
150-
border-radius: 8px;
151-
padding: 15px;
152-
background-color: #ffffff;
153-
}
154-
155-
.section-title {
156-
font-size: 24px;
157-
color: #495057;
158-
margin-bottom: 15px;
159-
}
160-
161-
.form-group {
162-
display: flex;
163-
flex-direction: row;
164-
align-items: center;
165-
justify-content: space-between;
166-
margin-bottom: 15px;
167-
}
168-
169-
.form-group label {
170-
font-weight: 600;
171-
color: #495057;
172-
}
173-
174-
.form-group input[type="checkbox"] {
175-
width: 20px;
176-
height: 20px;
177-
cursor: pointer;
178-
margin: 0;
179-
}
180-
181-
.form-group input[type="number"],
182-
.form-group select {
183-
padding: 8px;
184-
border: 1px solid #ced4da;
185-
border-radius: 4px;
186-
background-color: #ffffff;
187-
font-size: 16px;
188-
width: 100%;
189-
}
190-
191-
.btn-primary {
192-
padding: 12px 25px;
193-
background-color: #007bff;
194-
color: #ffffff;
195-
border: none;
196-
border-radius: 4px;
197-
cursor: pointer;
198-
font-size: 16px;
199-
text-align: center;
200-
transition: background-color 0.3s ease;
201-
}
202-
203-
.btn-primary:hover {
204-
background-color: #0056b3;
205-
}
206-
207-
@media (max-width: 768px) {
208-
.settings-container {
209-
padding: 15px;
210-
}
211-
212-
.settings-title {
213-
font-size: 28px;
214-
}
215-
216-
.btn-primary {
217-
width: 100%;
218-
}
21976
}

0 commit comments

Comments
 (0)