parent
31f7ee98c0
commit
2bc46325a5
5 changed files with 196 additions and 57 deletions
@ -1,39 +1,88 @@ |
||||
from flask import Flask, render_template, request, jsonify |
||||
import subprocess |
||||
import json |
||||
import os |
||||
import time |
||||
from flask import Flask, render_template, request, Response, jsonify, redirect, url_for, session |
||||
from datetime import timedelta |
||||
|
||||
app = Flask(__name__) |
||||
app.secret_key = "your_secret_key" |
||||
app.permanent_session_lifetime = timedelta(days=1) |
||||
|
||||
# Danh sách tài khoản & mật khẩu |
||||
USERS = { |
||||
"admin": "123456", |
||||
"user1": "password1", |
||||
"user2": "password2" |
||||
} |
||||
|
||||
# Định nghĩa đường dẫn file JSON chứa danh sách service |
||||
SERVICES_FILE = os.path.join(os.path.dirname(__file__), "data", "services.json") |
||||
|
||||
# Load danh sách dịch vụ từ file JSON |
||||
def load_services(): |
||||
with open(SERVICES_FILE, "r", encoding="utf-8") as file: |
||||
return json.load(file) |
||||
|
||||
@app.route("/") |
||||
def home(): |
||||
listService = load_services() # Đọc danh sách service từ file |
||||
if "user" not in session: |
||||
return redirect(url_for("login")) |
||||
|
||||
listService = load_services() |
||||
return render_template("index.html", listService=listService) |
||||
|
||||
@app.route("/run-command", methods=["POST"]) |
||||
@app.route("/login", methods=["GET", "POST"]) |
||||
def login(): |
||||
if request.method == "POST": |
||||
username = request.form.get("username") |
||||
password = request.form.get("password") |
||||
|
||||
if username in USERS and USERS[username] == password: |
||||
session["user"] = username |
||||
session.permanent = True |
||||
return redirect(url_for("home")) |
||||
else: |
||||
return render_template("login.html", error="Invalid username or password") |
||||
|
||||
return render_template("login.html") |
||||
|
||||
@app.route("/logout") |
||||
def logout(): |
||||
session.pop("user", None) |
||||
return redirect(url_for("login")) |
||||
|
||||
# Streaming real-time output của lệnh |
||||
def generate_output(command_list): |
||||
# Chuyển danh sách thành chuỗi để chạy trong shell |
||||
command_str = " ".join(command_list) |
||||
|
||||
process = subprocess.Popen(command_str, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, executable="/bin/bash") |
||||
|
||||
for line in iter(process.stdout.readline, ''): |
||||
yield f"data: {line.strip()}\n\n" |
||||
time.sleep(0.1) |
||||
|
||||
for line in iter(process.stderr.readline, ''): |
||||
yield f"data: {line.strip()}\n\n" |
||||
time.sleep(0.1) |
||||
|
||||
process.wait() |
||||
if process.returncode == 0: |
||||
yield f"data: SUCCESS\n\n" |
||||
else: |
||||
yield f"data: ERROR\n\n" |
||||
|
||||
@app.route("/run-command", methods=["GET"]) |
||||
def run_command(): |
||||
data = request.json |
||||
command = data.get("command") |
||||
if "user" not in session: |
||||
return jsonify({"error": "Unauthorized"}), 401 |
||||
|
||||
if not command: |
||||
return jsonify({"error": "No command provided"}), 400 |
||||
command_list = request.args.getlist("command[]") # Nhận danh sách lệnh từ frontend |
||||
|
||||
try: |
||||
# Chạy lệnh trong shell và lấy kết quả đầu ra |
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, executable="/bin/bash") |
||||
output = result.stdout.strip() if result.returncode == 0 else result.stderr.strip() |
||||
if not command_list: |
||||
return jsonify({"error": "No command provided"}), 400 |
||||
|
||||
return jsonify({"status": "success" if result.returncode == 0 else "error", "output": output}) |
||||
except Exception as e: |
||||
return jsonify({"status": "error", "output": str(e)}), 500 |
||||
return Response(generate_output(command_list), mimetype="text/event-stream") |
||||
|
||||
if __name__ == "__main__": |
||||
app.run(host="0.0.0.0", port=5000, debug=True) |
||||
|
@ -1,4 +1,20 @@ |
||||
[ |
||||
{"name": "service.sundayenglish.com", "command": "cd /project/service-se && ls"}, |
||||
{"name": "exercise.sundayenglish.com", "command": "cd /project/exercise-se && ls"} |
||||
{ |
||||
"name": "Service", |
||||
"url": "http://service.sundayenglish.com", |
||||
"description": "Sunday English Service", |
||||
"command": ["cd", "/projects/service-se", "&&", "ls"] |
||||
}, |
||||
{ |
||||
"name": "Exercise", |
||||
"url": "http://exercise.sundayenglish.com", |
||||
"description": "Sunday English Exercise", |
||||
"command": ["cd", "/projects/exercise-se", "&&", "ls"] |
||||
}, |
||||
{ |
||||
"name": "Julius", |
||||
"url": null, |
||||
"description": "Julius", |
||||
"command": ["cd", "/projects/julius", "&&", "docker", "compose", "down", "&&", "docker", "compose", "up", "-d"] |
||||
} |
||||
] |
||||
|
@ -0,0 +1,30 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>Login</title> |
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.0/css/bootstrap.min.css"> |
||||
</head> |
||||
<body class="d-flex align-items-center justify-content-center vh-100 bg-dark text-white"> |
||||
|
||||
<div class="card p-4 shadow-lg" style="width: 350px;"> |
||||
<h3 class="text-center">🔐 Login</h3> |
||||
{% if error %} |
||||
<div class="alert alert-danger text-center">{{ error }}</div> |
||||
{% endif %} |
||||
<form method="POST"> |
||||
<div class="mb-3"> |
||||
<label class="form-label">Username</label> |
||||
<input type="text" class="form-control" name="username" required autofocus> |
||||
</div> |
||||
<div class="mb-3"> |
||||
<label class="form-label">Password</label> |
||||
<input type="password" class="form-control" name="password" required> |
||||
</div> |
||||
<button type="submit" class="btn btn-primary w-100">Login</button> |
||||
</form> |
||||
</div> |
||||
|
||||
</body> |
||||
</html> |
Loading…
Reference in new issue