gongxiangp2/scripts/cleanup-server-orphaned.ts

150 lines
4.8 KiB
TypeScript

/**
* 在服务器上清理孤立的任务-详情素材残留文件
*/
import { PrismaClient } from '@prisma/client';
import { deleteFromS3, isS3Url } from '../lib/s3';
import { promises as fs } from 'fs';
import path from 'path';
const MATERIAL_ID = 'cc7e5cdd-d408-4bcd-a73f-4ddcaa8054b9';
const prisma = new PrismaClient();
async function main() {
console.log('开始检查服务器上的残留文件...\n');
try {
// 1. 检查数据库中的素材记录
console.log('1. 检查数据库中的素材记录...');
const material = await prisma.material.findUnique({
where: { id: MATERIAL_ID },
select: { id: true, materialType: true },
});
if (material) {
console.log(` ❌ 发现素材记录: id=${material.id}, type=${material.materialType}`);
} else {
console.log(` ✅ 素材记录已删除`);
}
// 2. 检查数据库中的资产记录
console.log('\n2. 检查数据库中的资产记录...');
const assets = await prisma.materialAsset.findMany({
where: { materialId: MATERIAL_ID },
select: {
id: true,
assetType: true,
fileUrl: true,
fileName: true,
},
});
if (assets.length > 0) {
console.log(` ❌ 发现 ${assets.length} 条资产记录:`);
assets.forEach((asset, index) => {
console.log(` [${index + 1}] type=${asset.assetType}, url=${asset.fileUrl}`);
});
} else {
console.log(` ✅ 资产记录已删除`);
}
// 3. 检查本地文件系统
console.log('\n3. 检查本地文件系统...');
const uploadsDir = path.join(process.cwd(), 'uploads', 'product-package', MATERIAL_ID);
let localFiles: string[] = [];
try {
const files = await fs.readdir(uploadsDir, { recursive: true });
localFiles = files.map(f => path.join(uploadsDir, f));
console.log(` ❌ 发现本地目录: ${uploadsDir}`);
console.log(` 包含 ${files.length} 个文件/目录`);
files.forEach((file, index) => {
console.log(` [${index + 1}] ${file}`);
});
} catch (err: any) {
if (err.code === 'ENOENT') {
console.log(` ✅ 本地目录已删除`);
} else {
console.log(` ⚠️ 检查本地目录时出错: ${err.message}`);
}
}
// 4. 总结
console.log('\n' + '='.repeat(60));
console.log('检查总结:');
console.log(` - 素材记录: ${material ? '存在' : '已删除'}`);
console.log(` - 资产记录: ${assets.length} 条`);
console.log(` - 本地文件: ${localFiles.length} 个`);
console.log('='.repeat(60));
// 5. 自动清理
if (material || assets.length > 0 || localFiles.length > 0) {
console.log('\n开始自动清理...\n');
// 删除资产记录
if (assets.length > 0) {
console.log('删除资产记录...');
const result = await prisma.materialAsset.deleteMany({
where: { materialId: MATERIAL_ID },
});
console.log(` ✅ 已删除 ${result.count} 条资产记录`);
}
// 删除素材记录
if (material) {
console.log('删除素材记录...');
try {
await prisma.material.delete({
where: { id: MATERIAL_ID },
});
console.log(` ✅ 已删除素材记录`);
} catch (err: any) {
console.log(` ⚠️ 删除素材记录失败: ${err.message}`);
}
}
// 删除本地文件
if (localFiles.length > 0) {
console.log('删除本地文件...');
try {
await fs.rm(uploadsDir, { recursive: true, force: true });
console.log(` ✅ 已删除本地目录: ${uploadsDir}`);
} catch (err: any) {
console.log(` ⚠️ 删除本地文件失败: ${err.message}`);
}
}
// 删除 S3 文件
const s3Files = assets.filter(a => isS3Url(a.fileUrl));
if (s3Files.length > 0) {
console.log(`删除 S3 文件 (${s3Files.length} 个)...`);
let deletedCount = 0;
let failedCount = 0;
for (const asset of s3Files) {
try {
await deleteFromS3(asset.fileUrl);
deletedCount++;
console.log(` ✅ 已删除: ${asset.fileUrl}`);
} catch (err: any) {
failedCount++;
console.log(` ❌ 删除失败: ${asset.fileUrl} - ${err.message}`);
}
}
console.log(` S3 文件清理完成: ${deletedCount} 成功, ${failedCount} 失败`);
}
console.log('\n✅ 清理完成!');
} else {
console.log('\n✅ 没有发现残留文件,无需清理。');
}
} catch (error: any) {
console.error('检查失败:', error.message);
throw error;
} finally {
await prisma.$disconnect();
}
}
main().catch(console.error);