Update admin gallery to fetch images directly from Cloudinary

Integrates Cloudinary API to fetch image details for the admin gallery page, replacing previous data sources.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 23852c00-4779-460a-9e0c-d09fee4b6c92
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: eb361b34-3147-44e1-a7c3-11100d3e83b8
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/23852c00-4779-460a-9e0c-d09fee4b6c92/ncMMRQ9
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic
2026-03-06 10:55:08 +00:00
parent 9d8d29208a
commit d9278f6fb6
4 changed files with 203 additions and 55 deletions
+57
View File
@@ -116,6 +116,63 @@ export async function listCloudinaryGallery(): Promise<string[]> {
}
}
export interface CloudinaryResourceInfo {
publicId: string;
fileName: string;
artist: string;
width: number;
height: number;
format: string;
bytes: number;
createdAt: string;
thumb: string;
large: string;
mobile: string;
full: string;
}
export async function listCloudinaryGalleryDetailed(): Promise<CloudinaryResourceInfo[]> {
try {
let allResources: any[] = [];
let nextCursor: string | undefined;
do {
const result = await cloudinary.api.resources({
type: "upload",
prefix: GALLERY_FOLDER + "/",
max_results: 500,
resource_type: "image",
...(nextCursor ? { next_cursor: nextCursor } : {}),
});
allResources = allResources.concat(result.resources);
nextCursor = result.next_cursor;
} while (nextCursor);
return allResources.map((r: any) => {
const urls = generateImageUrls(r.public_id);
const rawName = r.public_id.replace(`${GALLERY_FOLDER}/`, "").replace(/_/g, " ");
let originalFileName = rawName;
const ext = r.format || "jpg";
originalFileName = rawName + "." + ext;
return {
publicId: r.public_id,
fileName: originalFileName,
artist: extractArtistFromPublicId(r.public_id),
width: r.width,
height: r.height,
format: r.format,
bytes: r.bytes,
createdAt: r.created_at,
...urls,
};
});
} catch (err: any) {
console.error("Cloudinary detailed list failed:", err.message);
return [];
}
}
export function extractArtistFromPublicId(publicId: string): string {
const name = publicId.replace(`${GALLERY_FOLDER}/`, "").replace(/_/g, " ");
if (/^DSC\d/i.test(name) || /^IMG[\s_]\d/i.test(name)) return "";
+8
View File
@@ -2,5 +2,13 @@
"DSC07135.jpg": {
"x": 50,
"y": 30
},
"Arina 1.jpg": {
"x": 79,
"y": 56
},
"Anita Hofmann.jpg": {
"x": 42,
"y": 13
}
}
+23
View File
@@ -7,6 +7,7 @@ import { generateDailyHoroscopes, getHoroscopesForToday, getOrGenerateHoroscope
import { analyzeAllArticleImages, getCachedFocalPoints } from "./focal-point";
import { optimizeImage } from "./image-optimizer";
import { getAuthUrl, exchangeCodeForTokens, isConnected, fetchGalleryFromDropbox, getValidAccessToken, migrateGalleryToCloudinary } from "./dropbox";
import { listCloudinaryGalleryDetailed } from "./cloudinary";
import multer from "multer";
import path from "path";
import fs from "fs";
@@ -411,6 +412,28 @@ export async function registerRoutes(
}
});
app.get("/api/admin/cloudinary-gallery", async (_req, res) => {
try {
const images = await listCloudinaryGalleryDetailed();
const aPath = path.join(process.cwd(), "server/gallery-artist-overrides.json");
let artistOverrides: Record<string, string> = {};
if (fs.existsSync(aPath)) {
artistOverrides = JSON.parse(fs.readFileSync(aPath, "utf-8"));
}
const result = images.map((img) => {
const overriddenArtist = artistOverrides[img.fileName];
return {
...img,
artist: overriddenArtist || img.artist,
artistOverridden: !!overriddenArtist,
};
});
res.json(result);
} catch (err: any) {
res.status(500).json({ message: err.message });
}
});
app.get("/api/gallery/artists", (_req, res) => {
try {
const aPath = path.join(process.cwd(), "server/gallery-artist-overrides.json");