Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

# Node cruft
node_modules
.pnpm
dist
build
.cache
turbo
out

# Dependency locks from other ecosystems
package-lock.json
yarn.lock

# Logs & temp
*.log
*.tsbuildinfo
.vscode
.idea
.env
.env.*

# OS junk
.DS_Store
Thumbs.db

# Local-only files
coverage
*.local.*
*.swp
*.swo

# Git stuff
.git
.gitignore

# Prevent docker context pollution from other packages in monorepo
apps/*/node_modules
packages/*/node_modules
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Neo4j Configuration
NEO4J_URI=bolt://neo4j:7687
NEO4J_USER=neo4j
NEO4J_PASSWORD=your_secure_password_here

# eVault Configuration
PORT=4000
2 changes: 1 addition & 1 deletion .github/workflows/tests-evault-core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
pull_request:
branches: [main]
paths:
- 'infrastructure/w3id/**'
- 'infrastructure/evault-core/**'

jobs:
test:
Expand Down
23 changes: 23 additions & 0 deletions docker/Dockerfile.evault
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM node:22-slim AS deps
ENV PNPM_HOME="/pnpm"
ENV PATH="$PNPM_HOME:$PATH"
RUN corepack enable
COPY . /app
WORKDIR /app
RUN npm i -g corepack@latest
RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
RUN pnpm turbo prune evault-core --docker --use-gitignore=false
RUN mkdir /out
RUN cp -R ./out/full/* /out/
RUN cp -R ./out/json/* /out/
RUN cp ./out/pnpm-lock.yaml /out/pnpm-lock.yaml
RUN cp -R node_modules/ /out/


FROM node:22-slim AS core-api
WORKDIR /app
RUN npm i -g corepack@latest
COPY --from=deps /out/ /app
EXPOSE 1209
workdir /app/infrastructure/evault-core
CMD ["pnpm", "dev"]
50 changes: 50 additions & 0 deletions evault.docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: '3.8'

services:
evault:
build:
context: .
dockerfile: ./docker/Dockerfile.evault
ports:
- "4000:4000"
environment:
- NEO4J_URI=${NEO4J_URI}
- NEO4J_USER=${NEO4J_USER}
- NEO4J_PASSWORD=${NEO4J_PASSWORD}
networks:
- graphnet
depends_on:
- neo4j
develop:
watch:
- action: sync+restart
path: ./infrastructure/evault-core/
target: /app/infrastructure/evault-core
ignore:
- node_modules
- action: rebuild
path: ./infrastructure/evault-core/package.json
- action: rebuild
path: ./.env

neo4j:
image: neo4j:5.15
container_name: evault-neo4j
ports:
- "7474:7474" # HTTP
- "7687:7687" # Bolt
environment:
- NEO4J_AUTH=${NEO4J_USER}/${NEO4J_PASSWORD}
volumes:
- neo4j_data:/data
- neo4j_logs:/log
networks:
- graphnet

volumes:
neo4j_data:
neo4j_logs:

networks:
graphnet:
driver: bridge
22 changes: 0 additions & 22 deletions infrastructure/evault-core/docker-compose.yml

This file was deleted.

63 changes: 33 additions & 30 deletions infrastructure/evault-core/package.json
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
{
"name": "evault-core",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "vitest --config vitest.config.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/json-schema": "^7.0.15",
"@types/node": "^22.13.10",
"dotenv": "^16.5.0",
"testcontainers": "^10.24.2",
"tsx": "^4.19.3",
"typescript": "^5.8.3",
"uuid": "^11.1.0",
"vitest": "^3.0.9"
},
"dependencies": {
"@testcontainers/neo4j": "^10.24.2",
"graphql": "^16.10.0",
"graphql-type-json": "^0.3.2",
"graphql-voyager": "^2.1.0",
"graphql-yoga": "^5.13.4",
"json-schema": "^0.4.0",
"neo4j-driver": "^5.28.1",
"w3id": "workspace:*"
}
"name": "evault-core",
"version": "0.1.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "vitest --config vitest.config.ts",
"build": "tsc",
"dev": "node --watch --import tsx src/evault.ts"
},
"packageManager": "[email protected]",
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/json-schema": "^7.0.15",
"@types/node": "^22.13.10",
"dotenv": "^16.5.0",
"testcontainers": "^10.24.2",
"tsx": "^4.19.3",
"typescript": "^5.8.3",
"uuid": "^11.1.0",
"vitest": "^3.0.9"
},
"dependencies": {
"@testcontainers/neo4j": "^10.24.2",
"graphql": "^16.10.0",
"graphql-type-json": "^0.3.2",
"graphql-voyager": "^2.1.0",
"graphql-yoga": "^5.13.4",
"json-schema": "^0.4.0",
"neo4j-driver": "^5.28.1",
"w3id": "workspace:*"
}
}
107 changes: 98 additions & 9 deletions infrastructure/evault-core/src/db/db.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@ import {
* with proper type handling and access control.
*/
export class DbService {
private driver: Driver;

/**
* Creates a new instance of the DbService.
* @param driver - The Neo4j driver instance
*/
constructor(private driver: Driver) {}
constructor(driver: Driver) {
this.driver = driver;
}

/**
* Executes a Cypher query with the given parameters.
Expand Down Expand Up @@ -174,6 +177,59 @@ export class DbService {
});
}

/**
* Finds multiple meta-envelopes by an array of IDs.
* @param ids - Array of MetaEnvelope IDs
* @returns Array of meta-envelopes with envelopes and parsed payload
*/
async findMetaEnvelopesByIds<
T extends Record<string, any> = Record<string, any>,
>(ids: string[]): Promise<MetaEnvelopeResult<T>[]> {
if (!ids.length) return [];

const result = await this.runQuery(
`
MATCH (m:MetaEnvelope)-[:LINKS_TO]->(e:Envelope)
WHERE m.id IN $ids
RETURN m.id AS id, m.ontology AS ontology, m.acl AS acl, collect(e) AS envelopes
`,
{ ids },
);

return result.records.map((record): MetaEnvelopeResult<T> => {
const envelopes = record
.get("envelopes")
.map((node: any): Envelope<T[keyof T]> => {
const props = node.properties;
return {
id: props.id,
ontology: props.ontology,
value: deserializeValue(
props.value,
props.valueType,
) as T[keyof T],
valueType: props.valueType,
};
});

const parsed = envelopes.reduce(
(acc: T, env: Envelope<T[keyof T]>) => {
(acc as any)[env.ontology] = env.value;
return acc;
},
{} as T,
);

return {
id: record.get("id"),
ontology: record.get("ontology"),
acl: record.get("acl"),
envelopes,
parsed,
};
});
}

/**
* Finds a meta-envelope by its ID.
* @param id - The ID of the meta-envelope to find
Expand Down Expand Up @@ -226,20 +282,53 @@ export class DbService {
}

/**
* Finds all meta-envelope IDs for a given ontology.
* Finds all meta-envelopes by ontology with their envelopes and parsed payload.
* @param ontology - The ontology to search for
* @returns Array of meta-envelope IDs
* @returns Array of meta-envelopes
*/
async findMetaEnvelopesByOntology(ontology: string): Promise<string[]> {
async findMetaEnvelopesByOntology<
T extends Record<string, any> = Record<string, any>,
>(ontology: string): Promise<MetaEnvelopeResult<T>[]> {
const result = await this.runQuery(
`
MATCH (m:MetaEnvelope { ontology: $ontology })
RETURN m.id AS id
`,
MATCH (m:MetaEnvelope { ontology: $ontology })-[:LINKS_TO]->(e:Envelope)
RETURN m.id AS id, m.ontology AS ontology, m.acl AS acl, collect(e) AS envelopes
`,
{ ontology },
);

return result.records.map((r) => r.get("id"));
return result.records.map((record) => {
const envelopes = record
.get("envelopes")
.map((node: any): Envelope<T[keyof T]> => {
const properties = node.properties;
return {
id: properties.id,
ontology: properties.ontology,
value: deserializeValue(
properties.value,
properties.valueType,
) as T[keyof T],
valueType: properties.valueType,
};
});

const parsed = envelopes.reduce(
(acc: T, envelope: Envelope<T[keyof T]>) => {
(acc as any)[envelope.ontology] = envelope.value;
return acc;
},
{} as T,
);

return {
id: record.get("id"),
ontology: record.get("ontology"),
acl: record.get("acl"),
envelopes,
parsed,
};
});
}

/**
Expand Down
Loading