183 lines
4.8 KiB
JavaScript
183 lines
4.8 KiB
JavaScript
import express from "express";
|
|
import fs from "fs";
|
|
import bodyParser from "body-parser";
|
|
import cors from "cors";
|
|
import axios from "axios";
|
|
import path from "path";
|
|
import _ from "lodash";
|
|
|
|
import dotenv from "dotenv";
|
|
|
|
dotenv.config();
|
|
|
|
const metaPath = path.join(process.cwd(), "meta.json");
|
|
const dataFile = "data.json";
|
|
const cachePrefix = "product-cache-";
|
|
const app = express();
|
|
|
|
// Cho phép tất cả origin gọi API
|
|
app.use(cors());
|
|
|
|
// parse JSON body
|
|
app.use(bodyParser.json());
|
|
|
|
// API lưu dữ liệu
|
|
app.post("/sync", (req, res) => {
|
|
const data = req.body;
|
|
|
|
if (!Array.isArray(data)) {
|
|
return res.status(400).json({ error: "Dữ liệu phải là array" });
|
|
}
|
|
|
|
// Chuẩn hoá dữ liệu (có thêm date)
|
|
const mapped = data.map((item) => ({
|
|
...item,
|
|
date: new Date().toISOString(),
|
|
}));
|
|
|
|
// Đọc dữ liệu cũ
|
|
let oldData = [];
|
|
try {
|
|
oldData = JSON.parse(fs.readFileSync(dataFile, "utf-8"));
|
|
} catch (e) {
|
|
oldData = [];
|
|
}
|
|
|
|
// So sánh (bỏ qua field date vì nó luôn khác)
|
|
const stripDate = (arr) => arr.map(({ date, ...rest }) => rest);
|
|
|
|
const oldStripped = stripDate(oldData);
|
|
const newStripped = stripDate(mapped);
|
|
|
|
if (!_.isEqual(oldStripped, newStripped)) {
|
|
// Nếu khác → xoá cache
|
|
const files = fs.readdirSync(".");
|
|
files.forEach((file) => {
|
|
if (file.startsWith(cachePrefix)) {
|
|
fs.unlinkSync(path.join(".", file));
|
|
}
|
|
});
|
|
console.log("Dữ liệu thay đổi → xoá cache");
|
|
} else {
|
|
console.log("Dữ liệu không đổi → giữ cache");
|
|
}
|
|
|
|
// Ghi dữ liệu mới
|
|
fs.writeFileSync(dataFile, JSON.stringify(mapped, null, 2));
|
|
|
|
res.json({ message: "Đã lưu dữ liệu thành công", saved: mapped.length });
|
|
});
|
|
|
|
// API đọc lại dữ liệu
|
|
app.get("/data", (req, res) => {
|
|
if (!fs.existsSync("data.json")) {
|
|
return res.json([]);
|
|
}
|
|
const content = fs.readFileSync("data.json", "utf-8");
|
|
res.json(JSON.parse(content));
|
|
});
|
|
|
|
app.post("/", async (req, res) => {
|
|
try {
|
|
// ===== Helpers =====
|
|
const readJSON = (path, fallback = null) => {
|
|
try {
|
|
return JSON.parse(fs.readFileSync(path, "utf-8"));
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
};
|
|
const writeJSON = (path, data) => {
|
|
try {
|
|
fs.writeFileSync(path, JSON.stringify(data, null, 2));
|
|
} catch (e) {
|
|
console.warn("Không ghi được file:", e.message);
|
|
}
|
|
};
|
|
|
|
// ===== Meta =====
|
|
let meta = readJSON(metaPath, { total: 4000 });
|
|
|
|
const { info, ...filter } = req.body?.filter || {};
|
|
const originalFilter = { ...filter };
|
|
const status = filter?.where?.status_listing;
|
|
|
|
if (["listed", "unlisted"].includes(status)) {
|
|
filter.skip = 0;
|
|
}
|
|
|
|
// ===== Cache check =====
|
|
const cacheFile = `${cachePrefix}${JSON.stringify(originalFilter)}.json`;
|
|
if (fs.existsSync(cacheFile)) {
|
|
return res.json(readJSON(cacheFile, {}));
|
|
}
|
|
|
|
// ===== Load publisted data nếu cần =====
|
|
let publistedData = [];
|
|
if (status && meta.total) {
|
|
filter.limit = meta.total;
|
|
publistedData = readJSON("data.json", []);
|
|
}
|
|
|
|
// ===== Call API gốc =====
|
|
const { data } = await axios({
|
|
headers: { Authorization: req.headers.authorization },
|
|
url: "transferGetData",
|
|
baseURL: process.env.BASE_URL,
|
|
method: "POST",
|
|
data: { ...req.body, filter },
|
|
});
|
|
|
|
// Update meta
|
|
if (typeof data.total === "number") {
|
|
writeJSON(metaPath, { total: data.total });
|
|
}
|
|
|
|
// ===== Xử lý listed/unlisted =====
|
|
if (status === "listed" || status === "unlisted") {
|
|
const skip = originalFilter.skip || 0;
|
|
const limit = originalFilter.limit || data.data.length;
|
|
|
|
let filteredData = data.data.filter((item) => {
|
|
const key = item?.title.includes(item.code)
|
|
? `${item.title}`.toLowerCase()
|
|
: `${item.title} - ${item.code}`.toLowerCase();
|
|
|
|
if (status === "listed") {
|
|
return publistedData.some(
|
|
(i) =>
|
|
`${i.title}`.toLowerCase().includes(key) &&
|
|
i?.url_info == info?.url
|
|
);
|
|
} else {
|
|
return !publistedData.some(
|
|
(i) =>
|
|
`${i.title}`.toLowerCase().includes(key) &&
|
|
i?.url_info == info?.url
|
|
);
|
|
}
|
|
});
|
|
|
|
data.total = filteredData.length;
|
|
data.data = filteredData.slice(skip, skip + limit);
|
|
data.filter = { ...originalFilter, skip, limit };
|
|
|
|
writeJSON(cacheFile, data);
|
|
}
|
|
|
|
res.json(data);
|
|
} catch (err) {
|
|
console.error("API error:", err.message);
|
|
res.status(500).json({ error: err.message });
|
|
}
|
|
});
|
|
|
|
// Route trả về privacy policy
|
|
app.get("/policy", (req, res) => {
|
|
res.sendFile(path.join(process.cwd(), "views", "policy.html"));
|
|
});
|
|
|
|
app.listen(3000, () => {
|
|
console.log("Server chạy tại http://localhost:3000");
|
|
});
|