auto-post-marketplace-facebook/server/server.js

168 lines
4.4 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 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;
const listedCodes = new Set(
publistedData.map((i) => (i.title + i.price || "").trim().toLowerCase())
);
let filteredData = data.data.filter((item) => {
const key = `${item.name} - ${item.code}${item.price}`.toLowerCase();
return status === "listed"
? listedCodes.has(key)
: !listedCodes.has(key);
});
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 });
}
});
app.listen(3000, () => {
console.log("Server chạy tại http://localhost:3000");
});