migrate: replace Firebase with PostgreSQL across core routes
- chat-context.ts: session history now from fs_sessions - /api/sessions: reads from fs_sessions (NextAuth session auth) - /api/github/connect: NextAuth session + stores in fs_users.data - /api/user/api-key: NextAuth session + stores in fs_users.data - /api/projects/[id]/vision: PATCH to fs_projects JSONB - /api/projects/[id]/knowledge/items: reads from fs_knowledge_items - /api/projects/[id]/knowledge/import-ai-chat: uses pg createKnowledgeItem - lib/server/knowledge.ts: fully rewritten to use PostgreSQL - entrypoint.sh: add fs_knowledge_items and chat_conversations tables Made-with: Cursor
This commit is contained in:
@@ -205,17 +205,15 @@ export async function buildProjectContextForChat(
|
||||
};
|
||||
|
||||
try {
|
||||
// Query sessions linked to this project
|
||||
const sessionsSnapshot = await adminDb
|
||||
.collection('sessions')
|
||||
.where('projectId', '==', projectId)
|
||||
.orderBy('startTime', 'asc')
|
||||
.get();
|
||||
// Query sessions linked to this project from PostgreSQL
|
||||
const sessionRows = await query<{ id: string; data: any }>(
|
||||
`SELECT id, data FROM fs_sessions WHERE data->>'projectId' = $1 ORDER BY created_at ASC`,
|
||||
[projectId]
|
||||
);
|
||||
|
||||
if (sessionRows.length > 0) {
|
||||
sessionHistory.totalSessions = sessionRows.length;
|
||||
|
||||
if (!sessionsSnapshot.empty) {
|
||||
sessionHistory.totalSessions = sessionsSnapshot.size;
|
||||
|
||||
// Extract all messages from all sessions in chronological order
|
||||
const allMessages: Array<{
|
||||
role: string;
|
||||
content: string;
|
||||
@@ -223,32 +221,27 @@ export async function buildProjectContextForChat(
|
||||
sessionId: string;
|
||||
}> = [];
|
||||
|
||||
for (const sessionDoc of sessionsSnapshot.docs) {
|
||||
const sessionData = sessionDoc.data();
|
||||
const conversation = sessionData.conversation || [];
|
||||
|
||||
// Add messages from this session
|
||||
for (const row of sessionRows) {
|
||||
const conversation = row.data?.conversation || [];
|
||||
for (const msg of conversation) {
|
||||
if (msg.content && msg.content.trim()) {
|
||||
allMessages.push({
|
||||
role: msg.role || 'unknown',
|
||||
content: msg.content,
|
||||
timestamp: msg.timestamp instanceof Date
|
||||
? msg.timestamp.toISOString()
|
||||
: (typeof msg.timestamp === 'string' ? msg.timestamp : new Date().toISOString()),
|
||||
sessionId: sessionDoc.id,
|
||||
timestamp: typeof msg.timestamp === 'string'
|
||||
? msg.timestamp
|
||||
: new Date().toISOString(),
|
||||
sessionId: row.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort all messages by timestamp (chronological order)
|
||||
allMessages.sort((a, b) =>
|
||||
allMessages.sort((a, b) =>
|
||||
new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
|
||||
);
|
||||
|
||||
sessionHistory.messages = allMessages;
|
||||
|
||||
console.log(
|
||||
`[Chat Context] Loaded ${sessionHistory.totalSessions} sessions with ${allMessages.length} total messages for project ${projectId}`
|
||||
);
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import { getAdminDb } from '@/lib/firebase/admin';
|
||||
import { FieldValue } from 'firebase-admin/firestore';
|
||||
import { query } from '@/lib/db-postgres';
|
||||
import { randomUUID } from 'crypto';
|
||||
import type {
|
||||
KnowledgeItem,
|
||||
KnowledgeSourceMeta,
|
||||
KnowledgeSourceType,
|
||||
} from '@/lib/types/knowledge';
|
||||
|
||||
const COLLECTION = 'knowledge_items';
|
||||
|
||||
interface CreateKnowledgeItemInput {
|
||||
projectId: string;
|
||||
sourceType: KnowledgeSourceType;
|
||||
@@ -16,59 +14,62 @@ interface CreateKnowledgeItemInput {
|
||||
sourceMeta?: KnowledgeSourceMeta;
|
||||
}
|
||||
|
||||
function rowToItem(row: { id: string; project_id: string; data: any; created_at: string; updated_at: string }): KnowledgeItem {
|
||||
const d = row.data ?? {};
|
||||
return {
|
||||
id: row.id,
|
||||
projectId: row.project_id,
|
||||
sourceType: d.sourceType,
|
||||
title: d.title ?? null,
|
||||
content: d.content,
|
||||
sourceMeta: d.sourceMeta ?? null,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
} as KnowledgeItem;
|
||||
}
|
||||
|
||||
export async function createKnowledgeItem(
|
||||
input: CreateKnowledgeItemInput,
|
||||
): Promise<KnowledgeItem> {
|
||||
const adminDb = getAdminDb();
|
||||
const docRef = adminDb.collection(COLLECTION).doc();
|
||||
|
||||
const payload = {
|
||||
id: docRef.id,
|
||||
projectId: input.projectId,
|
||||
const id = randomUUID();
|
||||
const data = {
|
||||
sourceType: input.sourceType,
|
||||
title: input.title ?? null,
|
||||
content: input.content,
|
||||
sourceMeta: input.sourceMeta ?? null,
|
||||
createdAt: FieldValue.serverTimestamp(),
|
||||
updatedAt: FieldValue.serverTimestamp(),
|
||||
};
|
||||
|
||||
await docRef.set(payload);
|
||||
const snapshot = await docRef.get();
|
||||
return snapshot.data() as KnowledgeItem;
|
||||
await query(
|
||||
`INSERT INTO fs_knowledge_items (id, project_id, data) VALUES ($1, $2, $3::jsonb)`,
|
||||
[id, input.projectId, JSON.stringify(data)]
|
||||
);
|
||||
|
||||
const rows = await query<any>(
|
||||
`SELECT id, project_id, data, created_at, updated_at FROM fs_knowledge_items WHERE id = $1`,
|
||||
[id]
|
||||
);
|
||||
|
||||
return rowToItem(rows[0]);
|
||||
}
|
||||
|
||||
export async function getKnowledgeItem(
|
||||
projectId: string,
|
||||
knowledgeItemId: string,
|
||||
): Promise<KnowledgeItem | null> {
|
||||
const adminDb = getAdminDb();
|
||||
const docRef = adminDb.collection(COLLECTION).doc(knowledgeItemId);
|
||||
const snapshot = await docRef.get();
|
||||
|
||||
if (!snapshot.exists) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = snapshot.data() as KnowledgeItem;
|
||||
if (data.projectId !== projectId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return data;
|
||||
const rows = await query<any>(
|
||||
`SELECT id, project_id, data, created_at, updated_at FROM fs_knowledge_items WHERE id = $1 AND project_id = $2`,
|
||||
[knowledgeItemId, projectId]
|
||||
);
|
||||
if (rows.length === 0) return null;
|
||||
return rowToItem(rows[0]);
|
||||
}
|
||||
|
||||
export async function listKnowledgeItems(
|
||||
projectId: string,
|
||||
): Promise<KnowledgeItem[]> {
|
||||
const adminDb = getAdminDb();
|
||||
const querySnapshot = await adminDb
|
||||
.collection(COLLECTION)
|
||||
.where('projectId', '==', projectId)
|
||||
.orderBy('createdAt', 'desc')
|
||||
.get();
|
||||
|
||||
return querySnapshot.docs.map((doc) => doc.data() as KnowledgeItem);
|
||||
const rows = await query<any>(
|
||||
`SELECT id, project_id, data, created_at, updated_at FROM fs_knowledge_items WHERE project_id = $1 ORDER BY created_at DESC`,
|
||||
[projectId]
|
||||
);
|
||||
return rows.map(rowToItem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user