162 lines
3.8 KiB
JavaScript
162 lines
3.8 KiB
JavaScript
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 l’immagine più grande dentro l’ICO
|
||
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;
|