first commit

main
sundayenglish 5 months ago
commit 31f7ee98c0
  1. 39
      app.py
  2. 4
      data/services.json
  3. 54
      static/script.js
  4. 30
      static/style.css
  5. 63
      templates/index.html

@ -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…
Cancel
Save