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