commit
31f7ee98c0
5 changed files with 190 additions and 0 deletions
@ -0,0 +1,39 @@ |
||||
from flask import Flask, render_template, request, jsonify |
||||
import subprocess |
||||
import json |
||||
import os |
||||
|
||||
app = Flask(__name__) |
||||
|
||||
# Đị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 |
||||
return render_template("index.html", listService=listService) |
||||
|
||||
@app.route("/run-command", methods=["POST"]) |
||||
def run_command(): |
||||
data = request.json |
||||
command = data.get("command") |
||||
|
||||
if not command: |
||||
return jsonify({"error": "No command provided"}), 400 |
||||
|
||||
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() |
||||
|
||||
return jsonify({"status": "success" if result.returncode == 0 else "error", "output": output}) |
||||
except Exception as e: |
||||
return jsonify({"status": "error", "output": str(e)}), 500 |
||||
|
||||
if __name__ == "__main__": |
||||
app.run(host="0.0.0.0", port=5000, debug=True) |
@ -0,0 +1,4 @@ |
||||
[ |
||||
{"name": "service.sundayenglish.com", "command": "cd /project/service-se && ls"}, |
||||
{"name": "exercise.sundayenglish.com", "command": "cd /project/exercise-se && ls"} |
||||
] |
@ -0,0 +1,54 @@ |
||||
document.addEventListener("DOMContentLoaded", function () { |
||||
document.querySelectorAll(".run-btn").forEach((button) => { |
||||
button.addEventListener("click", function () { |
||||
let command = this.getAttribute("data-command"); |
||||
runCommand(this, command); |
||||
}); |
||||
}); |
||||
}); |
||||
|
||||
function runCommand(button, command) { |
||||
const originalText = button.innerHTML; |
||||
button.innerHTML = |
||||
'<span class="spinner-border spinner-border-sm"></span> Running...'; |
||||
button.disabled = true; |
||||
|
||||
fetch("/run-command", { |
||||
method: "POST", |
||||
headers: { "Content-Type": "application/json" }, |
||||
body: JSON.stringify({ command: command }), |
||||
}) |
||||
.then((response) => response.json()) |
||||
.then((data) => { |
||||
// Kiểm tra xem lệnh có chạy thành công không
|
||||
if (data.output) { |
||||
showToast("Success", "success"); // Hiển thị thông báo "Success"
|
||||
} else { |
||||
showToast("Error", "danger"); // Hiển thị thông báo "Error"
|
||||
} |
||||
}) |
||||
.catch(() => { |
||||
showToast("Error", "danger"); // Hiển thị "Error" nếu request thất bại
|
||||
}) |
||||
.finally(() => { |
||||
button.innerHTML = originalText; |
||||
button.disabled = false; |
||||
}); |
||||
} |
||||
|
||||
// Hàm hiển thị Toast chỉ với "Success" hoặc "Error"
|
||||
function showToast(status, type) { |
||||
const toastContainer = document.getElementById("toastContainer"); |
||||
const toast = document.createElement("div"); |
||||
|
||||
toast.className = `toast align-items-center text-bg-${type} border-0 show`; |
||||
toast.role = "alert"; |
||||
toast.innerHTML = `<div class="toast-body text-center fw-bold">${status}</div>`; |
||||
|
||||
toastContainer.appendChild(toast); |
||||
|
||||
// Tự động ẩn sau 5 giây
|
||||
setTimeout(() => { |
||||
toast.remove(); |
||||
}, 5000); |
||||
} |
@ -0,0 +1,30 @@ |
||||
body { |
||||
background: linear-gradient(135deg, #66d0ea, #764ba2); /* Gradient Background */ |
||||
min-height: 100vh; |
||||
display: flex; |
||||
align-items: center; |
||||
justify-content: center; |
||||
color: white; |
||||
} |
||||
.container { |
||||
background: rgba(255, 255, 255, 0.15); /* Transparent background */ |
||||
backdrop-filter: blur(10px); /* Glass effect */ |
||||
border-radius: 12px; |
||||
padding: 20px; |
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3); |
||||
} |
||||
h1 { |
||||
font-weight: bold; |
||||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2); |
||||
} |
||||
table { |
||||
background: rgba(255, 255, 255, 0.9); |
||||
border-radius: 8px; |
||||
overflow: hidden; |
||||
} |
||||
.run-btn { |
||||
transition: all 0.3s ease-in-out; |
||||
} |
||||
.run-btn:hover { |
||||
transform: scale(1.1); |
||||
} |
@ -0,0 +1,63 @@ |
||||
<!DOCTYPE html> |
||||
<html lang="en"> |
||||
|
||||
<head> |
||||
<meta charset="UTF-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> |
||||
<title>Service List</title> |
||||
|
||||
<!-- Bootstrap 5 --> |
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> |
||||
|
||||
<!-- Load CSS từ thư mục static --> |
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> |
||||
</head> |
||||
|
||||
<body> |
||||
|
||||
<div class="container p-4"> |
||||
<h1 class="text-center text-white">🚀 Service List 🚀</h1> |
||||
|
||||
<div class="table-responsive"> |
||||
<table class="table table-striped table-bordered text-center"> |
||||
<thead class="table-dark"> |
||||
<tr> |
||||
<th>Service Name</th> |
||||
<th>Command</th> |
||||
<th>Action</th> |
||||
</tr> |
||||
</thead> |
||||
<tbody> |
||||
{% for service in listService %} |
||||
<tr> |
||||
<td><strong>{{ service.name }}</strong></td> |
||||
<td><code class="text-primary">{{ service.command }}</code></td> |
||||
<td> |
||||
<button class="btn btn-success btn-sm run-btn" data-command="{{ service.command }}"> |
||||
Run |
||||
</button> |
||||
</td> |
||||
</tr> |
||||
{% endfor %} |
||||
</tbody> |
||||
</table> |
||||
</div> |
||||
|
||||
<!-- Hiển thị kết quả --> |
||||
<div class="mt-4"> |
||||
<h5 class="text-white">Command Output:</h5> |
||||
<pre id="commandOutput" class="bg-dark text-white p-3 rounded"></pre> |
||||
</div> |
||||
</div> |
||||
<!-- Container hiển thị Toast --> |
||||
<div id="toastContainer" class="position-fixed top-0 end-0 p-3" style="z-index: 1050;"></div> |
||||
|
||||
<!-- Bootstrap JS --> |
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> |
||||
|
||||
<!-- Load JavaScript từ file tĩnh --> |
||||
<script src="{{ url_for('static', filename='script.js') }}"></script> |
||||
|
||||
</body> |
||||
|
||||
</html> |
Loading…
Reference in new issue