Improve 3D gallery and debug checkup workflow for image curation.

Add parquet floor, museum-style exit, movement-tinted walls, and gold/black
frames with gallery sync after Fix it. Debug panel gets Checked and Fix it
buttons; documentation and image-fetch reliability updates included.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Danila Khodjaef
2026-06-20 22:05:24 +03:00
co-authored by Cursor
parent 4c6acd5a3a
commit 019ce4e136
67 changed files with 1061 additions and 190 deletions
+18 -6
View File
@@ -6,7 +6,7 @@ require('dotenv').config();
const pool = require('./db');
const { ensurePaintingImages, preloadArtistImagesLocal, replacePaintingImageFromUrl, IMAGE_DIR } = require('./image-service');
const { searchGoogleImagesFirst, fetchImageBuffer, pickExt } = require('../scripts/image-fetcher');
const { searchGoogleImagesFirst, fetchImageBuffer, friendlyImageFetchError, pickExt } = require('../scripts/image-fetcher');
const app = express();
const PORT = process.env.PORT || 3001;
@@ -240,7 +240,7 @@ app.get('/api/artists/:id', async (req, res) => {
const { id } = req.params;
const [artist, periods, paintings] = await Promise.all([
pool.query(
`SELECT a.*, m.name as movement_name
`SELECT a.*, m.name as movement_name, m.color as movement_color
FROM artists a
LEFT JOIN art_movements m ON a.movement_id = m.id
WHERE a.id = $1`,
@@ -252,6 +252,8 @@ app.get('/api/artists/:id', async (req, res) => {
),
pool.query(
`SELECT p.*,
p.checkup_checked,
p.checkup_fixed,
(${INFLUENCE_LINKS_EXISTS}) AS has_influence_links
FROM paintings p
WHERE p.artist_id = $1
@@ -477,12 +479,17 @@ app.get('/api/paintings/:id/debug-image-search', async (req, res) => {
app.post('/api/paintings/:id/fix-image', async (req, res) => {
try {
const paintingId = parseInt(req.params.id, 10);
const imageUrl = req.body?.imageUrl;
const { imageUrl, searchUrl, source, pageUrl, thumbUrl } = req.body ?? {};
if (!imageUrl || typeof imageUrl !== 'string' || !/^https?:\/\//i.test(imageUrl)) {
return res.status(400).json({ error: 'Valid imageUrl required' });
}
const updated = await replacePaintingImageFromUrl(paintingId, imageUrl);
const updated = await replacePaintingImageFromUrl(paintingId, imageUrl, {
searchUrl: typeof searchUrl === 'string' ? searchUrl : undefined,
source: typeof source === 'string' ? source : undefined,
pageUrl: typeof pageUrl === 'string' ? pageUrl : undefined,
thumbUrl: typeof thumbUrl === 'string' ? thumbUrl : undefined,
});
await pool.query(
`UPDATE paintings SET checkup_fixed = true, checkup_checked = true WHERE id = $1`,
[paintingId]
@@ -490,7 +497,7 @@ app.post('/api/paintings/:id/fix-image', async (req, res) => {
res.json({ ...updated, fixed: true, checked: true });
} catch (err) {
console.error('Fix image error:', err.message);
res.status(500).json({ error: err.message || 'Fix image failed' });
res.status(500).json({ error: friendlyImageFetchError(err) });
}
});
@@ -498,11 +505,16 @@ app.post('/api/paintings/:id/fix-image', async (req, res) => {
app.get('/api/debug/image-proxy', async (req, res) => {
try {
const imageUrl = req.query.url;
const searchUrl = req.query.searchUrl;
const source = req.query.source;
if (!imageUrl || typeof imageUrl !== 'string' || !/^https?:\/\//i.test(imageUrl)) {
return res.status(400).json({ error: 'Valid url query required' });
}
const buffer = await fetchImageBuffer(imageUrl);
const buffer = await fetchImageBuffer(imageUrl, {
searchUrl: typeof searchUrl === 'string' ? searchUrl : undefined,
source: typeof source === 'string' ? source : undefined,
});
const ext = pickExt(imageUrl).toLowerCase();
const type =
ext === '.png' ? 'image/png' : ext === '.webp' ? 'image/webp' : 'image/jpeg';