update delete log, authentication, add export device #4
			
				
			
		
		
		
	| 
						 | 
				
			
			@ -123,23 +123,29 @@
 | 
			
		|||
 | 
			
		||||
			async function getProfile() {
 | 
			
		||||
				const token = localStorage.getItem("token");
 | 
			
		||||
				const user = JSON.parse(localStorage.getItem("user"));
 | 
			
		||||
 | 
			
		||||
				await fetch("/api/profile", {
 | 
			
		||||
					method: "GET",
 | 
			
		||||
				await fetch("/api/confirm-count", {
 | 
			
		||||
					method: "POST",
 | 
			
		||||
					headers: {
 | 
			
		||||
						"Content-Type": "application/json",
 | 
			
		||||
						Authorization: `Bearer ${token}`,
 | 
			
		||||
					},
 | 
			
		||||
					body: JSON.stringify({
 | 
			
		||||
						email: user.email,
 | 
			
		||||
					}),
 | 
			
		||||
				})
 | 
			
		||||
					.then((res) => res.json())
 | 
			
		||||
					.then((data) => {
 | 
			
		||||
						document.getElementById(
 | 
			
		||||
							"welcome-user",
 | 
			
		||||
						).innerHTML = `Welcome, <strong>${data?.user?.name}</strong> ▼`;
 | 
			
		||||
						).innerHTML = `Welcome, <strong>${
 | 
			
		||||
							JSON.parse(localStorage.getItem("user")).name
 | 
			
		||||
						}</strong> ▼`;
 | 
			
		||||
 | 
			
		||||
						document.getElementById(
 | 
			
		||||
							"confirm-count-user",
 | 
			
		||||
						).innerHTML = `Confirmed: <strong>${data?.user?.confirm_count}</strong>`;
 | 
			
		||||
						).innerHTML = `Confirmed: <strong>${data?.count}</strong>`;
 | 
			
		||||
					})
 | 
			
		||||
					.catch((err) => {
 | 
			
		||||
						console.error(err);
 | 
			
		||||
| 
						 | 
				
			
			@ -304,8 +310,7 @@
 | 
			
		|||
					);
 | 
			
		||||
					if (currentVerUser) {
 | 
			
		||||
						deviceConfirmBtn.style.display = "none";
 | 
			
		||||
						confirmedUserName.textContent =
 | 
			
		||||
							currentVerUser.user.name;
 | 
			
		||||
						confirmedUserName.textContent = currentVerUser.user;
 | 
			
		||||
						confirmUserText.style.display = "inline";
 | 
			
		||||
					} else {
 | 
			
		||||
						deviceConfirmBtn.style.display = "inline-block";
 | 
			
		||||
| 
						 | 
				
			
			@ -602,6 +607,7 @@ ${license}`.trim();
 | 
			
		|||
				if (!pendingDevice) return;
 | 
			
		||||
 | 
			
		||||
				const token = localStorage.getItem("token");
 | 
			
		||||
				const user = JSON.parse(localStorage.getItem("user"));
 | 
			
		||||
				try {
 | 
			
		||||
					const res = await fetch("/api/confirm-device", {
 | 
			
		||||
						method: "POST",
 | 
			
		||||
| 
						 | 
				
			
			@ -609,7 +615,11 @@ ${license}`.trim();
 | 
			
		|||
							"Content-Type": "application/json",
 | 
			
		||||
							Authorization: `Bearer ${token}`,
 | 
			
		||||
						},
 | 
			
		||||
						body: JSON.stringify(pendingDevice),
 | 
			
		||||
						body: JSON.stringify({
 | 
			
		||||
							...pendingDevice,
 | 
			
		||||
							name: user.name,
 | 
			
		||||
							email: user.email,
 | 
			
		||||
						}),
 | 
			
		||||
					});
 | 
			
		||||
					const data = await res.json();
 | 
			
		||||
					if (data.success) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,11 +67,17 @@
 | 
			
		|||
					const errorMsg = document.querySelector(".error-msg");
 | 
			
		||||
 | 
			
		||||
					try {
 | 
			
		||||
						const res = await fetch("/api/login", {
 | 
			
		||||
							method: "POST",
 | 
			
		||||
							headers: { "Content-Type": "application/json" },
 | 
			
		||||
							body: JSON.stringify({ email, password }),
 | 
			
		||||
						});
 | 
			
		||||
						const res = await fetch(
 | 
			
		||||
							"https://stage.nswteam.net/api/login",
 | 
			
		||||
							{
 | 
			
		||||
								method: "POST",
 | 
			
		||||
								headers: { "Content-Type": "application/json" },
 | 
			
		||||
								body: JSON.stringify({
 | 
			
		||||
									userEmail: email,
 | 
			
		||||
									password,
 | 
			
		||||
								}),
 | 
			
		||||
							},
 | 
			
		||||
						);
 | 
			
		||||
 | 
			
		||||
						const data = await res.json();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +89,16 @@
 | 
			
		|||
						}
 | 
			
		||||
 | 
			
		||||
						localStorage.setItem("token", data.token);
 | 
			
		||||
						localStorage.setItem("user", JSON.stringify(data.user));
 | 
			
		||||
						localStorage.setItem(
 | 
			
		||||
							"user",
 | 
			
		||||
							JSON.stringify({
 | 
			
		||||
								name:
 | 
			
		||||
									data.data.firstName +
 | 
			
		||||
									" " +
 | 
			
		||||
									data.data.lastName,
 | 
			
		||||
								email: data.data.userEmail,
 | 
			
		||||
							}),
 | 
			
		||||
						);
 | 
			
		||||
 | 
			
		||||
						window.location.href = "/";
 | 
			
		||||
					} catch (err) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										58
									
								
								route/web.js
								
								
								
								
							
							
						
						
									
										58
									
								
								route/web.js
								
								
								
								
							| 
						 | 
				
			
			@ -1,8 +1,5 @@
 | 
			
		|||
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();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -15,60 +12,5 @@ module.exports = (app, db) => {
 | 
			
		|||
		res.sendFile(path.join(__dirname, "../public/login.html"));
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	router.post("/api/login", async (req, res) => {
 | 
			
		||||
		const { email, password } = req.body;
 | 
			
		||||
 | 
			
		||||
		try {
 | 
			
		||||
			const [rows] = await db.query(
 | 
			
		||||
				"SELECT * FROM users WHERE email = ?",
 | 
			
		||||
				[email],
 | 
			
		||||
			);
 | 
			
		||||
			const user = rows[0];
 | 
			
		||||
			const isMatch = await bcrypt.compare(
 | 
			
		||||
				password,
 | 
			
		||||
				user?.password || "!@#",
 | 
			
		||||
			);
 | 
			
		||||
 | 
			
		||||
			if (!user || !isMatch) {
 | 
			
		||||
				return res
 | 
			
		||||
					.status(401)
 | 
			
		||||
					.json({ message: "Invalid email or password" });
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			const token = createToken({
 | 
			
		||||
				id: user.id,
 | 
			
		||||
				email: user.email,
 | 
			
		||||
				name: user.name,
 | 
			
		||||
			});
 | 
			
		||||
			return res
 | 
			
		||||
				.status(200)
 | 
			
		||||
				.json({ token, user: { name: user.name, email: user.email } });
 | 
			
		||||
		} catch (err) {
 | 
			
		||||
			console.error("Login error:", err);
 | 
			
		||||
			return res.status(500).json({ message: "Internal Server Error" });
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										113
									
								
								server.js
								
								
								
								
							
							
						
						
									
										113
									
								
								server.js
								
								
								
								
							| 
						 | 
				
			
			@ -5,9 +5,7 @@ const dotenv = require("dotenv");
 | 
			
		|||
dotenv.config();
 | 
			
		||||
 | 
			
		||||
const stringSimilarity = require("string-similarity");
 | 
			
		||||
const bcrypt = require("bcrypt");
 | 
			
		||||
const inititalWebRoute = require("./route/web");
 | 
			
		||||
const { authenticateToken } = require("./middleware/auth");
 | 
			
		||||
 | 
			
		||||
// Nhận mảng message đầu vào, trả về mảng message đã loại bỏ trùng lặp
 | 
			
		||||
function deduplicateErrors(errors, threshold = 0.3) {
 | 
			
		||||
| 
						 | 
				
			
			@ -37,10 +35,17 @@ app.use(bodyParser.json());
 | 
			
		|||
app.use(express.static("public"));
 | 
			
		||||
app.use(express.urlencoded({ extended: true }));
 | 
			
		||||
 | 
			
		||||
// const db = mysql.createPool({
 | 
			
		||||
// 	host: "localhost",
 | 
			
		||||
// 	user: "admin",
 | 
			
		||||
// 	password: "Work1234",
 | 
			
		||||
// 	database: "log_analysis",
 | 
			
		||||
// });
 | 
			
		||||
 | 
			
		||||
const db = mysql.createPool({
 | 
			
		||||
	host: "localhost",
 | 
			
		||||
	user: "admin",
 | 
			
		||||
	password: "Work1234",
 | 
			
		||||
	user: "root",
 | 
			
		||||
	password: "",
 | 
			
		||||
	database: "log_analysis",
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -54,21 +59,6 @@ async function columnExists(table, column) {
 | 
			
		|||
	return rows[0].count > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const checkAndInsertAdminUser = async () => {
 | 
			
		||||
	const [rows] = await db.query(`SELECT id FROM users WHERE email = ?`, [
 | 
			
		||||
		"admin@apactech.io",
 | 
			
		||||
	]);
 | 
			
		||||
 | 
			
		||||
	if (rows.length === 0) {
 | 
			
		||||
		const hashedPassword = await bcrypt.hash("admin0312", 10);
 | 
			
		||||
 | 
			
		||||
		await db.query(
 | 
			
		||||
			`INSERT INTO users (name, email, password) VALUES (?, ?, ?)`,
 | 
			
		||||
			["Admin", "admin@apactech.io", hashedPassword],
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// === Tạo bảng
 | 
			
		||||
(async () => {
 | 
			
		||||
	await db.query(`
 | 
			
		||||
| 
						 | 
				
			
			@ -94,12 +84,25 @@ const checkAndInsertAdminUser = async () => {
 | 
			
		|||
		await db.query(`ALTER TABLE devices DROP COLUMN updated_by`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(await columnExists("devices", "updated_user"))) {
 | 
			
		||||
	if (await columnExists("devices", "updated_user")) {
 | 
			
		||||
		await db.query(
 | 
			
		||||
			`ALTER TABLE devices DROP FOREIGN KEY fk_updated_by_user`,
 | 
			
		||||
		);
 | 
			
		||||
		await db.query(`ALTER TABLE devices DROP COLUMN updated_user`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(await columnExists("devices", "update_name"))) {
 | 
			
		||||
		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
 | 
			
		||||
		`);
 | 
			
		||||
		ALTER TABLE devices 
 | 
			
		||||
		ADD COLUMN update_name VARCHAR(255)
 | 
			
		||||
	`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(await columnExists("devices", "update_email"))) {
 | 
			
		||||
		await db.query(`
 | 
			
		||||
		ALTER TABLE devices 
 | 
			
		||||
		ADD COLUMN update_email VARCHAR(255)
 | 
			
		||||
	`);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!(await columnExists("devices", "is_confirmed"))) {
 | 
			
		||||
| 
						 | 
				
			
			@ -121,9 +124,9 @@ const checkAndInsertAdminUser = async () => {
 | 
			
		|||
      )
 | 
			
		||||
    `);
 | 
			
		||||
 | 
			
		||||
		if (!(await columnExists(`${command}_outputs`, "is_deleted"))) {
 | 
			
		||||
		if (await columnExists(`${command}_outputs`, "is_deleted")) {
 | 
			
		||||
			await db.query(
 | 
			
		||||
				`ALTER TABLE ${command}_outputs ADD COLUMN is_deleted BOOLEAN DEFAULT FALSE`,
 | 
			
		||||
				`ALTER TABLE ${command}_outputs DROP COLUMN is_deleted`,
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
| 
						 | 
				
			
			@ -134,18 +137,8 @@ const checkAndInsertAdminUser = async () => {
 | 
			
		|||
	await createOutputTable("logging");
 | 
			
		||||
 | 
			
		||||
	await db.query(`
 | 
			
		||||
    CREATE TABLE IF NOT EXISTS users (
 | 
			
		||||
		id INT AUTO_INCREMENT PRIMARY KEY,
 | 
			
		||||
		name VARCHAR(100) NOT NULL,
 | 
			
		||||
		email VARCHAR(100) NOT NULL UNIQUE,
 | 
			
		||||
		password VARCHAR(255) NOT NULL,
 | 
			
		||||
		confirm_count INT DEFAULT 0,
 | 
			
		||||
		created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
 | 
			
		||||
		updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
 | 
			
		||||
		)
 | 
			
		||||
		DROP TABLE IF EXISTS users
 | 
			
		||||
	`);
 | 
			
		||||
 | 
			
		||||
	await checkAndInsertAdminUser();
 | 
			
		||||
})();
 | 
			
		||||
 | 
			
		||||
// === APIs ===
 | 
			
		||||
| 
						 | 
				
			
			@ -162,7 +155,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, updated_user FROM devices WHERE pid = ?",
 | 
			
		||||
		"SELECT id, version, update_name FROM devices WHERE pid = ?",
 | 
			
		||||
		[pid],
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -171,12 +164,7 @@ 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],
 | 
			
		||||
| 
						 | 
				
			
			@ -195,7 +183,7 @@ app.get("/api/pid/:pid/versions", async (req, res) => {
 | 
			
		|||
		);
 | 
			
		||||
 | 
			
		||||
		results.push({
 | 
			
		||||
			user: user[0],
 | 
			
		||||
			user: device.update_name,
 | 
			
		||||
			version,
 | 
			
		||||
			commands: {
 | 
			
		||||
				inventory: inv.c,
 | 
			
		||||
| 
						 | 
				
			
			@ -219,16 +207,15 @@ app.get("/api/device/:pid/:version/:command", async (req, res) => {
 | 
			
		|||
	if (!device) return res.status(404).json({ message: "Not found" });
 | 
			
		||||
 | 
			
		||||
	const [logs] = await db.query(
 | 
			
		||||
		`SELECT id, filename, output, created_at, is_deleted FROM ${command}_outputs WHERE device_id = ? ORDER BY created_at DESC`,
 | 
			
		||||
		`SELECT id, filename, output, created_at FROM ${command}_outputs WHERE device_id = ? ORDER BY created_at DESC`,
 | 
			
		||||
		[device.id],
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	res.json(logs);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.post("/api/confirm-device", authenticateToken, async (req, res) => {
 | 
			
		||||
	const { pid, version, deletedLogs } = req.body;
 | 
			
		||||
	const updatedBy = req.user?.id;
 | 
			
		||||
app.post("/api/confirm-device", async (req, res) => {
 | 
			
		||||
	const { pid, version, deletedLogs, name, email } = req.body;
 | 
			
		||||
 | 
			
		||||
	if (!pid || !version) {
 | 
			
		||||
		return res.status(400).json({
 | 
			
		||||
| 
						 | 
				
			
			@ -263,9 +250,10 @@ app.post("/api/confirm-device", authenticateToken, async (req, res) => {
 | 
			
		|||
			`UPDATE devices
 | 
			
		||||
				SET is_confirmed = true,
 | 
			
		||||
					updated_at = CURRENT_TIMESTAMP,
 | 
			
		||||
					updated_user = ?
 | 
			
		||||
					update_name = ?,
 | 
			
		||||
					update_email = ?
 | 
			
		||||
				WHERE pid = ? AND version = ?`,
 | 
			
		||||
			[updatedBy, pid, version],
 | 
			
		||||
			[name, email, pid, version],
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		// Delete Log
 | 
			
		||||
| 
						 | 
				
			
			@ -297,11 +285,6 @@ app.post("/api/confirm-device", authenticateToken, async (req, res) => {
 | 
			
		|||
			]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		await db.query(
 | 
			
		||||
			"UPDATE users SET confirm_count = confirm_count + 1 WHERE id = ?",
 | 
			
		||||
			[updatedBy],
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		res.json({
 | 
			
		||||
			success: true,
 | 
			
		||||
			message: "Device confirmed successfully!",
 | 
			
		||||
| 
						 | 
				
			
			@ -315,6 +298,26 @@ app.post("/api/confirm-device", authenticateToken, async (req, res) => {
 | 
			
		|||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
app.post("/api/confirm-count", async (req, res) => {
 | 
			
		||||
	try {
 | 
			
		||||
		const { email } = req.body;
 | 
			
		||||
 | 
			
		||||
		if (!email) {
 | 
			
		||||
			return res.status(400).json({ error: "Email is required" });
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		const [[result]] = await db.query(
 | 
			
		||||
			"SELECT COUNT(*) AS c FROM devices WHERE update_email = ?",
 | 
			
		||||
			[email],
 | 
			
		||||
		);
 | 
			
		||||
 | 
			
		||||
		res.json({ count: result.c });
 | 
			
		||||
	} catch (err) {
 | 
			
		||||
		console.error("Error fetching confirm count:", err);
 | 
			
		||||
		res.status(500).json({ error: "Internal server error" });
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// Danh sách regex lọc lỗi
 | 
			
		||||
const errorPatterns = [
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue