729 lines
15 KiB
Markdown
729 lines
15 KiB
Markdown
|
|
# Brainy Web Service
|
||
|
|
|
||
|
|
A secure, read-only web service wrapper for the Brainy vector graph database. This service exposes Brainy's search capabilities through RESTful API endpoints, designed specifically as a search service with comprehensive security measures.
|
||
|
|
|
||
|
|
## Features
|
||
|
|
|
||
|
|
- 🔒 **Read-only by design** - Only safe search operations are exposed
|
||
|
|
- 🚀 **RESTful API** - Clean, well-documented HTTP endpoints
|
||
|
|
- 🛡️ **Security-first** - Rate limiting, input validation, CORS, and security headers
|
||
|
|
- 📊 **Comprehensive search** - Vector search, text search, similarity search
|
||
|
|
- 🔍 **Flexible querying** - Support for noun types, verb filtering, and pagination
|
||
|
|
- 📈 **Production-ready** - Proper error handling, logging, and graceful shutdown
|
||
|
|
- 🌐 **CORS enabled** - Ready for browser-based applications
|
||
|
|
|
||
|
|
## Installation
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm install -g @soulcraft/brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
Or run directly:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npx @soulcraft/brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
## Quick Start
|
||
|
|
|
||
|
|
1. **Start the service:**
|
||
|
|
```bash
|
||
|
|
brainy-server
|
||
|
|
```
|
||
|
|
|
||
|
|
2. **Check if it's running:**
|
||
|
|
```bash
|
||
|
|
curl http://localhost:3000/health
|
||
|
|
```
|
||
|
|
|
||
|
|
3. **View API documentation:**
|
||
|
|
```bash
|
||
|
|
curl http://localhost:3000/api
|
||
|
|
```
|
||
|
|
|
||
|
|
## 🚀 Easy Deployment
|
||
|
|
|
||
|
|
### Option 1: Direct NPM Deployment
|
||
|
|
```bash
|
||
|
|
# Install globally and run
|
||
|
|
npm install -g @soulcraft/brainy-web-service
|
||
|
|
brainy-server
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 2: One-liner with npx
|
||
|
|
```bash
|
||
|
|
# Run without installation
|
||
|
|
npx @soulcraft/brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 3: Docker Deployment
|
||
|
|
```bash
|
||
|
|
# Create a simple Dockerfile
|
||
|
|
cat > Dockerfile << EOF
|
||
|
|
FROM node:24-alpine
|
||
|
|
RUN npm install -g @soulcraft/brainy-web-service
|
||
|
|
EXPOSE 3000
|
||
|
|
CMD ["brainy-server"]
|
||
|
|
EOF
|
||
|
|
|
||
|
|
# Build and run
|
||
|
|
docker build -t brainy-web-service .
|
||
|
|
docker run -p 3000:3000 brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 4: Cloud Platform Deployment
|
||
|
|
|
||
|
|
#### Google Cloud Platform (App Engine)
|
||
|
|
```bash
|
||
|
|
# Create package.json
|
||
|
|
echo '{"scripts":{"start":"brainy-server"},"dependencies":{"@soulcraft/brainy-web-service":"latest"}}' > package.json
|
||
|
|
|
||
|
|
# Create app.yaml for App Engine
|
||
|
|
cat > app.yaml << EOF
|
||
|
|
runtime: nodejs20
|
||
|
|
env: standard
|
||
|
|
automatic_scaling:
|
||
|
|
min_instances: 1
|
||
|
|
max_instances: 10
|
||
|
|
EOF
|
||
|
|
|
||
|
|
# Deploy to App Engine
|
||
|
|
gcloud app deploy
|
||
|
|
```
|
||
|
|
|
||
|
|
#### AWS (App Runner)
|
||
|
|
```bash
|
||
|
|
# Create package.json
|
||
|
|
echo '{"scripts":{"start":"brainy-server"},"dependencies":{"@soulcraft/brainy-web-service":"latest"}}' > package.json
|
||
|
|
|
||
|
|
# Create apprunner.yaml
|
||
|
|
cat > apprunner.yaml << EOF
|
||
|
|
version: 1.0
|
||
|
|
runtime: nodejs20
|
||
|
|
build:
|
||
|
|
commands:
|
||
|
|
build:
|
||
|
|
- npm install
|
||
|
|
run:
|
||
|
|
runtime-version: 20
|
||
|
|
command: npm start
|
||
|
|
network:
|
||
|
|
port: 3000
|
||
|
|
env: PORT
|
||
|
|
EOF
|
||
|
|
|
||
|
|
# Deploy via AWS App Runner console or CLI
|
||
|
|
aws apprunner create-service --service-name brainy-web-service --source-configuration '{...}'
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Microsoft Azure (App Service)
|
||
|
|
```bash
|
||
|
|
# Create package.json
|
||
|
|
echo '{"scripts":{"start":"brainy-server"},"dependencies":{"@soulcraft/brainy-web-service":"latest"}}' > package.json
|
||
|
|
|
||
|
|
# Deploy using Azure CLI
|
||
|
|
az webapp create --resource-group myResourceGroup --plan myAppServicePlan --name brainy-web-service --runtime "NODE:20-lts"
|
||
|
|
az webapp deployment source config-zip --resource-group myResourceGroup --name brainy-web-service --src deployment.zip
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Cloudflare Workers
|
||
|
|
```bash
|
||
|
|
# Create package.json
|
||
|
|
echo '{"scripts":{"start":"brainy-server"},"dependencies":{"@soulcraft/brainy-web-service":"latest"}}' > package.json
|
||
|
|
|
||
|
|
# Create wrangler.toml
|
||
|
|
cat > wrangler.toml << EOF
|
||
|
|
name = "brainy-web-service"
|
||
|
|
main = "src/worker.js"
|
||
|
|
compatibility_date = "2024-01-01"
|
||
|
|
|
||
|
|
[env.production]
|
||
|
|
name = "brainy-web-service"
|
||
|
|
EOF
|
||
|
|
|
||
|
|
# Deploy with Wrangler
|
||
|
|
npx wrangler deploy
|
||
|
|
```
|
||
|
|
|
||
|
|
### Option 5: Serverless Deployment
|
||
|
|
```javascript
|
||
|
|
// For serverless platforms, wrap the service:
|
||
|
|
const express = require('express');
|
||
|
|
const app = require('@soulcraft/brainy-web-service');
|
||
|
|
|
||
|
|
module.exports = app;
|
||
|
|
```
|
||
|
|
|
||
|
|
## Configuration
|
||
|
|
|
||
|
|
Configure the service using environment variables:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Server configuration
|
||
|
|
PORT=3000 # Server port (default: 3000)
|
||
|
|
HOST=0.0.0.0 # Server host (default: 0.0.0.0)
|
||
|
|
|
||
|
|
# Local Storage configuration (fallback)
|
||
|
|
BRAINY_DATA_PATH=/path/to/data # Path to Brainy database files (used when no cloud storage configured)
|
||
|
|
FORCE_LOCAL_STORAGE=true # Force local filesystem storage (ignores cloud storage config)
|
||
|
|
|
||
|
|
# Security configuration
|
||
|
|
CORS_ORIGIN=* # CORS origin (default: *)
|
||
|
|
RATE_LIMIT_WINDOW=900000 # Rate limit window in ms (default: 15 minutes)
|
||
|
|
RATE_LIMIT_MAX=100 # Max requests per window (default: 100)
|
||
|
|
|
||
|
|
# Environment
|
||
|
|
NODE_ENV=production # Environment mode
|
||
|
|
```
|
||
|
|
|
||
|
|
### Cloud Storage Configuration
|
||
|
|
|
||
|
|
The service automatically detects and uses cloud storage when configured. If no cloud storage is configured, it falls back to local filesystem storage.
|
||
|
|
|
||
|
|
#### AWS S3
|
||
|
|
```bash
|
||
|
|
S3_BUCKET_NAME=my-brainy-bucket
|
||
|
|
S3_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE # or AWS_ACCESS_KEY_ID
|
||
|
|
S3_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY # or AWS_SECRET_ACCESS_KEY
|
||
|
|
S3_REGION=us-west-2 # or AWS_REGION
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Cloudflare R2
|
||
|
|
```bash
|
||
|
|
R2_BUCKET_NAME=my-brainy-bucket
|
||
|
|
R2_ACCOUNT_ID=your-account-id
|
||
|
|
R2_ACCESS_KEY_ID=your-r2-access-key
|
||
|
|
R2_SECRET_ACCESS_KEY=your-r2-secret-key
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Google Cloud Storage (S3-compatible)
|
||
|
|
```bash
|
||
|
|
GCS_BUCKET_NAME=my-brainy-bucket
|
||
|
|
GCS_ACCESS_KEY_ID=your-gcs-access-key
|
||
|
|
GCS_SECRET_ACCESS_KEY=your-gcs-secret-key
|
||
|
|
GCS_ENDPOINT=https://storage.googleapis.com # Optional, defaults to GCS endpoint
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Azure Blob Storage (S3-compatible)
|
||
|
|
For Azure, you can use the S3-compatible interface with a custom endpoint:
|
||
|
|
```bash
|
||
|
|
S3_BUCKET_NAME=my-brainy-container
|
||
|
|
S3_ACCESS_KEY_ID=your-storage-account-name
|
||
|
|
S3_SECRET_ACCESS_KEY=your-storage-account-key
|
||
|
|
S3_REGION=auto
|
||
|
|
# Note: Azure Blob Storage S3 compatibility may require additional configuration
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Custom S3-Compatible Storage
|
||
|
|
```bash
|
||
|
|
S3_BUCKET_NAME=my-brainy-bucket
|
||
|
|
S3_ACCESS_KEY_ID=your-access-key
|
||
|
|
S3_SECRET_ACCESS_KEY=your-secret-key
|
||
|
|
S3_REGION=us-east-1
|
||
|
|
# Set custom endpoint via application configuration
|
||
|
|
```
|
||
|
|
|
||
|
|
## API Endpoints
|
||
|
|
|
||
|
|
### Health & Status
|
||
|
|
|
||
|
|
#### `GET /health`
|
||
|
|
Health check endpoint.
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"status": "healthy",
|
||
|
|
"timestamp": "2025-07-22T09:33:00.000Z",
|
||
|
|
"service": "brainy-web-service",
|
||
|
|
"version": "0.12.0"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `GET /api`
|
||
|
|
API documentation and endpoint listing.
|
||
|
|
|
||
|
|
#### `GET /api/status`
|
||
|
|
Database status and statistics.
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"size": 1000,
|
||
|
|
"readOnly": true,
|
||
|
|
"timestamp": "2025-07-22T09:33:00.000Z"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Search Operations
|
||
|
|
|
||
|
|
#### `POST /api/search`
|
||
|
|
Search using a vector.
|
||
|
|
|
||
|
|
**Request Body:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"vector": [0.1, 0.2, 0.3, ...],
|
||
|
|
"k": 10,
|
||
|
|
"nounTypes": ["document", "image"],
|
||
|
|
"includeVerbs": false
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"results": [
|
||
|
|
{
|
||
|
|
"id": "item-1",
|
||
|
|
"score": 0.95,
|
||
|
|
"vector": [0.1, 0.2, 0.3, ...],
|
||
|
|
"metadata": {
|
||
|
|
"title": "Example Document",
|
||
|
|
"type": "document"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
],
|
||
|
|
"query": {
|
||
|
|
"vectorLength": 384,
|
||
|
|
"k": 10,
|
||
|
|
"nounTypes": ["document"],
|
||
|
|
"includeVerbs": false
|
||
|
|
},
|
||
|
|
"timestamp": "2025-07-22T09:33:00.000Z"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `POST /api/search/text`
|
||
|
|
Search using text query (automatically vectorized).
|
||
|
|
|
||
|
|
**Request Body:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"query": "machine learning algorithms",
|
||
|
|
"k": 5,
|
||
|
|
"nounTypes": ["document"],
|
||
|
|
"includeVerbs": false
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:** Same format as vector search.
|
||
|
|
|
||
|
|
### Data Retrieval
|
||
|
|
|
||
|
|
#### `GET /api/item/:id`
|
||
|
|
Get a specific item by ID.
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"item": {
|
||
|
|
"id": "item-1",
|
||
|
|
"vector": [0.1, 0.2, 0.3, ...],
|
||
|
|
"metadata": {
|
||
|
|
"title": "Example Document",
|
||
|
|
"type": "document"
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"timestamp": "2025-07-22T09:33:00.000Z"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `GET /api/items?page=1&limit=20`
|
||
|
|
Get all items with pagination.
|
||
|
|
|
||
|
|
**Query Parameters:**
|
||
|
|
- `page` (optional): Page number (default: 1)
|
||
|
|
- `limit` (optional): Items per page (default: 20, max: 100)
|
||
|
|
|
||
|
|
**Response:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"items": [...],
|
||
|
|
"pagination": {
|
||
|
|
"page": 1,
|
||
|
|
"limit": 20,
|
||
|
|
"total": 1000,
|
||
|
|
"totalPages": 50,
|
||
|
|
"hasNext": true,
|
||
|
|
"hasPrev": false
|
||
|
|
},
|
||
|
|
"timestamp": "2025-07-22T09:33:00.000Z"
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `POST /api/similar/:id`
|
||
|
|
Find items similar to a given item.
|
||
|
|
|
||
|
|
**Request Body:**
|
||
|
|
```json
|
||
|
|
{
|
||
|
|
"k": 10,
|
||
|
|
"includeVerbs": false
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Response:** Same format as search results.
|
||
|
|
|
||
|
|
## Usage Examples
|
||
|
|
|
||
|
|
### JavaScript/Node.js
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
const axios = require('axios');
|
||
|
|
|
||
|
|
const API_BASE = 'http://localhost:3000/api';
|
||
|
|
|
||
|
|
// Text search
|
||
|
|
async function searchText(query) {
|
||
|
|
const response = await axios.post(`${API_BASE}/search/text`, {
|
||
|
|
query: query,
|
||
|
|
k: 10
|
||
|
|
});
|
||
|
|
return response.data.results;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Vector search
|
||
|
|
async function searchVector(vector) {
|
||
|
|
const response = await axios.post(`${API_BASE}/search`, {
|
||
|
|
vector: vector,
|
||
|
|
k: 5
|
||
|
|
});
|
||
|
|
return response.data.results;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Get item by ID
|
||
|
|
async function getItem(id) {
|
||
|
|
const response = await axios.get(`${API_BASE}/item/${id}`);
|
||
|
|
return response.data.item;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Usage
|
||
|
|
searchText('artificial intelligence').then(results => {
|
||
|
|
console.log('Search results:', results);
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
### Python
|
||
|
|
|
||
|
|
```python
|
||
|
|
import requests
|
||
|
|
|
||
|
|
API_BASE = 'http://localhost:3000/api'
|
||
|
|
|
||
|
|
def search_text(query, k=10):
|
||
|
|
response = requests.post(f'{API_BASE}/search/text', json={
|
||
|
|
'query': query,
|
||
|
|
'k': k
|
||
|
|
})
|
||
|
|
return response.json()['results']
|
||
|
|
|
||
|
|
def search_vector(vector, k=10):
|
||
|
|
response = requests.post(f'{API_BASE}/search', json={
|
||
|
|
'vector': vector,
|
||
|
|
'k': k
|
||
|
|
})
|
||
|
|
return response.json()['results']
|
||
|
|
|
||
|
|
def get_item(item_id):
|
||
|
|
response = requests.get(f'{API_BASE}/item/{item_id}')
|
||
|
|
return response.json()['item']
|
||
|
|
|
||
|
|
# Usage
|
||
|
|
results = search_text('machine learning')
|
||
|
|
print(f'Found {len(results)} results')
|
||
|
|
```
|
||
|
|
|
||
|
|
### cURL
|
||
|
|
|
||
|
|
```bash
|
||
|
|
# Text search
|
||
|
|
curl -X POST http://localhost:3000/api/search/text \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"query": "artificial intelligence", "k": 5}'
|
||
|
|
|
||
|
|
# Vector search
|
||
|
|
curl -X POST http://localhost:3000/api/search \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
-d '{"vector": [0.1, 0.2, 0.3], "k": 10}'
|
||
|
|
|
||
|
|
# Get item
|
||
|
|
curl http://localhost:3000/api/item/example-id
|
||
|
|
|
||
|
|
# Get items with pagination
|
||
|
|
curl "http://localhost:3000/api/items?page=1&limit=10"
|
||
|
|
```
|
||
|
|
|
||
|
|
## Security Features
|
||
|
|
|
||
|
|
### Read-Only Protection
|
||
|
|
- Database is automatically set to read-only mode on startup
|
||
|
|
- No write operations (add, delete, update) are exposed
|
||
|
|
- Only safe search and retrieval operations are available
|
||
|
|
|
||
|
|
### Rate Limiting
|
||
|
|
- Default: 100 requests per 15-minute window per IP
|
||
|
|
- Configurable via environment variables
|
||
|
|
- Returns 429 status code when limit exceeded
|
||
|
|
|
||
|
|
### Input Validation
|
||
|
|
- All inputs are validated using express-validator
|
||
|
|
- Vector dimensions and types are checked
|
||
|
|
- String lengths are limited to prevent abuse
|
||
|
|
- Numeric ranges are enforced
|
||
|
|
|
||
|
|
### Security Headers
|
||
|
|
- Helmet.js provides security headers
|
||
|
|
- Content Security Policy configured
|
||
|
|
- CORS properly configured
|
||
|
|
- Compression enabled for performance
|
||
|
|
|
||
|
|
### Error Handling
|
||
|
|
- Detailed error messages in development
|
||
|
|
- Generic error messages in production
|
||
|
|
- All errors are logged for monitoring
|
||
|
|
- Graceful shutdown on SIGTERM/SIGINT
|
||
|
|
|
||
|
|
## Deployment
|
||
|
|
|
||
|
|
### Docker
|
||
|
|
|
||
|
|
Create a `Dockerfile`:
|
||
|
|
|
||
|
|
```dockerfile
|
||
|
|
FROM node:24-alpine
|
||
|
|
|
||
|
|
# Create non-root user for security
|
||
|
|
RUN addgroup -g 1001 -S brainy && \
|
||
|
|
adduser -S brainy -u 1001
|
||
|
|
|
||
|
|
WORKDIR /app
|
||
|
|
|
||
|
|
# Copy package files
|
||
|
|
COPY package*.json ./
|
||
|
|
RUN npm ci --only=production
|
||
|
|
|
||
|
|
# Copy application
|
||
|
|
COPY . .
|
||
|
|
RUN npm run build
|
||
|
|
|
||
|
|
# Create data directory and set permissions
|
||
|
|
RUN mkdir -p /app/data && chown -R brainy:brainy /app
|
||
|
|
|
||
|
|
# Switch to non-root user
|
||
|
|
USER brainy
|
||
|
|
|
||
|
|
# Expose port
|
||
|
|
EXPOSE 3000
|
||
|
|
|
||
|
|
# Set environment
|
||
|
|
ENV NODE_ENV=production
|
||
|
|
|
||
|
|
# Start service
|
||
|
|
CMD ["npm", "start"]
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Local Storage Deployment
|
||
|
|
```bash
|
||
|
|
docker build -t brainy-web-service .
|
||
|
|
docker run -p 3000:3000 \
|
||
|
|
-v /host/data:/app/data \
|
||
|
|
-e BRAINY_DATA_PATH=/app/data \
|
||
|
|
brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
#### AWS S3 Deployment
|
||
|
|
```bash
|
||
|
|
docker run -p 3000:3000 \
|
||
|
|
-e S3_BUCKET_NAME=my-brainy-bucket \
|
||
|
|
-e S3_ACCESS_KEY_ID=your-access-key \
|
||
|
|
-e S3_SECRET_ACCESS_KEY=your-secret-key \
|
||
|
|
-e S3_REGION=us-west-2 \
|
||
|
|
brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Cloudflare R2 Deployment
|
||
|
|
```bash
|
||
|
|
docker run -p 3000:3000 \
|
||
|
|
-e R2_BUCKET_NAME=my-brainy-bucket \
|
||
|
|
-e R2_ACCOUNT_ID=your-account-id \
|
||
|
|
-e R2_ACCESS_KEY_ID=your-r2-access-key \
|
||
|
|
-e R2_SECRET_ACCESS_KEY=your-r2-secret-key \
|
||
|
|
brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
#### Google Cloud Storage Deployment
|
||
|
|
```bash
|
||
|
|
docker run -p 3000:3000 \
|
||
|
|
-e GCS_BUCKET_NAME=my-brainy-bucket \
|
||
|
|
-e GCS_ACCESS_KEY_ID=your-gcs-access-key \
|
||
|
|
-e GCS_SECRET_ACCESS_KEY=your-gcs-secret-key \
|
||
|
|
brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
### Docker Compose
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
brainy-web-service:
|
||
|
|
build: .
|
||
|
|
ports:
|
||
|
|
- "3000:3000"
|
||
|
|
volumes:
|
||
|
|
- ./data:/app/data
|
||
|
|
environment:
|
||
|
|
- NODE_ENV=production
|
||
|
|
- BRAINY_DATA_PATH=/app/data
|
||
|
|
- RATE_LIMIT_MAX=200
|
||
|
|
restart: unless-stopped
|
||
|
|
```
|
||
|
|
|
||
|
|
### Systemd Service
|
||
|
|
|
||
|
|
Create `/etc/systemd/system/brainy-web-service.service`:
|
||
|
|
|
||
|
|
```ini
|
||
|
|
[Unit]
|
||
|
|
Description=Brainy Web Service
|
||
|
|
After=network.target
|
||
|
|
|
||
|
|
[Service]
|
||
|
|
Type=simple
|
||
|
|
User=brainy
|
||
|
|
WorkingDirectory=/opt/brainy-web-service
|
||
|
|
ExecStart=/usr/bin/node dist/server.js
|
||
|
|
Restart=always
|
||
|
|
RestartSec=10
|
||
|
|
Environment=NODE_ENV=production
|
||
|
|
Environment=BRAINY_DATA_PATH=/opt/brainy-web-service/data
|
||
|
|
Environment=PORT=3000
|
||
|
|
|
||
|
|
[Install]
|
||
|
|
WantedBy=multi-user.target
|
||
|
|
```
|
||
|
|
|
||
|
|
Enable and start:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
sudo systemctl enable brainy-web-service
|
||
|
|
sudo systemctl start brainy-web-service
|
||
|
|
```
|
||
|
|
|
||
|
|
### Reverse Proxy (Nginx)
|
||
|
|
|
||
|
|
```nginx
|
||
|
|
server {
|
||
|
|
listen 80;
|
||
|
|
server_name your-domain.com;
|
||
|
|
|
||
|
|
location / {
|
||
|
|
proxy_pass http://localhost:3000;
|
||
|
|
proxy_http_version 1.1;
|
||
|
|
proxy_set_header Upgrade $http_upgrade;
|
||
|
|
proxy_set_header Connection 'upgrade';
|
||
|
|
proxy_set_header Host $host;
|
||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||
|
|
proxy_cache_bypass $http_upgrade;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
## Development
|
||
|
|
|
||
|
|
### Setup
|
||
|
|
|
||
|
|
```bash
|
||
|
|
git clone https://github.com/soulcraft-research/brainy.git
|
||
|
|
cd brainy/web-service-package
|
||
|
|
npm install
|
||
|
|
```
|
||
|
|
|
||
|
|
### Development Mode
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm run dev
|
||
|
|
```
|
||
|
|
|
||
|
|
### Building
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm run build
|
||
|
|
```
|
||
|
|
|
||
|
|
### Testing
|
||
|
|
|
||
|
|
```bash
|
||
|
|
npm test
|
||
|
|
```
|
||
|
|
|
||
|
|
## Monitoring
|
||
|
|
|
||
|
|
### Health Checks
|
||
|
|
|
||
|
|
The service provides health check endpoints for monitoring:
|
||
|
|
|
||
|
|
- `GET /health` - Basic health check
|
||
|
|
- `GET /api/status` - Detailed database status
|
||
|
|
|
||
|
|
### Logging
|
||
|
|
|
||
|
|
All requests and errors are logged to console. In production, consider using a logging service like Winston or Bunyan.
|
||
|
|
|
||
|
|
### Metrics
|
||
|
|
|
||
|
|
Consider integrating with monitoring solutions:
|
||
|
|
- Prometheus metrics
|
||
|
|
- New Relic
|
||
|
|
- DataDog
|
||
|
|
- Custom metrics via the status endpoint
|
||
|
|
|
||
|
|
## Troubleshooting
|
||
|
|
|
||
|
|
### Common Issues
|
||
|
|
|
||
|
|
1. **Service won't start**
|
||
|
|
- Check if the data directory exists and is readable
|
||
|
|
- Verify Node.js version (requires >= 24.4.0)
|
||
|
|
- Check port availability
|
||
|
|
|
||
|
|
2. **Database not found**
|
||
|
|
- Ensure `BRAINY_DATA_PATH` points to valid Brainy database files
|
||
|
|
- Check file permissions
|
||
|
|
|
||
|
|
3. **Rate limiting issues**
|
||
|
|
- Adjust `RATE_LIMIT_MAX` and `RATE_LIMIT_WINDOW`
|
||
|
|
- Consider implementing IP whitelisting
|
||
|
|
|
||
|
|
4. **Memory issues**
|
||
|
|
- Monitor memory usage with large databases
|
||
|
|
- Consider implementing pagination for large result sets
|
||
|
|
|
||
|
|
### Debug Mode
|
||
|
|
|
||
|
|
Enable debug logging:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
NODE_ENV=development npm start
|
||
|
|
```
|
||
|
|
|
||
|
|
## Contributing
|
||
|
|
|
||
|
|
1. Fork the repository
|
||
|
|
2. Create a feature branch
|
||
|
|
3. Make your changes
|
||
|
|
4. Add tests
|
||
|
|
5. Submit a pull request
|
||
|
|
|
||
|
|
## License
|
||
|
|
|
||
|
|
MIT License - see the main Brainy project for details.
|
||
|
|
|
||
|
|
## Support
|
||
|
|
|
||
|
|
- GitHub Issues: https://github.com/soulcraft-research/brainy/issues
|
||
|
|
- Documentation: https://github.com/soulcraft-research/brainy
|
||
|
|
- Email: david@soulcraft.com
|