Initial miaojingAI project with image resolution guard

This commit is contained in:
FengLee
2026-05-09 11:32:34 +08:00
commit d499020d4e
264 changed files with 54160 additions and 0 deletions

206
audit_recovered_data.js Normal file
View File

@@ -0,0 +1,206 @@
const { Pool } = require('pg');
require('dotenv').config({ path: '.env.local' });
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000000';
function short(value, length = 160) {
if (value == null) return value;
const text = typeof value === 'string' ? value : JSON.stringify(value);
return text.length > length ? `${text.slice(0, length)}...` : text;
}
async function main() {
const pool = new Pool({ connectionString: process.env.LOCAL_DB_URL });
const client = await pool.connect();
try {
const tableColumns = await client.query(`
SELECT table_schema, table_name, column_name, data_type
FROM information_schema.columns
WHERE (table_schema = 'public' AND table_name IN ('profiles', 'works', 'user_api_keys', 'orders', 'credit_transactions'))
OR (table_schema = 'auth' AND table_name = 'users')
ORDER BY table_schema, table_name, ordinal_position
`);
const userSummary = await client.query(`
SELECT
(SELECT COUNT(*)::int FROM auth.users) AS auth_users,
(SELECT COUNT(*)::int FROM profiles) AS profiles,
(SELECT COUNT(*)::int FROM profiles WHERE role = 'admin') AS admins,
(SELECT COUNT(*)::int FROM profiles WHERE role <> 'admin') AS non_admin_profiles,
(SELECT COUNT(*)::int FROM auth.users u LEFT JOIN profiles p ON p.id = u.id WHERE p.id IS NULL) AS auth_without_profile,
(SELECT COUNT(*)::int FROM profiles p LEFT JOIN auth.users u ON u.id = p.id WHERE u.id IS NULL) AS profile_without_auth,
(SELECT COUNT(*)::int FROM auth.users WHERE password_hash IS NULL OR password_hash = '') AS auth_without_password_hash,
(SELECT COUNT(*)::int FROM auth.users WHERE password_hash IS NOT NULL AND password_hash <> '') AS auth_with_password_hash
`);
const userSamples = await client.query(`
SELECT p.id, p.email, p.nickname, p.role, p.membership_tier, p.is_active,
u.id IS NOT NULL AS has_auth,
(u.password_hash IS NOT NULL AND u.password_hash <> '') AS has_password_hash,
p.created_at
FROM profiles p
LEFT JOIN auth.users u ON u.id = p.id
ORDER BY p.created_at DESC NULLS LAST
LIMIT 80
`);
const workSummary = await client.query(`
SELECT
COUNT(*)::int AS total,
COUNT(*) FILTER (WHERE status = 'completed')::int AS completed,
COUNT(*) FILTER (WHERE is_public = true AND status = 'completed')::int AS public_completed,
COUNT(*) FILTER (WHERE is_public = false AND status = 'completed')::int AS private_completed,
COUNT(*) FILTER (WHERE user_id IS NULL)::int AS null_user_id,
COUNT(*) FILTER (WHERE user_id = $1)::int AS system_user_id,
COUNT(*) FILTER (WHERE p.id IS NULL)::int AS missing_profile,
COUNT(*) FILTER (WHERE p.id IS NOT NULL)::int AS linked_profile
FROM works w
LEFT JOIN profiles p ON p.id = w.user_id
`, [SYSTEM_USER_ID]);
const publicWorkSummary = await client.query(`
SELECT
COUNT(*)::int AS public_total,
COUNT(*) FILTER (WHERE w.user_id IS NULL)::int AS null_user_id,
COUNT(*) FILTER (WHERE w.user_id = $1)::int AS system_user_id,
COUNT(*) FILTER (WHERE p.id IS NULL)::int AS missing_profile,
COUNT(*) FILTER (WHERE p.id IS NOT NULL)::int AS linked_profile
FROM works w
LEFT JOIN profiles p ON p.id = w.user_id
WHERE w.is_public = true AND w.status = 'completed'
`, [SYSTEM_USER_ID]);
const workByUser = await client.query(`
SELECT
COALESCE(p.email, '[missing-profile]') AS email,
COALESCE(p.nickname, '') AS nickname,
COALESCE(p.role, '') AS role,
w.user_id,
COUNT(*)::int AS total_works,
COUNT(*) FILTER (WHERE w.status = 'completed')::int AS completed_works,
COUNT(*) FILTER (WHERE w.is_public = true AND w.status = 'completed')::int AS public_works,
COUNT(*) FILTER (WHERE w.is_public = false AND w.status = 'completed')::int AS history_works
FROM works w
LEFT JOIN profiles p ON p.id = w.user_id
GROUP BY w.user_id, p.email, p.nickname, p.role
ORDER BY total_works DESC
LIMIT 120
`);
const orphanSamples = await client.query(`
SELECT w.id, w.user_id, w.type, w.status, w.is_public, w.result_url,
LEFT(COALESCE(w.prompt, ''), 140) AS prompt,
w.params,
w.created_at
FROM works w
LEFT JOIN profiles p ON p.id = w.user_id
WHERE p.id IS NULL OR w.user_id = $1 OR w.user_id IS NULL
ORDER BY w.created_at DESC NULLS LAST
LIMIT 80
`, [SYSTEM_USER_ID]);
const paramKeys = await client.query(`
SELECT key, COUNT(*)::int AS count
FROM works w
CROSS JOIN LATERAL jsonb_object_keys(COALESCE(w.params, '{}'::jsonb)) AS key
LEFT JOIN profiles p ON p.id = w.user_id
WHERE w.is_public = true
AND w.status = 'completed'
AND (p.id IS NULL OR w.user_id = $1 OR w.user_id IS NULL)
GROUP BY key
ORDER BY count DESC, key
LIMIT 80
`, [SYSTEM_USER_ID]);
const possibleOwnerFields = await client.query(`
SELECT
id,
user_id,
params->>'user_id' AS params_user_id,
params->>'userId' AS params_user_id_camel,
params->>'publisher_id' AS publisher_id,
params->>'publisherId' AS publisher_id_camel,
params->>'owner_id' AS owner_id,
params->>'ownerId' AS owner_id_camel,
params->>'created_by' AS created_by,
params->>'createdBy' AS created_by_camel,
params->>'email' AS params_email,
params->>'userEmail' AS params_user_email,
params->>'publisherEmail' AS params_publisher_email,
params->>'nickname' AS params_nickname,
params->>'userName' AS params_user_name,
LEFT(COALESCE(prompt, ''), 120) AS prompt
FROM works
WHERE is_public = true
AND status = 'completed'
AND (user_id IS NULL OR user_id = $1 OR NOT EXISTS (SELECT 1 FROM profiles p WHERE p.id = works.user_id))
ORDER BY created_at DESC NULLS LAST
LIMIT 80
`, [SYSTEM_USER_ID]);
const duplicateCandidates = await client.query(`
SELECT
public.id AS orphan_id,
public.user_id AS orphan_user_id,
private.id AS owned_id,
private.user_id AS owner_user_id,
p.email,
p.nickname,
CASE
WHEN private.result_url = public.result_url THEN 'result_url'
WHEN COALESCE(private.thumbnail_url, '') <> '' AND private.thumbnail_url = public.thumbnail_url THEN 'thumbnail_url'
WHEN COALESCE(private.prompt, '') <> '' AND private.prompt = public.prompt THEN 'prompt_time'
ELSE 'unknown'
END AS match_type,
ABS(EXTRACT(EPOCH FROM (private.created_at - public.created_at)))::int AS seconds_apart,
LEFT(COALESCE(public.prompt, ''), 120) AS prompt
FROM works public
JOIN works private
ON private.id <> public.id
AND private.user_id IS NOT NULL
AND private.user_id <> $1
AND (
private.result_url = public.result_url
OR (
COALESCE(public.thumbnail_url, '') <> ''
AND private.thumbnail_url = public.thumbnail_url
)
OR (
COALESCE(private.prompt, '') <> ''
AND private.prompt = public.prompt
AND private.created_at BETWEEN public.created_at - INTERVAL '30 minutes' AND public.created_at + INTERVAL '30 minutes'
)
)
JOIN profiles p ON p.id = private.user_id
LEFT JOIN profiles public_profile ON public_profile.id = public.user_id
WHERE public.is_public = true
AND public.status = 'completed'
AND (public_profile.id IS NULL OR public.user_id = $1 OR public.user_id IS NULL)
ORDER BY public.created_at DESC NULLS LAST, match_type, seconds_apart
LIMIT 100
`, [SYSTEM_USER_ID]);
const output = {
columns: tableColumns.rows,
userSummary: userSummary.rows[0],
userSamples: userSamples.rows,
workSummary: workSummary.rows[0],
publicWorkSummary: publicWorkSummary.rows[0],
workByUser: workByUser.rows,
orphanSamples: orphanSamples.rows.map(row => ({ ...row, result_url: short(row.result_url, 120), params: short(row.params, 300) })),
anonymousParamKeys: paramKeys.rows,
possibleOwnerFields: possibleOwnerFields.rows,
duplicateCandidates: duplicateCandidates.rows,
};
console.log(JSON.stringify(output, null, 2));
} finally {
client.release();
await pool.end();
}
}
main().catch(error => {
console.error(error);
process.exit(1);
});