From e8694be0e2e43703854d5f9d758b427d3548bbc1 Mon Sep 17 00:00:00 2001 From: dbdbd9 Date: Mon, 28 Jul 2025 09:49:24 +0700 Subject: [PATCH] update delete log, add confirm device --- public/css/style.css | 36 ++--- public/dashboard.html | 344 ++++++++++++++++++++++++------------------ route/web.js | 22 +++ server.js | 144 +++++++++++++----- 4 files changed, 343 insertions(+), 203 deletions(-) diff --git a/public/css/style.css b/public/css/style.css index f3a9a36..265bfa6 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -422,6 +422,7 @@ mark { display: flex; justify-content: flex-end; gap: 10px; + margin-top: 1rem; } @keyframes fadeIn { @@ -435,26 +436,6 @@ mark { } } -.btn-with-badge { - position: relative; - padding: 8px 14px !important; -} - -.btn-with-badge .badge { - position: absolute; - top: -6px; - right: -10px; - background-color: #fff; - color: #dc3545; - font-size: 12px; - font-weight: bold; - padding: 3px 6px; - border-radius: 999px; - border: 1px solid #dc3545; - min-width: 20px; - text-align: center; -} - .user-dropdown { position: relative; display: inline-block; @@ -500,6 +481,15 @@ mark { background-color: #e9ecef; } +.user-dropdown-content .item { + width: 100%; + padding: 10px 16px; + border: none; + background-color: #f8f9fa; + text-align: left; + font-size: 14px; +} + .user-dropdown:hover .user-dropdown-content { display: block; } @@ -522,3 +512,9 @@ mark { opacity: 1; bottom: 40px; } + +@media only screen and (max-width: 768px) { + .modal-content { + max-width: 90% !important; + } +} diff --git a/public/dashboard.html b/public/dashboard.html index bb2763b..96fbc43 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -38,6 +38,8 @@ >
+
+
@@ -60,14 +62,21 @@ - +
+ + +
@@ -85,54 +94,76 @@
- + + + + + + diff --git a/route/web.js b/route/web.js index 93a0fae..1aae46c 100644 --- a/route/web.js +++ b/route/web.js @@ -2,6 +2,7 @@ const express = require("express"); const path = require("path"); const bcrypt = require("bcrypt"); const { createToken } = require("../utils/jwt"); +const { authenticateToken } = require("../middleware/auth"); let router = express.Router(); @@ -48,5 +49,26 @@ module.exports = (app, db) => { } }); + router.get("/api/profile", authenticateToken, async (req, res) => { + const userId = req.user?.id; + + try { + const [rows] = await db.query("SELECT * FROM users WHERE id = ?", [ + userId, + ]); + const user = rows[0]; + + return res.status(200).json({ + user: { + name: user.name, + confirm_count: user.confirm_count, + }, + }); + } catch (err) { + console.error(err); + return res.status(500).json({ message: "Internal Server Error" }); + } + }); + app.use("/", router); }; diff --git a/server.js b/server.js index 3da655b..115d111 100644 --- a/server.js +++ b/server.js @@ -90,9 +90,21 @@ const checkAndInsertAdminUser = async () => { ); } - if (!(await columnExists("devices", "updated_by"))) { + if (await columnExists("devices", "updated_by")) { + await db.query(`ALTER TABLE devices DROP COLUMN updated_by`); + } + + if (!(await columnExists("devices", "updated_user"))) { + await db.query(` + ALTER TABLE devices + ADD COLUMN updated_user INT DEFAULT NULL, + ADD CONSTRAINT fk_updated_by_user FOREIGN KEY (updated_user) REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE + `); + } + + if (!(await columnExists("devices", "is_confirmed"))) { await db.query( - `ALTER TABLE devices ADD COLUMN updated_by VARCHAR(100) DEFAULT NULL`, + `ALTER TABLE devices ADD COLUMN is_confirmed BOOLEAN DEFAULT FALSE`, ); } @@ -144,7 +156,7 @@ app.get("/api/pid/:pid/versions", async (req, res) => { // Lấy danh sách các version tương ứng với PID const [devices] = await db.query( - "SELECT id, version FROM devices WHERE pid = ?", + "SELECT id, version, updated_user FROM devices WHERE pid = ?", [pid], ); @@ -153,7 +165,12 @@ app.get("/api/pid/:pid/versions", async (req, res) => { for (const device of devices) { const deviceId = device.id; const version = device.version; + const userId = device.updated_user; + const [user] = await db.query( + "SELECT id, name FROM users WHERE id = ?", + [userId], + ); const [[inv]] = await db.query( "SELECT COUNT(*) AS c FROM inventory_outputs WHERE device_id = ?", [deviceId], @@ -172,6 +189,7 @@ app.get("/api/pid/:pid/versions", async (req, res) => { ); results.push({ + user: user[0], version, commands: { inventory: inv.c, @@ -202,47 +220,33 @@ app.get("/api/device/:pid/:version/:command", async (req, res) => { res.json(logs); }); -app.post("/api/delete-logs", authenticateToken, async (req, res) => { - const items = req.body.items; - const userId = req.user?.id; - - if (!Array.isArray(items)) { - return res.status(400).json({ message: "Invalid payload" }); - } +app.post("/api/delete-log", authenticateToken, async (req, res) => { + const logId = req.body.id; + const logCommand = req.body.command; try { - for (const item of items) { - const { command, id } = item; - - if (!command || typeof id !== "number") continue; - - const allowedCommands = [ - "inventory", - "version", - "license", - "logging", - ]; - if (!allowedCommands.includes(command)) continue; - - const [rows] = await db.query( - `SELECT id FROM \`${command}_outputs\` WHERE id = ?`, - [id], - ); - - if (rows.length === 0) { - continue; - } - - // Delete log - await db.query(`DELETE FROM \`${command}_outputs\` WHERE id = ?`, [ - id, - ]); - await db.query( - "UPDATE users SET confirm_count = confirm_count + 1 WHERE id = ?", - [userId], - ); + if (!logCommand || typeof logId !== "number") { + return res + .status(400) + .json({ success: false, message: "Invalid input" }); } + const [rows] = await db.query( + `SELECT id FROM \`${logCommand}_outputs\` WHERE id = ?`, + [logId], + ); + + if (rows.length === 0) { + return res.status(404).json({ + success: false, + message: "Log not found", + }); + } + + await db.query(`DELETE FROM \`${logCommand}_outputs\` WHERE id = ?`, [ + logId, + ]); + res.json({ success: true }); } catch (error) { console.error("Delete error:", error); @@ -250,6 +254,66 @@ app.post("/api/delete-logs", authenticateToken, async (req, res) => { } }); +app.post("/api/confirm-device", authenticateToken, async (req, res) => { + const { pid, version } = req.body; + const updatedBy = req.user?.id; + + if (!pid || !version) { + return res.status(400).json({ + success: false, + message: "Missing pid or version", + }); + } + + try { + // Check if device exists + const [rows] = await db.query( + `SELECT * FROM devices WHERE pid = ? AND version = ?`, + [pid, version], + ); + + if (rows.length === 0) { + return res.status(404).json({ + success: false, + message: "Device not found.", + }); + } + + if (rows[0].is_confirmed) { + return res.status(400).json({ + success: false, + message: "Device is already confirmed.", + }); + } + + // Update device confirmation + await db.query( + `UPDATE devices + SET is_confirmed = true, + updated_at = CURRENT_TIMESTAMP, + updated_user = ? + WHERE pid = ? AND version = ?`, + [updatedBy, pid, version], + ); + + await db.query( + "UPDATE users SET confirm_count = confirm_count + 1 WHERE id = ?", + [updatedBy], + ); + + res.json({ + success: true, + message: "Device confirmed successfully!", + }); + } catch (error) { + console.error("Confirm device error:", error); + res.status(500).json({ + success: false, + message: "Server error, please try again!", + }); + } +}); + // Danh sách regex lọc lỗi const errorPatterns = [ {