import { EmailIngestor } from '../apps/backend/src/services/ingestors/EmailIngestor.js'; import { CalendarIngestor } from '../apps/backend/src/services/ingestors/CalendarIngestor.js'; import { neo4jAdapter } from '../apps/backend/src/adapters/Neo4jAdapter.js'; import { randomUUID } from 'crypto'; import fs from 'fs'; import path from 'path'; const loadEnvIfNeeded = () => { if (process.env.NEO4J_URI && process.env.NEO4J_USERNAME && process.env.NEO4J_PASSWORD) return; const envPath = path.join(process.cwd(), 'apps', 'backend', '.env'); if (fs.existsSync(envPath)) { const lines = fs.readFileSync(envPath, 'utf-8').split(/\r?\n/); for (const line of lines) { const m = line.match(/^([A-Z0-9_]+)=(.*)$/); if (m) { const [, k, v] = m; if (!process.env[k]) process.env[k] = v; } } } }; const persistNodes = async (nodes) => { const persisted = []; for (const node of nodes) { const labels = (node.labels?.length ? node.labels : ['Private']).map((l) => l.replace(/[^A-Za-z0-9_]/g, '') ); const labelString = labels.map((l) => `:${l}`).join(''); const props = { ...(node.properties || {}) }; if (!props.externalId) props.externalId = randomUUID(); if (node.ownerUid) { props.belongsTo = node.ownerUid; if (!labels.includes('Private')) labels.push('Private'); } const cypher = ` MERGE (n${labelString} {externalId: $props.externalId}) SET n += $props WITH n, $ownerUid AS ownerUid OPTIONAL MATCH (u:User {uid: ownerUid}) WITH n, ownerUid, u FOREACH (_ IN CASE WHEN ownerUid IS NULL THEN [] ELSE [1] END | MERGE (u:User {uid: ownerUid}) ON CREATE SET u.name = 'Claus', u.role = 'Executive', u.access_level = 'god_mode' MERGE (n)-[:BELONGS_TO]->(u) ) RETURN n.externalId AS externalId `; const res = await neo4jAdapter.executeQuery(cypher, { props, ownerUid: node.ownerUid ?? null }); if (res && res[0]?.externalId) persisted.push(res[0].externalId); } return persisted; }; const run = async () => { loadEnvIfNeeded(); const ingest = async (ingestor) => { await ingestor.connect(); const items = await ingestor.fetchRecent(2); const nodes = items.map((i) => ingestor.transformToGraphNode(i)); return persistNodes(nodes); }; await ingest(new EmailIngestor()); await ingest(new CalendarIngestor()); const res = await neo4jAdapter.executeQuery( 'MATCH (u:User)-[r:BELONGS_TO]-(n:Private) RETURN u.uid AS uid, type(r) AS rel, labels(n) AS labels, coalesce(n.title, n.subject, n.bodyPreview) AS title LIMIT 5' ); console.log(JSON.stringify(res, null, 2)); }; run().catch((err) => { console.error(err); process.exit(1); });