Add RAG semantic search and proactive event notifications
Implement hybrid knowledge search using Cloudflare Vectorize + Workers AI embeddings (bge-base-en-v1.5, 768d) merged with existing D1 LIKE queries, with graceful degradation when Vectorize is unavailable. Add admin API endpoints for batch/single article indexing. Add 4 proactive notification cron jobs: server status changes, deposit confirmation/rejection alerts, pending payment reminders (1h+), and bank deposit matching notifications — all with DB-column-based deduplication. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import { Hono } from 'hono';
|
||||
import type { Env, User, Transaction } from '../types';
|
||||
import { timingSafeEqual } from '../security';
|
||||
import { getPendingActions } from '../services/pending-actions';
|
||||
import { indexArticle, batchIndexArticles } from '../services/embedding';
|
||||
import { sendMessage } from '../telegram';
|
||||
import { createLogger } from '../utils/logger';
|
||||
|
||||
@@ -171,4 +172,67 @@ api.post('/broadcast', async (c) => {
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/knowledge/index - Batch index all knowledge articles
|
||||
api.post('/knowledge/index', async (c) => {
|
||||
try {
|
||||
if (!c.env.VECTORIZE) {
|
||||
return c.json({ error: 'VECTORIZE binding not configured' }, 400);
|
||||
}
|
||||
|
||||
const body = await c.req.json<{ force_reindex?: boolean }>().catch(() => ({ force_reindex: false }));
|
||||
const result = await batchIndexArticles(c.env, {
|
||||
forceReindex: body.force_reindex ?? false,
|
||||
});
|
||||
|
||||
logger.info('Knowledge batch indexing completed', {
|
||||
indexed: result.indexed,
|
||||
failed: result.failed,
|
||||
});
|
||||
return c.json(result);
|
||||
} catch (error) {
|
||||
logger.error('Knowledge batch indexing failed', error as Error);
|
||||
return c.json({ error: 'Internal error' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
// POST /api/knowledge/:id/index - Index a single knowledge article
|
||||
api.post('/knowledge/:id/index', async (c) => {
|
||||
try {
|
||||
if (!c.env.VECTORIZE) {
|
||||
return c.json({ error: 'VECTORIZE binding not configured' }, 400);
|
||||
}
|
||||
|
||||
const articleId = parseInt(c.req.param('id'));
|
||||
if (isNaN(articleId)) {
|
||||
return c.json({ error: 'Invalid article ID' }, 400);
|
||||
}
|
||||
|
||||
const article = await c.env.DB
|
||||
.prepare(
|
||||
`SELECT id, category, title, content, tags
|
||||
FROM knowledge_articles WHERE id = ? AND is_active = 1`
|
||||
)
|
||||
.bind(articleId)
|
||||
.first<{ id: number; category: string; title: string; content: string; tags: string | null }>();
|
||||
|
||||
if (!article) {
|
||||
return c.json({ error: 'Article not found' }, 404);
|
||||
}
|
||||
|
||||
await indexArticle(
|
||||
c.env,
|
||||
article.id,
|
||||
article.title,
|
||||
article.content,
|
||||
article.category,
|
||||
article.tags ?? undefined
|
||||
);
|
||||
|
||||
return c.json({ success: true, articleId: article.id });
|
||||
} catch (error) {
|
||||
logger.error('Knowledge article indexing failed', error as Error);
|
||||
return c.json({ error: 'Internal error' }, 500);
|
||||
}
|
||||
});
|
||||
|
||||
export { api as apiRouter };
|
||||
|
||||
Reference in New Issue
Block a user