image
This commit is contained in:
@@ -4,7 +4,7 @@ import { redirect } from 'next/navigation';
|
|||||||
import { Trash2, Plus, Image as ImageIcon } from 'lucide-react';
|
import { Trash2, Plus, Image as ImageIcon } from 'lucide-react';
|
||||||
import { addGalleryItem, deleteGalleryItem } from '../actions';
|
import { addGalleryItem, deleteGalleryItem } from '../actions';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { uploadImage } from '@/lib/cloudinary';
|
import { uploadImage } from '@/lib/openinary';
|
||||||
|
|
||||||
export default async function GalleryAdminPage() {
|
export default async function GalleryAdminPage() {
|
||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { redirect } from 'next/navigation';
|
|||||||
import { Image as ImageIcon, Video, Upload } from 'lucide-react';
|
import { Image as ImageIcon, Video, Upload } from 'lucide-react';
|
||||||
import { updateHeroMedia } from '../actions';
|
import { updateHeroMedia } from '../actions';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { uploadImage } from '@/lib/cloudinary';
|
import { uploadImage } from '@/lib/openinary';
|
||||||
|
|
||||||
export default async function HeroAdminPage() {
|
export default async function HeroAdminPage() {
|
||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { redirect } from 'next/navigation';
|
|||||||
import { Trash2, Plus, Briefcase, Edit, X } from 'lucide-react';
|
import { Trash2, Plus, Briefcase, Edit, X } from 'lucide-react';
|
||||||
import { addServiceItem, deleteServiceItem, updateServiceItem } from '../actions';
|
import { addServiceItem, deleteServiceItem, updateServiceItem } from '../actions';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { uploadImage } from '@/lib/cloudinary';
|
import { uploadImage } from '@/lib/openinary';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
|
|
||||||
export default async function ServicesAdminPage({ searchParams }: { searchParams: Promise<{ edit?: string }> }) {
|
export default async function ServicesAdminPage({ searchParams }: { searchParams: Promise<{ edit?: string }> }) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { redirect } from 'next/navigation'
|
|||||||
import { Save } from 'lucide-react';
|
import { Save } from 'lucide-react';
|
||||||
import { updateSiteSettings } from '../actions';
|
import { updateSiteSettings } from '../actions';
|
||||||
import { revalidatePath } from 'next/cache';
|
import { revalidatePath } from 'next/cache';
|
||||||
import { uploadImage } from '@/lib/cloudinary';
|
import { uploadImage } from '@/lib/openinary';
|
||||||
|
|
||||||
export default async function SettingsAdminPage() {
|
export default async function SettingsAdminPage() {
|
||||||
const session = await getSession();
|
const session = await getSession();
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
import { v2 as cloudinary } from 'cloudinary';
|
|
||||||
|
|
||||||
export async function uploadImage(file: File): Promise<string> {
|
|
||||||
const arrayBuffer = await file.arrayBuffer();
|
|
||||||
const buffer = Buffer.from(arrayBuffer);
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const uploadStream = cloudinary.uploader.upload_stream(
|
|
||||||
{ folder: 'moybeach', resource_type: 'auto' },
|
|
||||||
(error, result) => {
|
|
||||||
if (error) {
|
|
||||||
reject(error);
|
|
||||||
} else {
|
|
||||||
resolve(result!.secure_url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
uploadStream.end(buffer);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export async function uploadToOpeninary(file: File, folder: string = "uploads") {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("files", file);
|
||||||
|
formData.append("folder", folder);
|
||||||
|
|
||||||
|
const res = await fetch(`${process.env.OPENINARY_API_URL}/api/upload`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: { Authorization: `Bearer ${process.env.OPENINARY_API_KEY}` },
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
console.error("Openinary upload error:", text);
|
||||||
|
throw new Error("Upload başarısız");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return data.files[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function uploadImage(file: File): Promise<string> {
|
||||||
|
const fileData = await uploadToOpeninary(file, "moybeach");
|
||||||
|
return `${process.env.NEXT_PUBLIC_OPENINARY_URL}${fileData.url}`;
|
||||||
|
}
|
||||||
@@ -12,6 +12,10 @@ const nextConfig: NextConfig = {
|
|||||||
protocol: "https",
|
protocol: "https",
|
||||||
hostname: "res.cloudinary.com",
|
hostname: "res.cloudinary.com",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "media.ayris.tech",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
experimental: {
|
experimental: {
|
||||||
|
|||||||
Generated
-19
@@ -10,7 +10,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"cloudinary": "^2.10.0",
|
|
||||||
"framer-motion": "^12.40.0",
|
"framer-motion": "^12.40.0",
|
||||||
"jose": "^6.2.3",
|
"jose": "^6.2.3",
|
||||||
"lucide-react": "^1.17.0",
|
"lucide-react": "^1.17.0",
|
||||||
@@ -3283,18 +3282,6 @@
|
|||||||
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
"integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/cloudinary": {
|
|
||||||
"version": "2.10.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/cloudinary/-/cloudinary-2.10.0.tgz",
|
|
||||||
"integrity": "sha512-sY09kYg7wprkndAOjZBAYqFZqwL+SxnEGcAvksOvFA+5upnFn949UjkEkHKNSwkBtW/xRDd0p6NgbSXZcxkI3w==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"lodash": "^4.17.23"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=9"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||||
@@ -5631,12 +5618,6 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lodash": {
|
|
||||||
"version": "4.18.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
|
|
||||||
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@prisma/client": "^6.4.1",
|
"@prisma/client": "^6.4.1",
|
||||||
"bcryptjs": "^3.0.3",
|
"bcryptjs": "^3.0.3",
|
||||||
"cloudinary": "^2.10.0",
|
|
||||||
"framer-motion": "^12.40.0",
|
"framer-motion": "^12.40.0",
|
||||||
"jose": "^6.2.3",
|
"jose": "^6.2.3",
|
||||||
"lucide-react": "^1.17.0",
|
"lucide-react": "^1.17.0",
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
const OPENINARY_API_URL = process.env.OPENINARY_API_URL;
|
||||||
|
const OPENINARY_API_KEY = process.env.OPENINARY_API_KEY;
|
||||||
|
const NEXT_PUBLIC_OPENINARY_URL = process.env.NEXT_PUBLIC_OPENINARY_URL;
|
||||||
|
|
||||||
|
async function downloadImageAsBlob(url: string, filename: string): Promise<Blob> {
|
||||||
|
const response = await fetch(url);
|
||||||
|
if (!response.ok) throw new Error(`Failed to download ${url}`);
|
||||||
|
const arrayBuffer = await response.arrayBuffer();
|
||||||
|
|
||||||
|
// Create a Blob from the ArrayBuffer
|
||||||
|
return new Blob([arrayBuffer], { type: response.headers.get('content-type') || 'application/octet-stream' });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function uploadToOpeninary(blob: Blob, filename: string): Promise<string> {
|
||||||
|
const formData = new FormData();
|
||||||
|
// Next.js node fetch environment allows passing Blob to FormData
|
||||||
|
formData.append('files', blob, filename);
|
||||||
|
formData.append('folder', 'moybeach');
|
||||||
|
|
||||||
|
const res = await fetch(`${OPENINARY_API_URL}/api/upload`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { Authorization: `Bearer ${OPENINARY_API_KEY}` },
|
||||||
|
body: formData,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
console.error('Openinary upload error:', text);
|
||||||
|
throw new Error('Upload başarısız');
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await res.json();
|
||||||
|
return `${NEXT_PUBLIC_OPENINARY_URL}${data.files[0].url}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function migrateImage(url: string | null): Promise<string | null> {
|
||||||
|
if (!url || !url.includes('cloudinary.com')) return url;
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`Migrating: ${url}`);
|
||||||
|
const filename = url.split('/').pop() || 'image.jpg';
|
||||||
|
const blob = await downloadImageAsBlob(url, filename);
|
||||||
|
const newUrl = await uploadToOpeninary(blob, filename);
|
||||||
|
console.log(`Success: ${newUrl}`);
|
||||||
|
return newUrl;
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error migrating ${url}:`, error);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
if (!OPENINARY_API_URL || !OPENINARY_API_KEY) {
|
||||||
|
console.error('Missing Openinary Env Variables!');
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Starting migration from Cloudinary to Openinary...');
|
||||||
|
|
||||||
|
// 1. Gallery
|
||||||
|
const galleries = await prisma.gallery.findMany();
|
||||||
|
for (const item of galleries) {
|
||||||
|
if (item.imageUrl.includes('cloudinary.com')) {
|
||||||
|
const newUrl = await migrateImage(item.imageUrl);
|
||||||
|
if (newUrl && newUrl !== item.imageUrl) {
|
||||||
|
await prisma.gallery.update({ where: { id: item.id }, data: { imageUrl: newUrl } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Service
|
||||||
|
const services = await prisma.service.findMany();
|
||||||
|
for (const item of services) {
|
||||||
|
if (item.iconUrl && item.iconUrl.includes('cloudinary.com')) {
|
||||||
|
const newUrl = await migrateImage(item.iconUrl);
|
||||||
|
if (newUrl && newUrl !== item.iconUrl) {
|
||||||
|
await prisma.service.update({ where: { id: item.id }, data: { iconUrl: newUrl } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. HeroMedia
|
||||||
|
const heroMedias = await prisma.heroMedia.findMany();
|
||||||
|
for (const item of heroMedias) {
|
||||||
|
if (item.url.includes('cloudinary.com')) {
|
||||||
|
const newUrl = await migrateImage(item.url);
|
||||||
|
if (newUrl && newUrl !== item.url) {
|
||||||
|
await prisma.heroMedia.update({ where: { id: item.id }, data: { url: newUrl } });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. SiteSettings
|
||||||
|
const siteSettings = await prisma.siteSettings.findMany();
|
||||||
|
for (const item of siteSettings) {
|
||||||
|
let changed = false;
|
||||||
|
let newLogoUrl = item.logoUrl;
|
||||||
|
let newOgImage = item.ogImage;
|
||||||
|
|
||||||
|
if (item.logoUrl && item.logoUrl.includes('cloudinary.com')) {
|
||||||
|
newLogoUrl = await migrateImage(item.logoUrl) ?? item.logoUrl;
|
||||||
|
if (newLogoUrl !== item.logoUrl) changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.ogImage && item.ogImage.includes('cloudinary.com')) {
|
||||||
|
newOgImage = await migrateImage(item.ogImage) ?? item.ogImage;
|
||||||
|
if (newOgImage !== item.ogImage) changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
await prisma.siteSettings.update({
|
||||||
|
where: { id: item.id },
|
||||||
|
data: { logoUrl: newLogoUrl, ogImage: newOgImage }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Migration completed!');
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.catch(e => {
|
||||||
|
console.error(e);
|
||||||
|
process.exit(1);
|
||||||
|
})
|
||||||
|
.finally(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
});
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
const cloudinary = require('cloudinary').v2;
|
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
cloudinary.config({
|
|
||||||
cloud_name: 'du7xohbct',
|
|
||||||
api_key: '525922573613433',
|
|
||||||
api_secret: 'cJ0NDcaoQhSTAxBMv6jNMFupt3k'
|
|
||||||
});
|
|
||||||
|
|
||||||
const uploadImages = async () => {
|
|
||||||
const foldersToUpload = ['kitehotel', 'kitesurf'];
|
|
||||||
const results = {};
|
|
||||||
|
|
||||||
for (const folderName of foldersToUpload) {
|
|
||||||
const publicDir = path.join(__dirname, '..', 'public', folderName);
|
|
||||||
|
|
||||||
if (!fs.existsSync(publicDir)) {
|
|
||||||
console.log(`Directory does not exist: ${publicDir}`);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const files = fs.readdirSync(publicDir);
|
|
||||||
const images = files.filter(file => file.endsWith('.jpg') || file.endsWith('.jpeg') || file.endsWith('.png') || file.endsWith('.webp'));
|
|
||||||
|
|
||||||
for (const file of images) {
|
|
||||||
console.log(`Uploading ${folderName}/${file}...`);
|
|
||||||
try {
|
|
||||||
const filePath = path.join(publicDir, file);
|
|
||||||
const result = await cloudinary.uploader.upload(filePath, {
|
|
||||||
folder: `moygrup/${folderName}`,
|
|
||||||
use_filename: true,
|
|
||||||
unique_filename: false
|
|
||||||
});
|
|
||||||
console.log(`Uploaded ${file} -> ${result.secure_url}`);
|
|
||||||
results[`${folderName}/${file}`] = result.secure_url;
|
|
||||||
} catch (error) {
|
|
||||||
console.error(`Error uploading ${folderName}/${file}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('\n--- Upload Results ---');
|
|
||||||
console.log(JSON.stringify(results, null, 2));
|
|
||||||
};
|
|
||||||
|
|
||||||
uploadImages();
|
|
||||||
Reference in New Issue
Block a user