feat:优化数据
This commit is contained in:
107
server.js
107
server.js
@@ -6,8 +6,9 @@ const crypto = require("crypto");
|
||||
const app = express();
|
||||
const PORT = process.env.PORT || 5180;
|
||||
const DB_PATH = path.join(__dirname, "data", "shaders.json");
|
||||
const THUMB_DIR = path.join(__dirname, "data", "thumbnails");
|
||||
|
||||
app.use(express.json({ limit: "1mb" }));
|
||||
app.use(express.json({ limit: "4mb" }));
|
||||
app.use(express.static(__dirname));
|
||||
|
||||
async function ensureDb() {
|
||||
@@ -19,6 +20,50 @@ async function ensureDb() {
|
||||
}
|
||||
}
|
||||
|
||||
async function ensureThumbnailsDir() {
|
||||
await fs.mkdir(THUMB_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
function thumbPath(id) {
|
||||
return path.join(THUMB_DIR, `${id}.png`);
|
||||
}
|
||||
|
||||
async function thumbFileExists(id) {
|
||||
try {
|
||||
await fs.access(thumbPath(id));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function decodePngBase64(s) {
|
||||
if (!s || typeof s !== "string") return null;
|
||||
const t = s.trim();
|
||||
const m = /^data:image\/png;base64,(.+)$/i.exec(t);
|
||||
const b64 = m ? m[1] : t.replace(/\s/g, "");
|
||||
try {
|
||||
return Buffer.from(b64, "base64");
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function isPngBuffer(buf) {
|
||||
return (
|
||||
buf &&
|
||||
buf.length >= 8 &&
|
||||
buf[0] === 0x89 &&
|
||||
buf[1] === 0x50 &&
|
||||
buf[2] === 0x4e &&
|
||||
buf[3] === 0x47 &&
|
||||
buf[4] === 0x0d &&
|
||||
buf[5] === 0x0a &&
|
||||
buf[6] === 0x1a &&
|
||||
buf[7] === 0x0a
|
||||
);
|
||||
}
|
||||
|
||||
async function readDb() {
|
||||
await ensureDb();
|
||||
const raw = await fs.readFile(DB_PATH, "utf8");
|
||||
@@ -286,6 +331,7 @@ async function autoNormalizeStoredShaders() {
|
||||
|
||||
app.get("/api/shaders", async (_req, res) => {
|
||||
try {
|
||||
await ensureThumbnailsDir();
|
||||
let shaders = await readDb();
|
||||
// Runtime safeguard: always return normalized code for display layer.
|
||||
shaders = shaders.map((item) => {
|
||||
@@ -297,7 +343,16 @@ app.get("/api/shaders", async (_req, res) => {
|
||||
if (isAngleLike(raw)) next.sourceFormat = "angle-metal-auto-converted";
|
||||
return next;
|
||||
});
|
||||
res.json(shaders);
|
||||
const out = await Promise.all(
|
||||
shaders.map(async (item) => {
|
||||
const has = await thumbFileExists(item.id);
|
||||
const thumbnailUrl = has
|
||||
? `/api/shaders/${encodeURIComponent(item.id)}/thumbnail`
|
||||
: null;
|
||||
return { ...item, thumbnailUrl };
|
||||
})
|
||||
);
|
||||
res.json(out);
|
||||
} catch {
|
||||
res.status(500).json({ error: "读取失败" });
|
||||
}
|
||||
@@ -333,6 +388,48 @@ app.post("/api/shaders", async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
app.get("/api/shaders/:id/thumbnail", async (req, res) => {
|
||||
try {
|
||||
const p = thumbPath(req.params.id);
|
||||
await fs.access(p);
|
||||
res.setHeader("Content-Type", "image/png");
|
||||
res.setHeader("Cache-Control", "public, max-age=86400");
|
||||
res.sendFile(path.resolve(p));
|
||||
} catch {
|
||||
res.status(404).end();
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/shaders/:id/thumbnail", async (req, res) => {
|
||||
const { pngBase64 } = req.body || {};
|
||||
const buf = decodePngBase64(pngBase64);
|
||||
if (!buf || buf.length < 32) {
|
||||
return res.status(400).json({ error: "无效 PNG 数据" });
|
||||
}
|
||||
if (!isPngBuffer(buf)) {
|
||||
return res.status(400).json({ error: "必须是 PNG" });
|
||||
}
|
||||
if (buf.length > 2 * 1024 * 1024) {
|
||||
return res.status(400).json({ error: "缩略图过大" });
|
||||
}
|
||||
try {
|
||||
const shaders = await readDb();
|
||||
const idx = shaders.findIndex((it) => it.id === req.params.id);
|
||||
if (idx < 0) {
|
||||
return res.status(404).json({ error: "未找到该 shader" });
|
||||
}
|
||||
await ensureThumbnailsDir();
|
||||
await fs.writeFile(thumbPath(req.params.id), buf);
|
||||
const thumbnailAt = new Date().toISOString();
|
||||
shaders[idx] = { ...shaders[idx], thumbnailAt };
|
||||
await writeDb(shaders);
|
||||
const thumbnailUrl = `/api/shaders/${encodeURIComponent(req.params.id)}/thumbnail`;
|
||||
res.json({ ok: true, thumbnailUrl, thumbnailAt });
|
||||
} catch {
|
||||
res.status(500).json({ error: "保存缩略图失败" });
|
||||
}
|
||||
});
|
||||
|
||||
app.delete("/api/shaders/:id", async (req, res) => {
|
||||
try {
|
||||
const shaders = await readDb();
|
||||
@@ -341,6 +438,12 @@ app.delete("/api/shaders/:id", async (req, res) => {
|
||||
return res.status(404).json({ error: "未找到该 shader" });
|
||||
}
|
||||
await writeDb(next);
|
||||
await ensureThumbnailsDir();
|
||||
try {
|
||||
await fs.unlink(thumbPath(req.params.id));
|
||||
} catch (_) {
|
||||
/* no thumb */
|
||||
}
|
||||
res.status(204).end();
|
||||
} catch {
|
||||
res.status(500).json({ error: "删除失败" });
|
||||
|
||||
Reference in New Issue
Block a user