From 31f7ee98c046509815bf5d18e08dee6f3d3c1d42 Mon Sep 17 00:00:00 2001 From: sundayenglish Date: Wed, 19 Feb 2025 11:11:34 +0700 Subject: [PATCH] first commit --- app.py | 39 +++++++++++++++++++++++++++ data/services.json | 4 +++ static/script.js | 54 +++++++++++++++++++++++++++++++++++++ static/style.css | 30 +++++++++++++++++++++ templates/index.html | 63 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 app.py create mode 100644 data/services.json create mode 100644 static/script.js create mode 100644 static/style.css create mode 100644 templates/index.html diff --git a/app.py b/app.py new file mode 100644 index 0000000..f5a5a96 --- /dev/null +++ b/app.py @@ -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) diff --git a/data/services.json b/data/services.json new file mode 100644 index 0000000..36fc576 --- /dev/null +++ b/data/services.json @@ -0,0 +1,4 @@ +[ + {"name": "service.sundayenglish.com", "command": "cd /project/service-se && ls"}, + {"name": "exercise.sundayenglish.com", "command": "cd /project/exercise-se && ls"} +] diff --git a/static/script.js b/static/script.js new file mode 100644 index 0000000..c130e4d --- /dev/null +++ b/static/script.js @@ -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 = + ' 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 = `
${status}
`; + + toastContainer.appendChild(toast); + + // Tự động ẩn sau 5 giây + setTimeout(() => { + toast.remove(); + }, 5000); +} diff --git a/static/style.css b/static/style.css new file mode 100644 index 0000000..fbf9daa --- /dev/null +++ b/static/style.css @@ -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); +} \ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..3705a56 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,63 @@ + + + + + + + Service List + + + + + + + + + + +
+

🚀 Service List 🚀

+ +
+ + + + + + + + + + {% for service in listService %} + + + + + + {% endfor %} + +
Service NameCommandAction
{{ service.name }}{{ service.command }} + +
+
+ + +
+
Command Output:
+

+      
+
+ +
+ + + + + + + + + + \ No newline at end of file