const { Client } = require('pg'); const fs = require('fs'); const path = require('path'); require('dotenv').config({ path: '/root/miaojingAI/.env.local' }); const root = '/root/miaojingAI'; const c = new Client({ connectionString: process.env.LOCAL_DB_URL }); function keyFromUrl(url) { if (!url) return null; const marker = '/api/local-storage/'; const idx = String(url).indexOf(marker); if (idx < 0) return null; try { return decodeURIComponent(String(url).slice(idx + marker.length).split('?')[0]); } catch { return null; } } function publicUrl(key) { return `/api/local-storage/${key}`; } function copyToGallery(url, type, id, field) { const key = keyFromUrl(url); if (!key) return url; if (key.startsWith('gallery/')) return url; const src = path.join(root, 'local-storage', key); if (!fs.existsSync(src)) return url; const ext = path.extname(key) || '.bin'; const folder = field === 'thumbnail_url' ? 'gallery/thumbnails' : (type === 'text2video' || type === 'img2video' || type === 'video' ? 'gallery/videos' : 'gallery/images'); const destDir = path.join(root, 'local-storage', folder); fs.mkdirSync(destDir, { recursive: true }); const destKey = `${folder}/${id}-${field}${ext}`; const dest = path.join(root, 'local-storage', destKey); if (!fs.existsSync(dest)) fs.copyFileSync(src, dest); return publicUrl(destKey); } (async () => { await c.connect(); await c.query('BEGIN'); try { const adminRes = await c.query(`select id,email from profiles where role='admin' order by case when email='admin@example.com' then 0 else 1 end, created_at asc limit 1`); if (!adminRes.rows.length) throw new Error('No admin profile found'); const admin = adminRes.rows[0]; const password = process.env.ADMIN_DEFAULT_PASSWORD || 'admin123'; await c.query(`update auth.users set password_hash = crypt($1, gen_salt('bf')) where id=$2`, [password, admin.id]); const worksFixed = await c.query(`update works set user_id=$1 where user_id is null or user_id not in (select id from profiles) returning id`, [admin.id]); const creditDeleted = await c.query(`delete from credit_transactions where user_id not in (select id from profiles) returning id`); const publicWorks = await c.query(`select id,type,result_url,thumbnail_url from works where is_public=true order by created_at asc`); let copied = 0; for (const w of publicWorks.rows) { const nextResult = copyToGallery(w.result_url, w.type, w.id, 'result_url'); const nextThumb = w.thumbnail_url ? copyToGallery(w.thumbnail_url, w.type, w.id, 'thumbnail_url') : null; if (nextResult !== w.result_url || nextThumb !== w.thumbnail_url) { await c.query(`update works set result_url=$1, thumbnail_url=$2, updated_at=now() where id=$3`, [nextResult, nextThumb, w.id]); copied++; } } await c.query('COMMIT'); console.log(JSON.stringify({ admin: admin.email, passwordHashSet: true, worksFixed: worksFixed.rowCount, orphanCreditsDeleted: creditDeleted.rowCount, publicWorksCopiedToGallery: copied }, null, 2)); } catch (e) { await c.query('ROLLBACK'); throw e; } finally { await c.end(); } })().catch(e => { console.error(e); process.exit(1); });