feat:优化数据

This commit is contained in:
Daniel
2026-04-02 10:53:36 +08:00
parent a5bf2adad9
commit 0304805ce1
44 changed files with 902 additions and 392 deletions

107
server.js
View File

@@ -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: "删除失败" });