143 lines
4.8 KiB
TypeScript
143 lines
4.8 KiB
TypeScript
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());
|