myapps_remote_server_ui_app.../server/backend/routes/links.js
2026-01-05 22:50:31 +01:00

162 lines
3.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import express from "express";
import multer from "multer";
import axios from "axios";
import sharp from "sharp";
import Link from "../models/Link.js";
import { authMiddleware } from "../middleware/auth.js";
import { parseICO } from "icojs";
const router = express.Router();
// Multer in-memory (niente filesystem)
const upload = multer({ storage: multer.memoryStorage() });
router.use(authMiddleware);
// Scarica immagine remota come Buffer
async function downloadImageAsBuffer(url) {
const response = await axios.get(url, {
responseType: "arraybuffer",
maxRedirects: 5,
headers: {
"User-Agent": "Mozilla/5.0",
"Accept": "image/*"
}
});
return {
buffer: Buffer.from(response.data),
mime: response.headers["content-type"] || ""
};
}
// Converte immagine → WebP 128x128 contain
async function processIcon(buffer, mime) {
let inputBuffer = buffer;
// Se è ICO → converti in PNG
if (mime === "image/x-icon" || mime === "image/vnd.microsoft.icon") {
const images = await parseICO(buffer);
if (!images.length) {
throw new Error("ICO non valido");
}
// Prendiamo limmagine più grande dentro lICO
const best = images.reduce((a, b) => (a.width > b.width ? a : b));
inputBuffer = Buffer.from(best.buffer);
}
// Ora Sharp può lavorare
return await sharp(inputBuffer)
.resize(128, 128, {
fit: "contain",
background: { r: 0, g: 0, b: 0, alpha: 0 }
})
.webp({ quality: 90 })
.toBuffer();
}
// ===============================
// GET LINKS
// ===============================
router.get("/", async (req, res) => {
const links = await Link.find({ owner: req.userId });
res.json(links);
});
// ===============================
// CREATE LINK
// ===============================
router.post("/", upload.single("icon"), async (req, res) => {
const { url, name, iconURL } = req.body;
let originalBuffer = null;
// Caso 1: upload file
if (req.file) {
originalBuffer = req.file.buffer;
}
// Caso 2: URL remoto
else if (iconURL) {
originalBuffer = await downloadImageAsBuffer(iconURL);
}
let processedIcon = null;
if (originalBuffer) {
processedIcon = await processIcon(originalBuffer.buffer, originalBuffer.mime);
}
const link = await Link.create({
url,
name,
owner: req.userId,
icon: processedIcon
? {
data: processedIcon,
mime: "image/webp",
size: processedIcon.length
}
: null
});
res.json(link);
});
// ===============================
// UPDATE LINK
// ===============================
router.put("/:id", upload.single("icon"), async (req, res) => {
const { id } = req.params;
const { name, url, iconURL } = req.body;
const link = await Link.findOne({ _id: id, owner: req.userId });
if (!link) return res.status(404).json({ error: "Link non trovato" });
let originalBuffer = null;
if (req.file) {
originalBuffer = req.file.buffer;
} else if (iconURL) {
originalBuffer = await downloadImageAsBuffer(iconURL);
}
const update = { name, url };
if (originalBuffer) {
const processedIcon = await processIcon(originalBuffer.buffer, originalBuffer.mime);
update.icon = {
data: processedIcon,
mime: "image/webp",
size: processedIcon.length
};
}
const updated = await Link.findOneAndUpdate(
{ _id: id, owner: req.userId },
update,
{ new: true }
);
res.json(updated);
});
// ===============================
// DELETE LINK
// ===============================
router.delete("/:id", async (req, res) => {
const link = await Link.findOneAndDelete({
_id: req.params.id,
owner: req.userId
});
if (!link) return res.status(404).json({ error: "Link non trovato" });
res.json({ success: true });
});
export default router;