Further truncate Openinary paths to 25 chars to prevent Next.js cache ENAMETOOLONG errors

This commit is contained in:
AyrisAI
2026-07-17 13:45:28 +03:00
parent da2029452d
commit a15b42f9f9
2 changed files with 148 additions and 6 deletions
+142
View File
@@ -0,0 +1,142 @@
import { PrismaClient } from '@prisma/client'
import crypto from 'crypto'
import fs from 'fs'
import path from 'path'
import axios from 'axios'
import FormData from 'form-data'
const prisma = new PrismaClient()
const baseUrl = process.env.OPENINARY_API_URL?.replace(/\/+$/, '') || 'https://media.ayris.tech';
const key = process.env.OPENINARY_API_KEY;
function getSafeFolder(folder: string): string {
return folder
.split('/')
.filter(Boolean)
.map(part => {
if (part.length > 25) {
const hash = crypto.createHash('md5').update(part).digest('hex').substring(0, 5);
return `${part.substring(0, 20)}-${hash}`;
}
return part;
})
.join('/');
}
function getSafeFileName(fileName: string): string {
const lastDotIndex = fileName.lastIndexOf('.');
const ext = lastDotIndex !== -1 ? fileName.substring(lastDotIndex) : '';
let nameWithoutExt = lastDotIndex !== -1 ? fileName.substring(0, lastDotIndex) : fileName;
if (nameWithoutExt.length > 25) {
const hash = crypto.createHash('md5').update(nameWithoutExt).digest('hex').substring(0, 5);
nameWithoutExt = `${nameWithoutExt.substring(0, 20)}-${hash}`;
}
return `${nameWithoutExt}${ext}`;
}
async function uploadFile(filePath: string, folder: string, safeFileName: string): Promise<string> {
const form = new FormData();
form.append('files', fs.createReadStream(filePath), { filename: safeFileName, contentType: 'image/jpeg' });
form.append('folder', folder);
const res = await axios.post(`${baseUrl}/api/upload`, form, {
headers: {
...form.getHeaders(),
"Authorization": `Bearer ${key}`
}
});
const data = res.data;
if (Array.isArray(data) && data.length > 0) {
const fileUrl = data[0].url || data[0].secure_url;
return fileUrl.startsWith('/') ? `${baseUrl}${fileUrl}` : fileUrl;
} else if (data && typeof data === "object") {
const fileUrl = data.url || data.secure_url || (data.files && data.files[0]?.url);
return fileUrl.startsWith('/') ? `${baseUrl}${fileUrl}` : fileUrl;
}
throw new Error("Invalid response format");
}
async function processUrl(url: string): Promise<string | null> {
if (!url || !url.startsWith(baseUrl)) return null;
try {
const urlObj = new URL(url);
const pathParts = urlObj.pathname.split('/').filter(Boolean);
// Check if it has a part longer than 25 characters
const isBad = pathParts.some(p => p.length > 25);
if (!isBad) return null;
console.log(`Found long URL: ${url}`);
const originalFileName = decodeURIComponent(pathParts.pop() || 'image.jpg');
let folderParts = pathParts;
if (folderParts[0] === 't') {
folderParts.shift();
}
const originalFolder = decodeURIComponent(folderParts.join('/'));
const safeFolder = getSafeFolder(originalFolder);
const safeFileName = getSafeFileName(originalFileName);
if (safeFolder === originalFolder && safeFileName === originalFileName) {
return null;
}
console.log(`-> Safe folder: ${safeFolder}`);
console.log(`-> Safe filename: ${safeFileName}`);
const res = await axios.get(url, { responseType: 'arraybuffer' });
const buffer = Buffer.from(res.data);
const tmpPath = path.join('/tmp', safeFileName);
fs.writeFileSync(tmpPath, buffer);
const newUrl = await uploadFile(tmpPath, safeFolder, safeFileName);
console.log(`-> New URL: ${newUrl}`);
fs.unlinkSync(tmpPath);
return newUrl;
} catch (error) {
console.error(`Error processing ${url}:`, error.message);
return null;
}
}
async function main() {
const galleries = await prisma.gallery.findMany();
for (const g of galleries) {
const newUrl = await processUrl(g.url);
if (newUrl) await prisma.gallery.update({ where: { id: g.id }, data: { url: newUrl }});
}
const posts = await prisma.blogPost.findMany({ where: { coverImage: { not: null } } });
for (const p of posts) {
if (p.coverImage) {
const newUrl = await processUrl(p.coverImage);
if (newUrl) await prisma.blogPost.update({ where: { id: p.id }, data: { coverImage: newUrl }});
}
}
const events = await prisma.event.findMany({ where: { coverImage: { not: null } } });
for (const e of events) {
if (e.coverImage) {
const newUrl = await processUrl(e.coverImage);
if (newUrl) await prisma.event.update({ where: { id: e.id }, data: { coverImage: newUrl }});
}
}
const collections = await prisma.collection.findMany({ where: { coverImage: { not: null } } });
for (const c of collections) {
if (c.coverImage) {
const newUrl = await processUrl(c.coverImage);
if (newUrl) await prisma.collection.update({ where: { id: c.id }, data: { coverImage: newUrl }});
}
}
}
main().catch(console.error).finally(() => prisma.$disconnect());