update delete log, add confirm device
This commit is contained in:
parent
06554328b1
commit
e8694be0e2
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,6 +38,8 @@
|
|||
></button>
|
||||
|
||||
<div class="user-dropdown-content">
|
||||
<div id="confirm-count-user" class="item"></div>
|
||||
|
||||
<button onclick="logout()">Logout</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -60,14 +62,21 @@
|
|||
</button>
|
||||
</div>
|
||||
|
||||
<button
|
||||
id="logConfirmDeleteBtn"
|
||||
type="button"
|
||||
class="btn-with-badge danger-btn"
|
||||
>
|
||||
Delete
|
||||
<span class="badge" id="logDeleteBadge">0</span>
|
||||
</button>
|
||||
<div>
|
||||
<button
|
||||
id="deviceConfirmBtn"
|
||||
type="button"
|
||||
class="primary-btn"
|
||||
>
|
||||
Confirm
|
||||
</button>
|
||||
<span
|
||||
id="confirmUserText"
|
||||
style="font-size: 12px; display: none"
|
||||
>
|
||||
Confirmed by <strong id="confirmedUserName"></strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="errorTableContainer" class="command-section">
|
||||
|
|
@ -85,54 +94,76 @@
|
|||
</div>
|
||||
<div id="logSections"></div>
|
||||
|
||||
<!-- Log Delete Modal -->
|
||||
<div id="logDeleteModal" class="modal hidden">
|
||||
<div class="modal-content">
|
||||
<h2 class="modal-title">Confirm Deletion</h2>
|
||||
|
||||
<table class="delete-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>PID</th>
|
||||
<th>Version</th>
|
||||
<th>Command</th>
|
||||
<th style="text-align: center">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="logDeleteListContainer"></tbody>
|
||||
</table>
|
||||
|
||||
<div style="max-width: 30%" class="modal-content">
|
||||
<div id="deleteLogText"></div>
|
||||
<div class="modal-actions">
|
||||
<button id="logCloseModalBtn" class="btn secondary-btn">
|
||||
<button id="logCancelDeleteBtn" class="secondary-btn">
|
||||
Cancel
|
||||
</button>
|
||||
<button id="logSubmitDeleteBtn" class="btn danger-btn">
|
||||
<button id="logDeleteBtn" class="danger-btn">
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="deviceConfirmModal" class="modal hidden">
|
||||
<div style="max-width: 30%" class="modal-content">
|
||||
<div id="deviceConfirmText"></div>
|
||||
<div class="modal-actions">
|
||||
<button id="deviceCancelBtn" class="secondary-btn">
|
||||
Cancel
|
||||
</button>
|
||||
<button id="submitDeviceConfirmBtn" class="danger-btn">
|
||||
Confirm
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Authentication and get user info -->
|
||||
<script>
|
||||
if (!localStorage.getItem("token")) {
|
||||
logout();
|
||||
}
|
||||
|
||||
document.getElementById(
|
||||
"welcome-user",
|
||||
).innerHTML = `Welcome, <strong>${
|
||||
JSON.parse(localStorage.getItem("user")).name
|
||||
}</strong> ▼`;
|
||||
|
||||
function logout() {
|
||||
localStorage.removeItem("token");
|
||||
localStorage.removeItem("user");
|
||||
window.location.href = "/login";
|
||||
}
|
||||
|
||||
async function getProfile() {
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
await fetch("/api/profile", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
document.getElementById(
|
||||
"welcome-user",
|
||||
).innerHTML = `Welcome, <strong>${data?.user?.name}</strong> ▼`;
|
||||
|
||||
document.getElementById(
|
||||
"confirm-count-user",
|
||||
).innerHTML = `Confirmed: <strong>${data?.user?.confirm_count}</strong>`;
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
}
|
||||
|
||||
getProfile();
|
||||
</script>
|
||||
|
||||
<!-- Get PID - Version - Log -->
|
||||
<script>
|
||||
// Get param pid, version from URL
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
|
|
@ -146,7 +177,12 @@
|
|||
const logSections = document.getElementById("logSections");
|
||||
const commands = ["inventory", "version", "license", "logging"];
|
||||
|
||||
const confirmUserText = document.getElementById("confirmUserText");
|
||||
const confirmedUserName =
|
||||
document.getElementById("confirmedUserName");
|
||||
|
||||
const logsCache = {}; // Store logs and current index for each command
|
||||
let versionsUser = [];
|
||||
|
||||
async function loadPIDs(pidParam, versionParam) {
|
||||
const res = await fetch("/api/pids");
|
||||
|
|
@ -181,6 +217,15 @@
|
|||
);
|
||||
const versions = await res.json();
|
||||
|
||||
versionsUser = versions
|
||||
.filter((verItem) => verItem.user)
|
||||
.map((verItem) => {
|
||||
return {
|
||||
version: verItem.version,
|
||||
user: verItem.user,
|
||||
};
|
||||
});
|
||||
|
||||
pidList.innerHTML = `<option value="${pid}">${pid}</option>`;
|
||||
versionList.innerHTML = versions
|
||||
.map((v) => {
|
||||
|
|
@ -206,7 +251,7 @@
|
|||
versionList.dispatchEvent(new Event("change"));
|
||||
currentParams.set("version", versionParam);
|
||||
} else {
|
||||
// ✅ Default chọn version đầu tiên
|
||||
// Default chọn version đầu tiên
|
||||
if (versions.length > 0) {
|
||||
versionList.value = versions[0].version;
|
||||
versionList.dispatchEvent(new Event("change"));
|
||||
|
|
@ -266,6 +311,20 @@
|
|||
`?${currentParams.toString()}`,
|
||||
);
|
||||
|
||||
// Render Confirm Section
|
||||
const currentVerUser = versionsUser.find(
|
||||
(verItem) => verItem.version === version,
|
||||
);
|
||||
if (currentVerUser) {
|
||||
deviceConfirmBtn.style.display = "none";
|
||||
confirmedUserName.textContent =
|
||||
currentVerUser.user.name;
|
||||
confirmUserText.style.display = "inline";
|
||||
} else {
|
||||
deviceConfirmBtn.style.display = "inline-block";
|
||||
confirmUserText.style.display = "none";
|
||||
}
|
||||
|
||||
loadLogs(pid, version);
|
||||
}
|
||||
});
|
||||
|
|
@ -435,36 +494,11 @@ ${license}`.trim();
|
|||
}
|
||||
</script>
|
||||
|
||||
<!-- Handle delete log -->
|
||||
<script>
|
||||
const STORAGE_KEY = "logDeleteList";
|
||||
const logConfirmDeleteBtn = document.getElementById(
|
||||
"logConfirmDeleteBtn",
|
||||
);
|
||||
const logDeleteModal = document.getElementById("logDeleteModal");
|
||||
const logDeleteListContainer = document.getElementById(
|
||||
"logDeleteListContainer",
|
||||
);
|
||||
const logCloseModalBtn =
|
||||
document.getElementById("logCloseModalBtn");
|
||||
const logSubmitDeleteBtn =
|
||||
document.getElementById("logSubmitDeleteBtn");
|
||||
let pendingDeleteLog = null;
|
||||
|
||||
function getDeleteList() {
|
||||
return JSON.parse(localStorage.getItem(STORAGE_KEY) || "[]");
|
||||
}
|
||||
|
||||
function saveDeleteList(list) {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(list));
|
||||
updateDeleteBtn();
|
||||
}
|
||||
|
||||
function updateDeleteBtn() {
|
||||
const deleteList = getDeleteList();
|
||||
const badge = document.getElementById("logDeleteBadge");
|
||||
badge.textContent = deleteList.length;
|
||||
}
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
document.addEventListener("click", async (e) => {
|
||||
if (e.target.classList.contains("trash-btn")) {
|
||||
const section = e.target.closest(".command-section");
|
||||
const pid = pidList.value;
|
||||
|
|
@ -474,103 +508,58 @@ ${license}`.trim();
|
|||
const log =
|
||||
logsCache[`${pid}_${version}`][command].logs[index];
|
||||
|
||||
const currentList = getDeleteList();
|
||||
if (!currentList.some((item) => item.id === log.id)) {
|
||||
currentList.push({
|
||||
id: log.id,
|
||||
pid,
|
||||
version,
|
||||
command,
|
||||
});
|
||||
saveDeleteList(currentList);
|
||||
pendingDeleteLog = { id: log.id, command, pid, version };
|
||||
|
||||
showToast("Added to delete list");
|
||||
}
|
||||
const text = `Are you sure you want to delete this log? <br /><code>${pid} - ${version} - ${command}</code> <br /> <div style="font-size: 12px; font-weight: 700" >${log.filename}</div>`;
|
||||
document.getElementById("deleteLogText").innerHTML = text;
|
||||
showDeleteModal();
|
||||
}
|
||||
});
|
||||
|
||||
function renderDeleteModal() {
|
||||
const deleteList = getDeleteList();
|
||||
logDeleteListContainer.innerHTML = "";
|
||||
deleteList.forEach((item, i) => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.innerHTML = `
|
||||
<td>${item.id}</td>
|
||||
<td>${item.pid}</td>
|
||||
<td>${item.version}</td>
|
||||
<td>${item.command}</td>
|
||||
<td style="text-align: center;"><button type="button" class="danger-btn remove-item-btn" data-index="${i}" title="Remove">🗑</button></td>
|
||||
`;
|
||||
logDeleteListContainer.appendChild(tr);
|
||||
});
|
||||
const logDeleteModal = document.getElementById("logDeleteModal");
|
||||
const logCancelDeleteBtn =
|
||||
document.getElementById("logCancelDeleteBtn");
|
||||
const logDeleteBtn = document.getElementById("logDeleteBtn");
|
||||
|
||||
function showDeleteModal() {
|
||||
logDeleteModal.classList.remove("hidden");
|
||||
}
|
||||
function hideDeleteModal() {
|
||||
logDeleteModal.classList.add("hidden");
|
||||
pendingDeleteLog = null;
|
||||
}
|
||||
|
||||
logDeleteListContainer.addEventListener("click", (e) => {
|
||||
if (e.target.classList.contains("remove-item-btn")) {
|
||||
const index = parseInt(e.target.getAttribute("data-index"));
|
||||
const list = getDeleteList();
|
||||
list.splice(index, 1);
|
||||
saveDeleteList(list);
|
||||
renderDeleteModal();
|
||||
}
|
||||
});
|
||||
logCancelDeleteBtn.addEventListener("click", hideDeleteModal);
|
||||
|
||||
// Open delete modal
|
||||
logConfirmDeleteBtn.addEventListener("click", () => {
|
||||
renderDeleteModal();
|
||||
logDeleteModal.classList.remove("hidden");
|
||||
});
|
||||
logDeleteBtn.addEventListener("click", async () => {
|
||||
if (!pendingDeleteLog) return;
|
||||
|
||||
// Open close modal
|
||||
logCloseModalBtn.addEventListener("click", () => {
|
||||
logDeleteModal.classList.add("hidden");
|
||||
});
|
||||
|
||||
// Submit delete
|
||||
logSubmitDeleteBtn.addEventListener("click", async () => {
|
||||
const deleteList = getDeleteList();
|
||||
if (deleteList.length === 0) {
|
||||
alert("Please select at least one item to delete.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
confirm(
|
||||
`Are you sure you want to delete ${deleteList.length} item(s)?`,
|
||||
)
|
||||
) {
|
||||
const deletedItems = deleteList.map((item) => {
|
||||
return {
|
||||
id: item.id,
|
||||
command: item.command,
|
||||
};
|
||||
});
|
||||
|
||||
const token = localStorage.getItem("token");
|
||||
|
||||
await fetch("/api/delete-logs", {
|
||||
const token = localStorage.getItem("token");
|
||||
try {
|
||||
const res = await fetch("/api/delete-log", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify({ items: deletedItems }),
|
||||
})
|
||||
.then((res) => res.json())
|
||||
.then((data) => {
|
||||
alert("Deleted successfully!");
|
||||
localStorage.removeItem(STORAGE_KEY);
|
||||
location.reload();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
alert("Delete failed.");
|
||||
});
|
||||
body: JSON.stringify(pendingDeleteLog),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
alert("Deleted successfully!");
|
||||
location.reload();
|
||||
} else {
|
||||
alert("Delete failed.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert("Delete failed.");
|
||||
} finally {
|
||||
hideDeleteModal();
|
||||
pendingDeleteLog = null;
|
||||
}
|
||||
});
|
||||
|
||||
updateDeleteBtn();
|
||||
|
||||
function showToast(message = "Announce something") {
|
||||
const toast = document.getElementById("logToast");
|
||||
toast.textContent = message;
|
||||
|
|
@ -583,5 +572,74 @@ ${license}`.trim();
|
|||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Handle confirm device -->
|
||||
<script>
|
||||
const deviceConfirmBtn =
|
||||
document.getElementById("deviceConfirmBtn");
|
||||
const deviceConfirmModal =
|
||||
document.getElementById("deviceConfirmModal");
|
||||
const deviceCancelBtn = document.getElementById("deviceCancelBtn");
|
||||
const submitDeviceConfirmBtn = document.getElementById(
|
||||
"submitDeviceConfirmBtn",
|
||||
);
|
||||
const deviceConfirmText =
|
||||
document.getElementById("deviceConfirmText");
|
||||
|
||||
let pendingDevice = null;
|
||||
|
||||
deviceConfirmBtn.addEventListener("click", () => {
|
||||
const pid = document.getElementById("pidList").value;
|
||||
const version = document.getElementById("versionList").value;
|
||||
|
||||
if (!pid || !version) {
|
||||
alert("PID or version is missing.");
|
||||
return;
|
||||
}
|
||||
|
||||
pendingDevice = { pid, version };
|
||||
|
||||
deviceConfirmText.innerHTML = `
|
||||
Are you sure you want to confirm this device?<br />
|
||||
<strong>${pid} - ${version}</strong>
|
||||
`;
|
||||
|
||||
deviceConfirmModal.classList.remove("hidden");
|
||||
});
|
||||
|
||||
deviceCancelBtn.addEventListener("click", () => {
|
||||
deviceConfirmModal.classList.add("hidden");
|
||||
pendingDevice = null;
|
||||
});
|
||||
|
||||
submitDeviceConfirmBtn.addEventListener("click", async () => {
|
||||
if (!pendingDevice) return;
|
||||
|
||||
const token = localStorage.getItem("token");
|
||||
try {
|
||||
const res = await fetch("/api/confirm-device", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
body: JSON.stringify(pendingDevice),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (data.success) {
|
||||
alert(data.message || "Device confirmed successfully!");
|
||||
location.reload();
|
||||
} else {
|
||||
alert(data.message || "Confirmation failed.");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert("Confirmation failed.");
|
||||
} finally {
|
||||
deviceConfirmModal.classList.add("hidden");
|
||||
pendingDevice = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
22
route/web.js
22
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);
|
||||
};
|
||||
|
|
|
|||
144
server.js
144
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 = [
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue