97 lines
3.2 KiB
TypeScript
97 lines
3.2 KiB
TypeScript
import fs from 'fs/promises';
|
|
import path from 'path';
|
|
import { Prompt, PromptCache } from '@/lib/types';
|
|
import { JimmyLvCrawler, YouMindCrawler, ZeroLuCrawler } from '@/lib/crawler';
|
|
|
|
const DATA_FILE = path.join(process.cwd(), 'data', 'prompts.json');
|
|
|
|
export async function getPrompts(): Promise<PromptCache> {
|
|
try {
|
|
const fileContent = await fs.readFile(DATA_FILE, 'utf-8');
|
|
return JSON.parse(fileContent);
|
|
} catch (e) {
|
|
return { prompts: [], last_updated: null, categories: {}, total_count: 0, sources: [] };
|
|
}
|
|
}
|
|
|
|
export async function syncPromptsService(): Promise<{ success: boolean, count: number, added: number }> {
|
|
console.log("[SyncService] Starting sync...");
|
|
|
|
// 1. Crawl all sources
|
|
const jimmyCrawler = new JimmyLvCrawler();
|
|
const youMindCrawler = new YouMindCrawler();
|
|
const zeroLuCrawler = new ZeroLuCrawler();
|
|
|
|
const [jimmyPrompts, youMindPrompts, zeroLuPrompts] = await Promise.all([
|
|
jimmyCrawler.crawl(),
|
|
youMindCrawler.crawl(),
|
|
zeroLuCrawler.crawl()
|
|
]);
|
|
|
|
const crawledPrompts = [...jimmyPrompts, ...youMindPrompts, ...zeroLuPrompts];
|
|
console.log(`[SyncService] Total crawled ${crawledPrompts.length} prompts (Jimmy: ${jimmyPrompts.length}, YouMind: ${youMindPrompts.length}, ZeroLu: ${zeroLuPrompts.length}).`);
|
|
|
|
// 2. Read existing
|
|
const cache = await getPrompts();
|
|
const existingPrompts = cache.prompts || [];
|
|
|
|
// 3. Merge
|
|
let addedCount = 0;
|
|
const now = Date.now();
|
|
const finalPrompts: Prompt[] = [];
|
|
const existingMap = new Map<string, Prompt>();
|
|
|
|
existingPrompts.forEach(p => {
|
|
if (p.source_url) {
|
|
existingMap.set(p.source_url, p);
|
|
} else {
|
|
finalPrompts.push(p);
|
|
}
|
|
});
|
|
|
|
crawledPrompts.forEach(newP => {
|
|
const existing = existingMap.get(newP.source_url);
|
|
if (existing) {
|
|
finalPrompts.push({
|
|
...newP,
|
|
id: existing.id,
|
|
images: existing.images,
|
|
createdAt: existing.createdAt || (existing.published ? new Date(existing.published).getTime() : undefined),
|
|
useCount: existing.useCount,
|
|
lastUsedAt: existing.lastUsedAt
|
|
});
|
|
existingMap.delete(newP.source_url);
|
|
} else {
|
|
addedCount++;
|
|
finalPrompts.push({
|
|
...newP,
|
|
createdAt: now,
|
|
useCount: 0
|
|
});
|
|
}
|
|
});
|
|
|
|
// Add remaining existing
|
|
existingMap.forEach(p => finalPrompts.push(p));
|
|
|
|
// Re-ID
|
|
finalPrompts.forEach((p, i) => p.id = i + 1);
|
|
|
|
// Meta
|
|
const categories: Record<string, string[]> = {
|
|
"style": Array.from(new Set(finalPrompts.map(p => p.category)))
|
|
};
|
|
|
|
const newCache: PromptCache = {
|
|
last_updated: new Date(now).toISOString(),
|
|
lastSync: now,
|
|
categories,
|
|
total_count: finalPrompts.length,
|
|
sources: Array.from(new Set(finalPrompts.map(p => p.source))),
|
|
prompts: finalPrompts
|
|
};
|
|
|
|
await fs.writeFile(DATA_FILE, JSON.stringify(newCache, null, 2), 'utf-8');
|
|
|
|
return { success: true, count: finalPrompts.length, added: addedCount };
|
|
}
|