68 lines
5.7 KiB
JavaScript
68 lines
5.7 KiB
JavaScript
const { Client } = require('pg');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
require('dotenv').config({ path: '/root/miaojingAI/.env.local' });
|
|
const c = new Client({ connectionString: process.env.LOCAL_DB_URL });
|
|
const root = '/root/miaojingAI';
|
|
function storageKeyFromUrl(url) {
|
|
if (!url || typeof url !== 'string') return null;
|
|
const marker = '/api/local-storage/';
|
|
const idx = url.indexOf(marker);
|
|
if (idx < 0) return null;
|
|
try { return decodeURIComponent(url.slice(idx + marker.length).split('?')[0]); } catch { return null; }
|
|
}
|
|
async function query(name, sql, params=[]) {
|
|
try { const r = await c.query(sql, params); return { name, rows: r.rows }; }
|
|
catch (e) { return { name, error: e.message }; }
|
|
}
|
|
(async () => {
|
|
await c.connect();
|
|
const out = {};
|
|
out.db = await query('db', `select current_database() as database, current_user as user, now() as checked_at`);
|
|
out.tables = await query('tables', `select schemaname, tablename from pg_tables where schemaname in ('public','auth') order by schemaname, tablename`);
|
|
const tableNames = out.tables.rows || [];
|
|
out.counts = [];
|
|
for (const t of tableNames) {
|
|
const full = t.schemaname === 'public' ? t.tablename : `${t.schemaname}.${t.tablename}`;
|
|
const r = await query(full, `select count(*)::int as count from ${full}`);
|
|
out.counts.push({ table: full, count: r.rows?.[0]?.count ?? null, error: r.error });
|
|
}
|
|
out.admins = await query('admins', `select id,email,nickname,role,membership_tier,is_active,created_at,updated_at from profiles where role='admin' or email in ('admin@example.com','admin@miaojing.ai') order by created_at`);
|
|
out.authAdmins = await query('auth_admins', `select id,email,created_at, password_hash is not null as has_password from auth.users where email in ('admin@example.com','admin@miaojing.ai') order by created_at`);
|
|
out.profileWithoutAuth = await query('profile_without_auth', `select p.id,p.email,p.role from profiles p left join auth.users u on u.id=p.id where u.id is null order by p.created_at limit 50`);
|
|
out.authWithoutProfile = await query('auth_without_profile', `select u.id,u.email from auth.users u left join profiles p on p.id=u.id where p.id is null order by u.created_at limit 50`);
|
|
out.worksMissingUser = await query('works_missing_user', `select w.id,w.user_id,w.title,w.type,w.status,w.created_at from works w left join profiles p on p.id=w.user_id where w.user_id is not null and p.id is null order by w.created_at desc limit 50`);
|
|
out.worksNullUser = await query('works_null_user', `select id,title,type,status,created_at from works where user_id is null order by created_at desc limit 50`);
|
|
out.worksByStatus = await query('works_by_status', `select status, count(*)::int from works group by status order by status`);
|
|
out.worksByUser = await query('works_by_user', `select p.email,p.nickname,w.user_id,count(*)::int as works from works w left join profiles p on p.id=w.user_id group by p.email,p.nickname,w.user_id order by works desc nulls last limit 20`);
|
|
out.workLikesMissing = await query('work_likes_missing_refs', `select wl.id,wl.user_id,wl.work_id from work_likes wl left join profiles p on p.id=wl.user_id left join works w on w.id=wl.work_id where p.id is null or w.id is null limit 50`);
|
|
out.creditMissingUser = await query('credit_missing_user', `select ct.id,ct.user_id,ct.amount,ct.type,ct.created_at from credit_transactions ct left join profiles p on p.id=ct.user_id where p.id is null limit 50`);
|
|
out.ordersMissingUser = await query('orders_missing_user', `select o.id,o.user_id,o.order_no,o.status,o.created_at from orders o left join profiles p on p.id=o.user_id where o.user_id is not null and p.id is null limit 50`);
|
|
out.apiKeysMissingUser = await query('api_keys_missing_user', `select k.id,k.user_id,k.provider,k.type from user_api_keys k left join profiles p on p.id=k.user_id where p.id is null limit 50`);
|
|
out.jobsMissingUser = await query('jobs_missing_user', `select j.id,j.user_id,j.type,j.status,j.created_at from generation_jobs j left join profiles p on p.id=j.user_id where j.user_id is not null and p.id is null order by j.created_at desc limit 50`);
|
|
out.jobsNullUser = await query('jobs_null_user', `select id,type,status,created_at from generation_jobs where user_id is null order by created_at desc limit 50`);
|
|
out.jobsByUser = await query('jobs_by_user', `select p.email,p.nickname,j.user_id,count(*)::int as jobs from generation_jobs j left join profiles p on p.id=j.user_id group by p.email,p.nickname,j.user_id order by jobs desc nulls last limit 20`);
|
|
out.worksFileCheck = { totalLocalUrls: 0, missing: [] };
|
|
const works = await query('works_urls', `select id,result_url,thumbnail_url from works order by created_at desc`);
|
|
for (const w of works.rows || []) {
|
|
for (const field of ['result_url','thumbnail_url']) {
|
|
const key = storageKeyFromUrl(w[field]);
|
|
if (!key) continue;
|
|
out.worksFileCheck.totalLocalUrls++;
|
|
const abs = path.join(root, 'local-storage', key);
|
|
if (!fs.existsSync(abs)) out.worksFileCheck.missing.push({ workId: w.id, field, key });
|
|
}
|
|
}
|
|
out.storage = {
|
|
localStorageExists: fs.existsSync(path.join(root,'local-storage')),
|
|
backupsExists: fs.existsSync(path.join(root,'backups')),
|
|
localFiles: 0,
|
|
galleryFiles: 0,
|
|
};
|
|
function walk(dir) { if (!fs.existsSync(dir)) return 0; let n=0; for (const e of fs.readdirSync(dir,{withFileTypes:true})) { const p=path.join(dir,e.name); n += e.isDirectory()?walk(p):1; } return n; }
|
|
out.storage.localFiles = walk(path.join(root,'local-storage'));
|
|
out.storage.galleryFiles = walk(path.join(root,'local-storage','gallery'));
|
|
console.log(JSON.stringify(out, null, 2));
|
|
await c.end();
|
|
})().catch(e => { console.error(e); process.exit(1); });
|