-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
134 lines (109 loc) · 4.34 KB
/
Copy pathapp.py
File metadata and controls
134 lines (109 loc) · 4.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import queue
import threading
from flask import Flask, request, render_template, redirect, url_for, flash, session, jsonify, Response
from werkzeug.security import check_password_hash, generate_password_hash
from dotenv import dotenv_values, load_dotenv
import os
import subprocess
import json
import subprocess
import datetime
load_dotenv()
app = Flask(__name__)
app.secret_key = 'supersecretkey'
PASSWORD_HASH = generate_password_hash(os.getenv('PASSWORD'))
# Декоратор для проверки авторизации
def login_required(f):
from functools import wraps
@wraps(f)
def decorated_function(*args, **kwargs):
if not session.get('authenticated'):
return redirect(url_for('index'))
return f(*args, **kwargs)
return decorated_function
def load_script_env(script_name):
env_path = os.path.join('scripts', f"{script_name}.env")
if os.path.exists(env_path):
return dotenv_values(env_path)
return {}
def get_scripts():
try:
return [s for s in os.listdir('scripts') if s.endswith('.sh')]
except:
return ["No scripts yet. Make the 'scripts' folder in your workspace and add there some! Add scriptname.sh.env file to add default variables"]
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
password = request.form.get('password')
if check_password_hash(PASSWORD_HASH, password):
session['authenticated'] = True
return redirect(url_for('scripts'))
else:
flash('Invalid password')
return redirect(url_for('index'))
return render_template('index.html')
@app.route('/scripts', methods=['GET'])
@login_required
def scripts():
return render_template('scripts.html', scripts=get_scripts())
@app.route('/scripts/<script_name>', methods=['GET'])
@login_required
def get_script(script_name):
env_vars = load_script_env(script_name)
return jsonify(env_vars)
@app.route('/run_script', methods=['GET'])
@login_required
def run_script():
script_name = request.args.get('script_name')
if script_name not in get_scripts():
return Response(status=403)
try:
env_vars = json.loads(request.args.get('env_vars', '{}'))
except:
env_vars = {}
# filter unallowed env vars
allowed_keys = load_script_env(script_name).keys()
env_vars = {key: value for key, value in env_vars.items() if key in allowed_keys}
script_path = os.path.join('scripts', script_name)
log_filename = f"{script_name}_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.log"
log_path = os.path.join(os.path.dirname(__file__), 'logs', log_filename)
os.makedirs(os.path.dirname(log_path), exist_ok=True)
def generate_output():
def enqueue_output(pipe, output_queue, isstderr, log_file):
for line in iter(pipe.readline, ''):
if isstderr:
formatted_line = f"<span style='color: red;'>{line}</span>\n\n"
else:
formatted_line = line
log_file.write(line)
log_file.flush()
output_queue.put(formatted_line)
pipe.close()
with open(log_path, 'a') as log_file:
process = subprocess.Popen(
['bash', script_path],
env={**os.environ, **env_vars},
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
bufsize=1
)
output_queue = queue.Queue()
stdout_thread = threading.Thread(target=enqueue_output, args=(process.stdout, output_queue, False, log_file))
stderr_thread = threading.Thread(target=enqueue_output, args=(process.stderr, output_queue, True, log_file))
stdout_thread.start()
stderr_thread.start()
while stdout_thread.is_alive() or stderr_thread.is_alive() or not output_queue.empty():
try:
line = output_queue.get(timeout=0.1)
yield f"data: {line}\n\n"
except queue.Empty:
pass
stdout_thread.join()
stderr_thread.join()
process.wait()
return Response(generate_output(), mimetype='text/event-stream')
def return_app():
return app
if __name__ == '__main__':
app.run(debug=True, threaded=True)