Further truncate Openinary paths to 25 chars to prevent Next.js cache ENAMETOOLONG errors
This commit is contained in:
@@ -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());
|
||||||
+6
-6
@@ -14,9 +14,9 @@ export async function uploadToOpeninary(file: File, folder: string = "marmarislo
|
|||||||
.split('/')
|
.split('/')
|
||||||
.filter(Boolean) // Avoid double slashes in folder path
|
.filter(Boolean) // Avoid double slashes in folder path
|
||||||
.map(part => {
|
.map(part => {
|
||||||
if (part.length > 50) {
|
if (part.length > 25) {
|
||||||
const hash = crypto.createHash('md5').update(part).digest('hex').substring(0, 8);
|
const hash = crypto.createHash('md5').update(part).digest('hex').substring(0, 5);
|
||||||
return `${part.substring(0, 40)}-${hash}`;
|
return `${part.substring(0, 20)}-${hash}`;
|
||||||
}
|
}
|
||||||
return part;
|
return part;
|
||||||
})
|
})
|
||||||
@@ -27,9 +27,9 @@ export async function uploadToOpeninary(file: File, folder: string = "marmarislo
|
|||||||
const ext = lastDotIndex !== -1 ? file.name.substring(lastDotIndex) : '';
|
const ext = lastDotIndex !== -1 ? file.name.substring(lastDotIndex) : '';
|
||||||
let nameWithoutExt = lastDotIndex !== -1 ? file.name.substring(0, lastDotIndex) : file.name;
|
let nameWithoutExt = lastDotIndex !== -1 ? file.name.substring(0, lastDotIndex) : file.name;
|
||||||
|
|
||||||
if (nameWithoutExt.length > 50) {
|
if (nameWithoutExt.length > 25) {
|
||||||
const hash = crypto.createHash('md5').update(nameWithoutExt).digest('hex').substring(0, 8);
|
const hash = crypto.createHash('md5').update(nameWithoutExt).digest('hex').substring(0, 5);
|
||||||
nameWithoutExt = `${nameWithoutExt.substring(0, 40)}-${hash}`;
|
nameWithoutExt = `${nameWithoutExt.substring(0, 20)}-${hash}`;
|
||||||
}
|
}
|
||||||
const safeFileName = `${nameWithoutExt}${ext}`;
|
const safeFileName = `${nameWithoutExt}${ext}`;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user