Spaces:
danilonovais
/
Running on CPU Upgrade

danilonovais commited on
Commit
a7a6ad0
·
1 Parent(s): 0c10583

Refactor infra: update n8n, simplify Docker, add knowledge sync

Browse files

Upgrades n8n to 1.108.2 and simplifies Dockerfile and docker-compose.yml for local and Hugging Face Spaces deployment. Adds scripts for knowledge base sync with Supabase and OpenAI embeddings, and provides a Supabase schema for documents and embeddings. Updates backup/restore scripts for direct Postgres and n8n API usage. Enhances README with ChromaDB setup and clarifies instructions. Adds a knowledge sync workflow blueprint and VSCode spell checker settings.

.vscode/settings.json CHANGED
@@ -1,3 +1,10 @@
1
  {
2
- "DockerRun.DisableDockerrc": true
 
 
 
 
 
 
 
3
  }
 
1
  {
2
+ "DockerRun.DisableDockerrc": true,
3
+ "spellright.language": [],
4
+ "spellright.documentTypes": ["markdown", "latex", "plaintext"],
5
+ "spellright.parserByClass": {
6
+ "code-runner-output": {
7
+ "parser": "markdown"
8
+ }
9
+ }
10
  }
README.md CHANGED
@@ -5,18 +5,21 @@ A comprehensive, production-ready infrastructure setup for deploying n8n automat
5
  ## 🚀 Features
6
 
7
  ### Core Platform
 
8
  - **n8n v1.17.1**: Self-hosted workflow automation platform
9
  - **Hugging Face Spaces**: Docker-based deployment with automatic scaling
10
  - **Supabase PostgreSQL**: SSL-encrypted database with pgvector extension
11
  - **ChromaDB**: Vector store for embeddings and AI-powered search
12
 
13
  ### AI & Automation
 
14
  - **LangChain Integration**: Advanced AI workflow capabilities
15
  - **Multi-Model Support**: OpenAI GPT, Anthropic Claude, Google Vertex AI
16
  - **Vector Knowledge Base**: Automated content ingestion with embeddings
17
  - **Community Nodes**: Extended functionality with custom AI nodes
18
 
19
  ### DevOps & Monitoring
 
20
  - **GitHub Actions CI/CD**: Automated deployment and maintenance
21
  - **Automated Backups**: Daily workflow and configuration backups
22
  - **Knowledge Sync**: Multi-repository content synchronization
@@ -100,13 +103,14 @@ gh workflow run deploy-to-hf.yml
100
  ### Supabase Configuration
101
 
102
  1. **Create Supabase Project**:
 
103
  ```sql
104
  -- Enable pgvector extension
105
  CREATE EXTENSION IF NOT EXISTS vector;
106
-
107
  -- Create knowledge base schema
108
  CREATE SCHEMA IF NOT EXISTS knowledge;
109
-
110
  -- Create embeddings table
111
  CREATE TABLE knowledge.embeddings (
112
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
@@ -118,23 +122,24 @@ gh workflow run deploy-to-hf.yml
118
  created_at TIMESTAMPTZ DEFAULT NOW(),
119
  updated_at TIMESTAMPTZ DEFAULT NOW()
120
  );
121
-
122
  -- Create indexes for performance
123
  CREATE INDEX IF NOT EXISTS idx_embeddings_collection ON knowledge.embeddings(collection_name);
124
  CREATE INDEX IF NOT EXISTS idx_embeddings_content_id ON knowledge.embeddings(content_id);
125
- CREATE INDEX IF NOT EXISTS idx_embeddings_vector ON knowledge.embeddings
126
  USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
127
  ```
128
 
129
  2. **Configure Row Level Security**:
 
130
  ```sql
131
  -- Enable RLS
132
  ALTER TABLE knowledge.embeddings ENABLE ROW LEVEL SECURITY;
133
-
134
  -- Allow authenticated users to read embeddings
135
  CREATE POLICY "Users can read embeddings" ON knowledge.embeddings
136
  FOR SELECT TO authenticated USING (true);
137
-
138
  -- Allow service role to manage embeddings
139
  CREATE POLICY "Service role can manage embeddings" ON knowledge.embeddings
140
  FOR ALL TO service_role USING (true);
@@ -193,7 +198,7 @@ docker-compose -f docker/docker-compose.yml restart n8n
193
  The system automatically syncs content from these repositories:
194
 
195
  - **n8n Knowledge**: `/projects/n8n` - Workflow examples and best practices
196
- - **Video & Animation**: `/projects/videos-e-animacoes` - Multimedia processing guides
197
  - **Midjourney Prompts**: `/projects/midjorney-prompt` - AI art generation prompts
198
 
199
  ### Manual Knowledge Sync
@@ -225,6 +230,7 @@ Query the knowledge base in n8n workflows:
225
  ### Automated Backups
226
 
227
  Daily backups include:
 
228
  - All n8n workflows (exported as JSON)
229
  - Encrypted credentials
230
  - Database schema
@@ -268,12 +274,13 @@ curl http://localhost:8000/api/v1/heartbeat
268
  ### Performance Tuning
269
 
270
  **Database Optimization**:
 
271
  ```sql
272
  -- Monitor query performance
273
- SELECT query, mean_exec_time, calls
274
- FROM pg_stat_statements
275
- WHERE query LIKE '%n8n%'
276
- ORDER BY mean_exec_time DESC
277
  LIMIT 10;
278
 
279
  -- Optimize vector searches
@@ -281,6 +288,7 @@ SET ivfflat.probes = 10;
281
  ```
282
 
283
  **Container Resources**:
 
284
  ```yaml
285
  # docker-compose.yml resource limits
286
  services:
@@ -288,10 +296,10 @@ services:
288
  deploy:
289
  resources:
290
  limits:
291
- cpus: '2.0'
292
  memory: 4G
293
  reservations:
294
- cpus: '1.0'
295
  memory: 2G
296
  ```
297
 
@@ -327,6 +335,7 @@ chmod 600 config/credentials/*
327
  ### Common Issues
328
 
329
  **Connection Problems**:
 
330
  ```bash
331
  # Test database connection
332
  docker exec n8n-automation psql "$DB_POSTGRESDB_HOST" -U "$DB_POSTGRESDB_USER" -c "\l"
@@ -339,6 +348,7 @@ curl -I "$WEBHOOK_URL/healthz"
339
  ```
340
 
341
  **Deployment Issues**:
 
342
  ```bash
343
  # Check Hugging Face Space status
344
  curl -I "https://huggingface.co/spaces/$HF_USERNAME/$HF_SPACE_NAME"
@@ -349,6 +359,7 @@ gh run view [run-id] --log
349
  ```
350
 
351
  **Knowledge Sync Problems**:
 
352
  ```bash
353
  # Manual knowledge sync debug
354
  ./scripts/sync-knowledge.sh
@@ -366,16 +377,18 @@ with open('knowledge/n8n/n8n_embeddings.json') as f:
366
  ### Recovery Procedures
367
 
368
  **Emergency Restore**:
 
369
  1. Stop all services: `docker-compose down`
370
  2. Restore from latest backup: `./scripts/restore.sh [backup-name]`
371
  3. Restart services: `docker-compose up -d`
372
  4. Verify functionality: Access web interface
373
 
374
  **Database Recovery**:
 
375
  ```sql
376
  -- Check database integrity
377
- SELECT schemaname, tablename, n_tup_ins, n_tup_upd, n_tup_del
378
- FROM pg_stat_user_tables
379
  WHERE schemaname = 'public';
380
 
381
  -- Rebuild vector indexes if needed
@@ -455,4 +468,28 @@ This project is licensed under the Apache License 2.0 - see the LICENSE file for
455
 
456
  ---
457
 
458
- *Built with ❤️ for the n8n automation community*
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  ## 🚀 Features
6
 
7
  ### Core Platform
8
+
9
  - **n8n v1.17.1**: Self-hosted workflow automation platform
10
  - **Hugging Face Spaces**: Docker-based deployment with automatic scaling
11
  - **Supabase PostgreSQL**: SSL-encrypted database with pgvector extension
12
  - **ChromaDB**: Vector store for embeddings and AI-powered search
13
 
14
  ### AI & Automation
15
+
16
  - **LangChain Integration**: Advanced AI workflow capabilities
17
  - **Multi-Model Support**: OpenAI GPT, Anthropic Claude, Google Vertex AI
18
  - **Vector Knowledge Base**: Automated content ingestion with embeddings
19
  - **Community Nodes**: Extended functionality with custom AI nodes
20
 
21
  ### DevOps & Monitoring
22
+
23
  - **GitHub Actions CI/CD**: Automated deployment and maintenance
24
  - **Automated Backups**: Daily workflow and configuration backups
25
  - **Knowledge Sync**: Multi-repository content synchronization
 
103
  ### Supabase Configuration
104
 
105
  1. **Create Supabase Project**:
106
+
107
  ```sql
108
  -- Enable pgvector extension
109
  CREATE EXTENSION IF NOT EXISTS vector;
110
+
111
  -- Create knowledge base schema
112
  CREATE SCHEMA IF NOT EXISTS knowledge;
113
+
114
  -- Create embeddings table
115
  CREATE TABLE knowledge.embeddings (
116
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
 
122
  created_at TIMESTAMPTZ DEFAULT NOW(),
123
  updated_at TIMESTAMPTZ DEFAULT NOW()
124
  );
125
+
126
  -- Create indexes for performance
127
  CREATE INDEX IF NOT EXISTS idx_embeddings_collection ON knowledge.embeddings(collection_name);
128
  CREATE INDEX IF NOT EXISTS idx_embeddings_content_id ON knowledge.embeddings(content_id);
129
+ CREATE INDEX IF NOT EXISTS idx_embeddings_vector ON knowledge.embeddings
130
  USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
131
  ```
132
 
133
  2. **Configure Row Level Security**:
134
+
135
  ```sql
136
  -- Enable RLS
137
  ALTER TABLE knowledge.embeddings ENABLE ROW LEVEL SECURITY;
138
+
139
  -- Allow authenticated users to read embeddings
140
  CREATE POLICY "Users can read embeddings" ON knowledge.embeddings
141
  FOR SELECT TO authenticated USING (true);
142
+
143
  -- Allow service role to manage embeddings
144
  CREATE POLICY "Service role can manage embeddings" ON knowledge.embeddings
145
  FOR ALL TO service_role USING (true);
 
198
  The system automatically syncs content from these repositories:
199
 
200
  - **n8n Knowledge**: `/projects/n8n` - Workflow examples and best practices
201
+ - **Video & Animation**: `/projects/videos-e-animacoes` - Multimedia processing guides
202
  - **Midjourney Prompts**: `/projects/midjorney-prompt` - AI art generation prompts
203
 
204
  ### Manual Knowledge Sync
 
230
  ### Automated Backups
231
 
232
  Daily backups include:
233
+
234
  - All n8n workflows (exported as JSON)
235
  - Encrypted credentials
236
  - Database schema
 
274
  ### Performance Tuning
275
 
276
  **Database Optimization**:
277
+
278
  ```sql
279
  -- Monitor query performance
280
+ SELECT query, mean_exec_time, calls
281
+ FROM pg_stat_statements
282
+ WHERE query LIKE '%n8n%'
283
+ ORDER BY mean_exec_time DESC
284
  LIMIT 10;
285
 
286
  -- Optimize vector searches
 
288
  ```
289
 
290
  **Container Resources**:
291
+
292
  ```yaml
293
  # docker-compose.yml resource limits
294
  services:
 
296
  deploy:
297
  resources:
298
  limits:
299
+ cpus: "2.0"
300
  memory: 4G
301
  reservations:
302
+ cpus: "1.0"
303
  memory: 2G
304
  ```
305
 
 
335
  ### Common Issues
336
 
337
  **Connection Problems**:
338
+
339
  ```bash
340
  # Test database connection
341
  docker exec n8n-automation psql "$DB_POSTGRESDB_HOST" -U "$DB_POSTGRESDB_USER" -c "\l"
 
348
  ```
349
 
350
  **Deployment Issues**:
351
+
352
  ```bash
353
  # Check Hugging Face Space status
354
  curl -I "https://huggingface.co/spaces/$HF_USERNAME/$HF_SPACE_NAME"
 
359
  ```
360
 
361
  **Knowledge Sync Problems**:
362
+
363
  ```bash
364
  # Manual knowledge sync debug
365
  ./scripts/sync-knowledge.sh
 
377
  ### Recovery Procedures
378
 
379
  **Emergency Restore**:
380
+
381
  1. Stop all services: `docker-compose down`
382
  2. Restore from latest backup: `./scripts/restore.sh [backup-name]`
383
  3. Restart services: `docker-compose up -d`
384
  4. Verify functionality: Access web interface
385
 
386
  **Database Recovery**:
387
+
388
  ```sql
389
  -- Check database integrity
390
+ SELECT schemaname, tablename, n_tup_ins, n_tup_upd, n_tup_del
391
+ FROM pg_stat_user_tables
392
  WHERE schemaname = 'public';
393
 
394
  -- Rebuild vector indexes if needed
 
468
 
469
  ---
470
 
471
+ _Built with ❤️ for the n8n automation community_
472
+
473
+ ### ChromaDB
474
+
475
+ ChromaDB é utilizado como vector store para armazenar embeddings e permitir buscas semânticas avançadas nos fluxos de trabalho do n8n.
476
+
477
+ #### Configuração
478
+
479
+ 1. **Obtenha seu token de autenticação (API Key) no painel do Chroma Cloud**.
480
+ 2. No arquivo `.env`, adicione as variáveis:
481
+
482
+ ```dotenv
483
+ CHROMA_AUTH_TOKEN=ck-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
484
+ CHROMA_HOST=api.chroma.com
485
+ CHROMA_PORT=443
486
+ ```
487
+
488
+ 3. Certifique-se de que o serviço Chroma está acessível e que o token está correto.
489
+
490
+ 4. Para uso local, ajuste `CHROMA_HOST` para `localhost` e `CHROMA_PORT` para a porta configurada.
491
+
492
+ #### Referências
493
+
494
+ - [Documentação ChromaDB](https://docs.trychroma.com/)
495
+ - [Como gerar API Key no Chroma Cloud](https://docs.trychroma.com/cloud)
docker/Dockerfile CHANGED
@@ -1,56 +1,39 @@
1
- # Production-ready n8n Dockerfile for Hugging Face Spaces
2
- FROM n8nio/n8n:1.17.1
3
-
4
- # Set environment for production
5
- ENV NODE_ENV=production
6
- ENV N8N_PORT=7860
7
- ENV N8N_LISTEN_ADDRESS=0.0.0.0
8
- ENV N8N_PROTOCOL=https
 
 
 
 
 
 
 
 
 
 
9
  ENV N8N_METRICS=true
10
- ENV N8N_LOG_LEVEL=info
11
- ENV N8N_LOG_OUTPUT=console
12
- ENV EXECUTIONS_MODE=queue
13
- ENV N8N_DISABLE_UI=false
14
-
15
- # Create app user for security
16
- USER root
17
-
18
- # Install additional dependencies for AI and automation
19
- RUN apk add --no-cache \
20
- curl \
21
- git \
22
- python3 \
23
- py3-pip \
24
- postgresql-client \
25
- && pip3 install --no-cache-dir \
26
- langchain \
27
- chromadb \
28
- sentence-transformers
29
-
30
- # Create necessary directories
31
- RUN mkdir -p /home/node/.n8n/custom \
32
- && mkdir -p /home/node/.n8n/nodes \
33
- && mkdir -p /home/node/.n8n/credentials \
34
- && mkdir -p /home/node/knowledge
35
-
36
- # Copy custom configurations
37
- COPY config/custom-nodes.json /home/node/.n8n/custom/
38
- COPY knowledge/ /home/node/knowledge/
39
-
40
- # Set proper permissions
41
- RUN chown -R node:node /home/node/.n8n \
42
- && chown -R node:node /home/node/knowledge \
43
- && chmod +x /home/node/knowledge/sync-*.sh
44
-
45
- # Switch back to node user
46
- USER node
47
-
48
- # Health check
49
- HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
50
- CMD curl -f http://localhost:7860/healthz || exit 1
51
-
52
- # Expose port for Hugging Face Spaces
53
- EXPOSE 7860
54
-
55
- # Start n8n
56
- CMD ["n8n", "start"]
 
1
+ # Pin the n8n version for predictable upgrades/rollbacks
2
+ FROM n8nio/n8n:1.108.2
3
+
4
+ # Hugging Face Spaces injects $PORT; n8n should listen on it
5
+ ENV N8N_PORT=$PORT
6
+ ENV N8N_PROTOCOL=http
7
+ # Public URL (important for webhooks behind HF proxy)
8
+ ENV WEBHOOK_URL=${WEBHOOK_URL}
9
+
10
+ # Execution & retention (production-friendly defaults)
11
+ ENV EXECUTIONS_MODE=regular
12
+ ENV EXECUTIONS_DATA_SAVE_ON_ERROR=all
13
+ ENV EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
14
+ ENV EXECUTIONS_DATA_PRUNE=true
15
+ ENV EXECUTIONS_DATA_MAX_AGE=336
16
+ ENV QUEUE_BULL_REDIS_DISABLED=true
17
+
18
+ # Health/metrics
19
  ENV N8N_METRICS=true
20
+ ENV QUEUE_HEALTH_CHECK_ACTIVE=true
21
+
22
+ # Database (set via Secrets in HF Space)
23
+ # ENV DB_TYPE=postgresdb
24
+ # ENV DB_POSTGRESDB_HOST=
25
+ # ENV DB_POSTGRESDB_PORT=5432
26
+ # ENV DB_POSTGRESDB_DATABASE=
27
+ # ENV DB_POSTGRESDB_USER=
28
+ # ENV DB_POSTGRESDB_PASSWORD=
29
+ # ENV DB_POSTGRESDB_SCHEMA=public
30
+ # ENV DB_POSTGRESDB_SSL=true
31
+ # ENV DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=false
32
+
33
+ # Security
34
+ # ENV N8N_ENCRYPTION_KEY=
35
+ # ENV N8N_USER_MANAGEMENT_JWT_SECRET=
36
+ # Optional: protect UI with Basic Auth
37
+ # ENV N8N_BASIC_AUTH_ACTIVE=true
38
+ # ENV N8N_BASIC_AUTH_USER=
39
+ # ENV N8N_BASIC_AUTH_PASSWORD=
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
docker/docker-compose.yml CHANGED
@@ -1,101 +1,33 @@
1
- version: '3.8'
2
-
3
  services:
4
  n8n:
5
- build:
6
- context: ..
7
- dockerfile: docker/Dockerfile
8
- container_name: n8n-automation
9
- restart: unless-stopped
10
  ports:
11
- - "7860:7860"
12
  environment:
13
- # Core Configuration
14
- - NODE_ENV=production
15
- - N8N_PORT=7860
16
- - N8N_LISTEN_ADDRESS=0.0.0.0
17
- - N8N_PROTOCOL=https
18
- - N8N_HOST=${N8N_HOST:-localhost}
19
-
20
- # Security
21
- - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
22
- - N8N_USER_MANAGEMENT_JWT_SECRET=${N8N_USER_MANAGEMENT_JWT_SECRET}
23
-
24
- # Database Configuration
25
  - DB_TYPE=postgresdb
26
- - DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}
27
- - DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT:-5432}
28
- - DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
29
- - DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
30
- - DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
 
31
  - DB_POSTGRESDB_SSL=true
32
-
33
- # Webhooks and External Access
34
- - WEBHOOK_URL=${WEBHOOK_URL}
35
- - N8N_EDITOR_BASE_URL=${WEBHOOK_URL}
36
-
37
- # AI and External Integrations
38
- - GOOGLE_PROJECT_ID=${GOOGLE_PROJECT_ID}
39
- - GOOGLE_CREDENTIALS_PATH=${GOOGLE_CREDENTIALS_PATH}
40
- - OPENAI_API_KEY=${OPENAI_API_KEY}
41
- - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
42
-
43
- # Performance
44
- - EXECUTIONS_MODE=queue
45
  - EXECUTIONS_DATA_SAVE_ON_ERROR=all
46
- - EXECUTIONS_DATA_SAVE_ON_SUCCESS=all
47
- - EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
48
-
49
- # Logging and Monitoring
50
- - N8N_LOG_LEVEL=info
51
- - N8N_LOG_OUTPUT=console
52
  - N8N_METRICS=true
53
-
 
 
54
  volumes:
55
- - n8n_data:/home/node/.n8n
56
- - ./knowledge:/home/node/knowledge:ro
57
- - ./config/credentials:/home/node/.n8n/credentials:ro
58
- - ./workflows/backup:/home/node/.n8n/backup
59
-
60
- networks:
61
- - n8n-network
62
-
63
- depends_on:
64
- - vector-store
65
- - backup-service
66
-
67
- vector-store:
68
- image: chromadb/chroma:latest
69
- container_name: n8n-vector-store
70
  restart: unless-stopped
71
- ports:
72
- - "8000:8000"
73
- environment:
74
- - CHROMA_SERVER_AUTHN_CREDENTIALS=${CHROMA_AUTH_TOKEN}
75
- - CHROMA_SERVER_AUTHN_PROVIDER=chromadb.auth.token.TokenAuthServerProvider
76
- volumes:
77
- - vector_data:/chroma/chroma
78
- networks:
79
- - n8n-network
80
-
81
- backup-service:
82
- image: alpine:latest
83
- container_name: n8n-backup
84
- restart: "no"
85
- command: /bin/sh -c "while true; do sleep 3600; done"
86
- volumes:
87
- - n8n_data:/backup/n8n:ro
88
- - ./scripts:/scripts:ro
89
- - ./workflows/backup:/backup/workflows
90
- networks:
91
- - n8n-network
92
-
93
- volumes:
94
- n8n_data:
95
- driver: local
96
- vector_data:
97
- driver: local
98
-
99
- networks:
100
- n8n-network:
101
- driver: bridge
 
1
+ version: "3.9"
 
2
  services:
3
  n8n:
4
+ image: n8nio/n8n:1.108.2
5
+ container_name: n8n-local
 
 
 
6
  ports:
7
+ - "5678:5678"
8
  environment:
9
+ - N8N_PORT=5678
10
+ - N8N_PROTOCOL=http
11
+ - WEBHOOK_URL=http://localhost:5678/
 
 
 
 
 
 
 
 
 
12
  - DB_TYPE=postgresdb
13
+ - DB_POSTGRESDB_HOST=${DB_HOST}
14
+ - DB_POSTGRESDB_PORT=${DB_PORT}
15
+ - DB_POSTGRESDB_DATABASE=${DB_NAME}
16
+ - DB_POSTGRESDB_USER=${DB_USER}
17
+ - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
18
+ - DB_POSTGRESDB_SCHEMA=public
19
  - DB_POSTGRESDB_SSL=true
20
+ - DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=false
21
+ - EXECUTIONS_MODE=regular
 
 
 
 
 
 
 
 
 
 
 
22
  - EXECUTIONS_DATA_SAVE_ON_ERROR=all
23
+ - EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
24
+ - EXECUTIONS_DATA_PRUNE=true
25
+ - EXECUTIONS_DATA_MAX_AGE=336
 
 
 
26
  - N8N_METRICS=true
27
+ - QUEUE_HEALTH_CHECK_ACTIVE=true
28
+ - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
29
+ - N8N_USER_MANAGEMENT_JWT_SECRET=${N8N_USER_MANAGEMENT_JWT_SECRET}
30
  volumes:
31
+ - ./config:/config
32
+ - ./workflows:/workflows
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  restart: unless-stopped
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package.json CHANGED
@@ -5,7 +5,7 @@
5
  "private": true,
6
  "keywords": [
7
  "n8n",
8
- "workflow-automation",
9
  "ai-integration",
10
  "langchain",
11
  "vector-database",
@@ -14,25 +14,25 @@
14
  "devops"
15
  ],
16
  "scripts": {
17
- "dev": "echo 'Docker is not available in WebContainer environment. Please run this project on a system with Docker installed.'",
18
- "start": "echo 'Docker is not available in WebContainer environment. Please run this project on a system with Docker installed.'",
19
- "stop": "echo 'Docker is not available in WebContainer environment. Please run this project on a system with Docker installed.'",
20
- "logs": "echo 'Docker is not available in WebContainer environment. Please run this project on a system with Docker installed.'",
21
  "backup": "bash scripts/backup.sh",
22
  "restore": "bash scripts/restore.sh",
23
  "sync-knowledge": "bash scripts/sync-knowledge.sh",
24
- "build": "echo 'Docker is not available in WebContainer environment. Please run this project on a system with Docker installed.'",
25
- "clean": "echo 'Docker is not available in WebContainer environment. Please run this project on a system with Docker installed.'",
26
  "deploy": "gh workflow run deploy-to-hf.yml",
27
  "test": "bash scripts/test-infrastructure.sh"
28
  },
29
  "repository": {
30
  "type": "git",
31
- "url": "https://github.com/your-username/n8n-infra.git"
32
  },
33
  "author": {
34
- "name": "Your Name",
35
- "email": "your-email@example.com"
36
  },
37
  "license": "Apache-2.0",
38
  "engines": {
@@ -42,4 +42,4 @@
42
  "devDependencies": {
43
  "@types/node": "^20.0.0"
44
  }
45
- }
 
5
  "private": true,
6
  "keywords": [
7
  "n8n",
8
+ "workflow-automation",
9
  "ai-integration",
10
  "langchain",
11
  "vector-database",
 
14
  "devops"
15
  ],
16
  "scripts": {
17
+ "dev": "echo 'Use Docker Compose para rodar o ambiente: docker-compose -f docker/docker-compose.yml up -d'",
18
+ "start": "echo 'Use Docker Compose para rodar o ambiente: docker-compose -f docker/docker-compose.yml up -d'",
19
+ "stop": "docker-compose -f docker/docker-compose.yml down",
20
+ "logs": "docker-compose -f docker/docker-compose.yml logs -f n8n",
21
  "backup": "bash scripts/backup.sh",
22
  "restore": "bash scripts/restore.sh",
23
  "sync-knowledge": "bash scripts/sync-knowledge.sh",
24
+ "build": "docker-compose -f docker/docker-compose.yml build",
25
+ "clean": "docker system prune -af",
26
  "deploy": "gh workflow run deploy-to-hf.yml",
27
  "test": "bash scripts/test-infrastructure.sh"
28
  },
29
  "repository": {
30
  "type": "git",
31
+ "url": "https://github.com/danilonovaisv/n8n-infra.git"
32
  },
33
  "author": {
34
+ "name": "Danilo Novais",
35
+ "email": "danilo_novais@yahoo.com.br"
36
  },
37
  "license": "Apache-2.0",
38
  "engines": {
 
42
  "devDependencies": {
43
  "@types/node": "^20.0.0"
44
  }
45
+ }
scripts/backup.sh CHANGED
@@ -1,198 +1,23 @@
1
- #!/bin/bash
2
-
3
- # n8n Infrastructure Backup Script
4
- # Backs up workflows, credentials, and configurations
5
- # Usage: ./backup.sh [backup-name]
6
-
7
  set -euo pipefail
8
 
9
- # Configuration
10
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
- PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
12
- BACKUP_DIR="$PROJECT_ROOT/workflows/backup"
13
- TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
14
- BACKUP_NAME="${1:-n8n_backup_$TIMESTAMP}"
15
-
16
- # Colors for output
17
- RED='\033[0;31m'
18
- GREEN='\033[0;32m'
19
- YELLOW='\033[1;33m'
20
- NC='\033[0m' # No Color
21
-
22
- log_info() {
23
- echo -e "${GREEN}[INFO]${NC} $1"
24
- }
25
-
26
- log_warn() {
27
- echo -e "${YELLOW}[WARN]${NC} $1"
28
- }
29
-
30
- log_error() {
31
- echo -e "${RED}[ERROR]${NC} $1"
32
- }
33
-
34
- # Check if Docker is running
35
- check_docker() {
36
- if ! docker ps > /dev/null 2>&1; then
37
- log_error "Docker is not running or accessible"
38
- exit 1
39
- fi
40
- }
41
-
42
- # Create backup directory
43
- create_backup_dir() {
44
- local backup_path="$BACKUP_DIR/$BACKUP_NAME"
45
- mkdir -p "$backup_path"
46
- echo "$backup_path"
47
- }
48
-
49
- # Backup n8n workflows via API
50
- backup_workflows() {
51
- local backup_path="$1"
52
- local container_name="n8n-automation"
53
-
54
- log_info "Backing up n8n workflows..."
55
-
56
- if docker ps --format '{{.Names}}' | grep -q "^$container_name$"; then
57
- # Export workflows using n8n CLI inside container
58
- docker exec "$container_name" n8n export:workflow --all --output="/home/node/.n8n/backup/workflows_$TIMESTAMP.json" || {
59
- log_warn "Failed to export workflows via CLI, trying API approach"
60
- return 1
61
- }
62
-
63
- # Copy exported file to backup directory
64
- docker cp "$container_name:/home/node/.n8n/backup/workflows_$TIMESTAMP.json" "$backup_path/"
65
- log_info "Workflows backed up successfully"
66
- else
67
- log_error "n8n container not found or not running"
68
- return 1
69
- fi
70
- }
71
-
72
- # Backup credentials (encrypted)
73
- backup_credentials() {
74
- local backup_path="$1"
75
- local container_name="n8n-automation"
76
-
77
- log_info "Backing up encrypted credentials..."
78
-
79
- if docker ps --format '{{.Names}}' | grep -q "^$container_name$"; then
80
- docker cp "$container_name:/home/node/.n8n/credentials" "$backup_path/" 2>/dev/null || {
81
- log_warn "No credentials found or access denied"
82
- }
83
- fi
84
- }
85
-
86
- # Backup database schema and essential data
87
- backup_database() {
88
- local backup_path="$1"
89
-
90
- log_info "Backing up database schema..."
91
-
92
- if [[ -n "${DB_POSTGRESDB_PASSWORD:-}" ]]; then
93
- export PGPASSWORD="$DB_POSTGRESDB_PASSWORD"
94
-
95
- pg_dump \
96
- --host="${DB_POSTGRESDB_HOST}" \
97
- --port="${DB_POSTGRESDB_PORT:-5432}" \
98
- --username="${DB_POSTGRESDB_USER}" \
99
- --dbname="${DB_POSTGRESDB_DATABASE}" \
100
- --schema-only \
101
- --no-owner \
102
- --no-privileges \
103
- > "$backup_path/schema_$TIMESTAMP.sql" || {
104
- log_error "Database backup failed"
105
- return 1
106
- }
107
-
108
- log_info "Database schema backed up successfully"
109
- else
110
- log_warn "Database credentials not available, skipping database backup"
111
- fi
112
- }
113
-
114
- # Backup knowledge base content
115
- backup_knowledge() {
116
- local backup_path="$1"
117
-
118
- log_info "Backing up knowledge base..."
119
-
120
- if [[ -d "$PROJECT_ROOT/knowledge" ]]; then
121
- cp -r "$PROJECT_ROOT/knowledge" "$backup_path/"
122
- log_info "Knowledge base backed up successfully"
123
- else
124
- log_warn "Knowledge base directory not found"
125
- fi
126
- }
127
 
128
- # Create backup metadata
129
- create_metadata() {
130
- local backup_path="$1"
131
-
132
- cat > "$backup_path/backup_metadata.json" << EOF
133
- {
134
- "backup_name": "$BACKUP_NAME",
135
- "timestamp": "$TIMESTAMP",
136
- "created_at": "$(date -Iseconds)",
137
- "n8n_version": "$(docker exec n8n-automation n8n --version 2>/dev/null || echo 'unknown')",
138
- "backup_type": "full",
139
- "components": [
140
- "workflows",
141
- "credentials",
142
- "database_schema",
143
- "knowledge_base"
144
- ],
145
- "notes": "Automated backup created by backup.sh script"
146
- }
147
- EOF
148
- }
149
 
150
- # Cleanup old backups
151
- cleanup_old_backups() {
152
- local retention_days="${BACKUP_RETENTION_DAYS:-30}"
153
-
154
- log_info "Cleaning up backups older than $retention_days days..."
155
-
156
- find "$BACKUP_DIR" -type d -name "n8n_backup_*" -mtime "+$retention_days" -exec rm -rf {} + 2>/dev/null || true
157
- }
158
 
159
- # Main backup process
160
- main() {
161
- log_info "Starting n8n infrastructure backup: $BACKUP_NAME"
162
-
163
- # Preliminary checks
164
- check_docker
165
-
166
- # Load environment variables
167
- if [[ -f "$PROJECT_ROOT/.env" ]]; then
168
- source "$PROJECT_ROOT/.env"
169
- fi
170
-
171
- # Create backup directory
172
- local backup_path=$(create_backup_dir)
173
- log_info "Backup directory created: $backup_path"
174
-
175
- # Perform backups
176
- backup_workflows "$backup_path" || log_warn "Workflow backup incomplete"
177
- backup_credentials "$backup_path" || log_warn "Credentials backup incomplete"
178
- backup_database "$backup_path" || log_warn "Database backup incomplete"
179
- backup_knowledge "$backup_path" || log_warn "Knowledge base backup incomplete"
180
-
181
- # Create metadata
182
- create_metadata "$backup_path"
183
-
184
- # Cleanup old backups
185
- cleanup_old_backups
186
-
187
- log_info "Backup completed successfully: $backup_path"
188
-
189
- # Optional: Create compressed archive
190
- if command -v tar > /dev/null; then
191
- local archive_name="$BACKUP_DIR/${BACKUP_NAME}.tar.gz"
192
- tar -czf "$archive_name" -C "$BACKUP_DIR" "$BACKUP_NAME"
193
- log_info "Compressed backup created: $archive_name"
194
- fi
195
- }
196
 
197
- # Run main function
198
- main "$@"
 
1
+ #!/usr/bin/env bash
 
 
 
 
 
2
  set -euo pipefail
3
 
4
+ : "${DB_HOST?Missing DB_HOST}"
5
+ : "${DB_PORT:=5432}"
6
+ : "${DB_NAME?Missing DB_NAME}"
7
+ : "${DB_USER?Missing DB_USER}"
8
+ : "${DB_PASSWORD?Missing DB_PASSWORD}"
9
+ : "${N8N_BASE_URL?Missing N8N_BASE_URL}"
10
+ : "${N8N_API_KEY?Missing N8N_API_KEY}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ TS=$(date +%Y%m%d-%H%M%S)
13
+ OUTDIR="workflows/backup/${TS}"
14
+ mkdir -p "$OUTDIR"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
+ echo "==> Dumping Postgres (Supabase) ..."
17
+ export PGPASSWORD="${DB_PASSWORD}"
18
+ pg_dump -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" -F c -Z 5 -f "${OUTDIR}/db.dump"
 
 
 
 
 
19
 
20
+ echo "==> Exporting n8n workflows ..."
21
+ curl -sS -H "X-N8N-API-KEY: ${N8N_API_KEY}" "${N8N_BASE_URL}/rest/workflows" > "${OUTDIR}/workflows.json"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ echo "==> Done. Artifacts at ${OUTDIR}"
 
scripts/restore.sh CHANGED
@@ -1,204 +1,14 @@
1
- #!/bin/bash
2
-
3
- # n8n Infrastructure Restore Script
4
- # Restores workflows, credentials, and configurations from backup
5
- # Usage: ./restore.sh <backup-name>
6
-
7
  set -euo pipefail
8
 
9
- # Configuration
10
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
11
- PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
12
- BACKUP_DIR="$PROJECT_ROOT/workflows/backup"
13
-
14
- # Colors for output
15
- RED='\033[0;31m'
16
- GREEN='\033[0;32m'
17
- YELLOW='\033[1;33m'
18
- NC='\033[0m' # No Color
19
-
20
- log_info() {
21
- echo -e "${GREEN}[INFO]${NC} $1"
22
- }
23
-
24
- log_warn() {
25
- echo -e "${YELLOW}[WARN]${NC} $1"
26
- }
27
-
28
- log_error() {
29
- echo -e "${RED}[ERROR]${NC} $1"
30
- }
31
-
32
- # Validate backup name argument
33
- if [[ $# -eq 0 ]]; then
34
- log_error "Usage: $0 <backup-name>"
35
- log_info "Available backups:"
36
- ls -1 "$BACKUP_DIR" | grep -E "^n8n_backup_" | head -10
37
- exit 1
38
- fi
39
-
40
- BACKUP_NAME="$1"
41
- BACKUP_PATH="$BACKUP_DIR/$BACKUP_NAME"
42
-
43
- # Check if backup exists
44
- if [[ ! -d "$BACKUP_PATH" ]]; then
45
- # Try with .tar.gz extension
46
- if [[ -f "$BACKUP_DIR/$BACKUP_NAME.tar.gz" ]]; then
47
- log_info "Found compressed backup, extracting..."
48
- tar -xzf "$BACKUP_DIR/$BACKUP_NAME.tar.gz" -C "$BACKUP_DIR"
49
- else
50
- log_error "Backup not found: $BACKUP_NAME"
51
- exit 1
52
- fi
53
- fi
54
-
55
- # Verify backup integrity
56
- verify_backup() {
57
- local metadata_file="$BACKUP_PATH/backup_metadata.json"
58
-
59
- if [[ ! -f "$metadata_file" ]]; then
60
- log_warn "Backup metadata not found, proceeding with caution"
61
- return 0
62
- fi
63
-
64
- log_info "Backup verification:"
65
- cat "$metadata_file" | jq -r '.backup_name, .created_at, .backup_type'
66
- }
67
-
68
- # Restore workflows
69
- restore_workflows() {
70
- local container_name="n8n-automation"
71
-
72
- log_info "Restoring workflows..."
73
-
74
- if ! docker ps --format '{{.Names}}' | grep -q "^$container_name$"; then
75
- log_error "n8n container not running. Start the container first."
76
- return 1
77
- fi
78
-
79
- # Find workflow backup file
80
- local workflow_file=$(find "$BACKUP_PATH" -name "workflows_*.json" | head -1)
81
-
82
- if [[ -n "$workflow_file" ]]; then
83
- # Copy workflow file to container
84
- docker cp "$workflow_file" "$container_name:/tmp/workflows_restore.json"
85
-
86
- # Import workflows
87
- docker exec "$container_name" n8n import:workflow --input="/tmp/workflows_restore.json" || {
88
- log_error "Failed to import workflows"
89
- return 1
90
- }
91
-
92
- log_info "Workflows restored successfully"
93
- else
94
- log_warn "No workflow backup file found"
95
- fi
96
- }
97
-
98
- # Restore credentials
99
- restore_credentials() {
100
- local container_name="n8n-automation"
101
-
102
- log_info "Restoring credentials..."
103
-
104
- if [[ -d "$BACKUP_PATH/credentials" ]]; then
105
- docker cp "$BACKUP_PATH/credentials/." "$container_name:/home/node/.n8n/credentials/"
106
- log_info "Credentials restored successfully"
107
- else
108
- log_warn "No credentials backup found"
109
- fi
110
- }
111
-
112
- # Restore database (schema only, data should be preserved)
113
- restore_database() {
114
- log_info "Restoring database schema..."
115
-
116
- local schema_file=$(find "$BACKUP_PATH" -name "schema_*.sql" | head -1)
117
-
118
- if [[ -n "$schema_file" && -n "${DB_POSTGRESDB_PASSWORD:-}" ]]; then
119
- export PGPASSWORD="$DB_POSTGRESDB_PASSWORD"
120
-
121
- log_warn "This will update database schema. Proceed? (y/N)"
122
- read -r response
123
- if [[ "$response" =~ ^[Yy]$ ]]; then
124
- psql \
125
- --host="${DB_POSTGRESDB_HOST}" \
126
- --port="${DB_POSTGRESDB_PORT:-5432}" \
127
- --username="${DB_POSTGRESDB_USER}" \
128
- --dbname="${DB_POSTGRESDB_DATABASE}" \
129
- --file="$schema_file" || {
130
- log_error "Database restore failed"
131
- return 1
132
- }
133
- log_info "Database schema restored successfully"
134
- else
135
- log_info "Database restore skipped"
136
- fi
137
- else
138
- log_warn "No database backup found or credentials missing"
139
- fi
140
- }
141
-
142
- # Restart services after restore
143
- restart_services() {
144
- log_info "Restarting n8n services..."
145
-
146
- docker-compose -f "$PROJECT_ROOT/docker/docker-compose.yml" restart n8n
147
-
148
- # Wait for service to be ready
149
- local max_attempts=30
150
- local attempt=1
151
-
152
- while [[ $attempt -le $max_attempts ]]; do
153
- if curl -f http://localhost:7860/healthz > /dev/null 2>&1; then
154
- log_info "n8n service is ready"
155
- break
156
- fi
157
-
158
- log_info "Waiting for n8n to start... ($attempt/$max_attempts)"
159
- sleep 10
160
- ((attempt++))
161
- done
162
-
163
- if [[ $attempt -gt $max_attempts ]]; then
164
- log_error "n8n failed to start after restore"
165
- return 1
166
- fi
167
- }
168
-
169
- # Main restore process
170
- main() {
171
- log_info "Starting n8n infrastructure restore: $BACKUP_NAME"
172
-
173
- # Load environment variables
174
- if [[ -f "$PROJECT_ROOT/.env" ]]; then
175
- source "$PROJECT_ROOT/.env"
176
- fi
177
-
178
- # Verify backup
179
- verify_backup
180
-
181
- # Confirm restore operation
182
- log_warn "This will restore n8n configuration from backup: $BACKUP_NAME"
183
- log_warn "Current workflows and credentials may be overwritten. Continue? (y/N)"
184
- read -r response
185
-
186
- if [[ ! "$response" =~ ^[Yy]$ ]]; then
187
- log_info "Restore operation cancelled"
188
- exit 0
189
- fi
190
-
191
- # Perform restore operations
192
- restore_workflows || log_warn "Workflow restore incomplete"
193
- restore_credentials || log_warn "Credentials restore incomplete"
194
- restore_database || log_warn "Database restore incomplete"
195
-
196
- # Restart services
197
- restart_services
198
-
199
- log_info "Restore completed successfully"
200
- log_info "Access your n8n instance at: ${WEBHOOK_URL:-http://localhost:7860}"
201
- }
202
-
203
- # Run main function
204
- main "$@"
 
1
+ #!/usr/bin/env bash
 
 
 
 
 
2
  set -euo pipefail
3
 
4
+ : "${DB_HOST?Missing DB_HOST}"
5
+ : "${DB_PORT:=5432}"
6
+ : "${DB_NAME?Missing DB_NAME}"
7
+ : "${DB_USER?Missing DB_USER}"
8
+ : "${DB_PASSWORD?Missing DB_PASSWORD}"
9
+ : "${DUMP_PATH?Usage: DUMP_PATH=/path/to/db.dump ./scripts/restore.sh}"
10
+
11
+ echo "==> Restoring Postgres from ${DUMP_PATH} ..."
12
+ export PGPASSWORD="${DB_PASSWORD}"
13
+ pg_restore -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" --clean --if-exists "${DUMP_PATH}"
14
+ echo "==> Done."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
scripts/sync-knowledge.mjs ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Node 20 script: sync knowledge repos into Supabase with embeddings
2
+ // Requires: OPENAI_API_KEY, SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY, KNOWLEDGE_REPO_URL, KNOWLEDGE_DIRS
3
+ import { createClient } from '@supabase/supabase-js';
4
+ import crypto from 'node:crypto';
5
+ import { execSync } from 'node:child_process';
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import process from 'node:process';
9
+ import OpenAI from 'openai';
10
+
11
+ const {
12
+ OPENAI_API_KEY,
13
+ SUPABASE_URL,
14
+ SUPABASE_SERVICE_ROLE_KEY,
15
+ KNOWLEDGE_REPO_URL,
16
+ KNOWLEDGE_DIRS = 'projects/n8n,projects/videos-e-animacoes,projects/midjorney-prompt',
17
+ } = process.env;
18
+
19
+ if (!SUPABASE_URL || !SUPABASE_SERVICE_ROLE_KEY || !KNOWLEDGE_REPO_URL) {
20
+ console.error('Missing env SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY or KNOWLEDGE_REPO_URL');
21
+ process.exit(1);
22
+ }
23
+
24
+ const openai = OPENAI_API_KEY ? new OpenAI({ apiKey: OPENAI_API_KEY }) : null;
25
+ const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);
26
+
27
+ const workdir = path.resolve('knowledge');
28
+ if (!fs.existsSync(workdir)) fs.mkdirSync(workdir, { recursive: true });
29
+
30
+ const repoDir = path.join(workdir, 'CHATGPT-knowledge-base');
31
+ if (!fs.existsSync(repoDir)) {
32
+ console.log('Cloning KB repo...');
33
+ execSync(`git clone --depth 1 ${KNOWLEDGE_REPO_URL} ${repoDir}`, { stdio: 'inherit' });
34
+ } else {
35
+ console.log('Pulling KB repo...');
36
+ execSync(`git -C ${repoDir} pull`, { stdio: 'inherit' });
37
+ }
38
+
39
+ const dirs = KNOWLEDGE_DIRS.split(',').map(s => s.trim());
40
+
41
+ function sha256(s){ return crypto.createHash('sha256').update(s).digest('hex'); }
42
+
43
+ async function upsertDoc(pth, content) {
44
+ const title = path.basename(pth);
45
+ const hash = sha256(content);
46
+
47
+ // Upsert document
48
+ const { data: doc, error: docErr } = await supabase
49
+ .from('knowledge.documents')
50
+ .upsert({ path: pth, title, content, hash }, { onConflict: 'path' })
51
+ .select()
52
+ .single();
53
+ if (docErr) throw docErr;
54
+
55
+ if (openai) {
56
+ // Embedding
57
+ const input = content.slice(0, 12000); // truncate
58
+ const emb = await openai.embeddings.create({
59
+ model: 'text-embedding-3-large',
60
+ input
61
+ });
62
+ const vector = emb.data[0].embedding;
63
+
64
+ const { error: embErr } = await supabase
65
+ .from('knowledge.embeddings')
66
+ .upsert({ doc_id: doc.id, embedding: vector, model: 'text-embedding-3-large' });
67
+ if (embErr) throw embErr;
68
+ } else {
69
+ console.warn('OPENAI_API_KEY not set, skipping embeddings for', pth);
70
+ }
71
+ }
72
+
73
+ async function main() {
74
+ for (const rel of dirs) {
75
+ const abs = path.join(repoDir, rel);
76
+ if (!fs.existsSync(abs)) {
77
+ console.warn('Skip missing dir:', rel);
78
+ continue;
79
+ }
80
+ const entries = await fs.promises.readdir(abs, { withFileTypes: true });
81
+ for (const ent of entries) {
82
+ const full = path.join(abs, ent.name);
83
+ if (ent.isDirectory()) continue;
84
+ if (!/\.(md|markdown|json|txt)$/i.test(ent.name)) continue;
85
+
86
+ const content = await fs.promises.readFile(full, 'utf8');
87
+ const repoRelPath = path.relative(repoDir, full);
88
+ console.log('Ingest:', repoRelPath);
89
+ await upsertDoc(repoRelPath, content);
90
+ }
91
+ }
92
+ console.log('Sync complete');
93
+ }
94
+
95
+ main().catch(err => { console.error(err); process.exit(1); });
supabase/schema.sql ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- Enable pgvector (Supabase: run as superuser/migration)
2
+ create extension if not exists vector;
3
+
4
+ -- Schema
5
+ create schema if not exists knowledge;
6
+
7
+ -- Documents table
8
+ create table if not exists knowledge.documents (
9
+ id uuid primary key default gen_random_uuid(),
10
+ path text unique not null,
11
+ title text,
12
+ content text,
13
+ source_url text,
14
+ hash text not null,
15
+ updated_at timestamptz not null default now()
16
+ );
17
+
18
+ -- Embeddings table (OpenAI text-embedding-3-large: 3072 dims; adjust if needed)
19
+ create table if not exists knowledge.embeddings (
20
+ doc_id uuid primary key references knowledge.documents(id) on delete cascade,
21
+ embedding vector(3072) not null,
22
+ model text default 'text-embedding-3-large'
23
+ );
24
+
25
+ -- Vector index (IVFFLAT)
26
+ create index if not exists idx_embeddings_ivfflat on knowledge.embeddings using ivfflat (embedding vector_l2_ops) with (lists = 100);
27
+
28
+ -- Helpful view
29
+ create or replace view knowledge.searchable as
30
+ select d.id, d.title, d.path, d.source_url, d.updated_at
31
+ from knowledge.documents d
32
+ order by d.updated_at desc;
workflows/examples/knowledge-sync-blueprint.json ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "Knowledge Sync (Webhook \u2192 Postgres)",
3
+ "nodes": [
4
+ {
5
+ "parameters": {},
6
+ "id": "Start",
7
+ "name": "Webhook Trigger",
8
+ "type": "n8n-nodes-base.webhook",
9
+ "typeVersion": 1,
10
+ "position": [
11
+ -460,
12
+ 0
13
+ ],
14
+ "webhookId": "replace-with-your-id",
15
+ "path": "knowledge-sync",
16
+ "httpMethod": "POST",
17
+ "responseMode": "onReceived",
18
+ "responseData": "allEntries"
19
+ },
20
+ {
21
+ "parameters": {
22
+ "functionCode": "items = items.map(item => ({ json: {\n path: item.json.path,\n title: item.json.title || item.json.path.split('/').pop(),\n content: item.json.content,\n hash: item.json.hash\n}}));\nreturn items;"
23
+ },
24
+ "id": "Map",
25
+ "name": "Map Fields",
26
+ "type": "n8n-nodes-base.function",
27
+ "typeVersion": 2,
28
+ "position": [
29
+ -200,
30
+ 0
31
+ ]
32
+ },
33
+ {
34
+ "parameters": {
35
+ "operation": "executeQuery",
36
+ "query": "INSERT INTO knowledge.documents(path, title, content, hash)\nVALUES(:path, :title, :content, :hash)\nON CONFLICT (path) DO UPDATE SET\n title=EXCLUDED.title,\n content=EXCLUDED.content,\n hash=EXCLUDED.hash,\n updated_at=now()\nRETURNING id;"
37
+ },
38
+ "id": "Postgres",
39
+ "name": "Postgres Upsert",
40
+ "type": "n8n-nodes-base.postgres",
41
+ "typeVersion": 1,
42
+ "position": [
43
+ 60,
44
+ 0
45
+ ],
46
+ "credentials": {
47
+ "postgres": "REPLACE_WITH_YOUR_CREDENTIAL"
48
+ }
49
+ }
50
+ ],
51
+ "connections": {
52
+ "Webhook Trigger": {
53
+ "main": [
54
+ [
55
+ {
56
+ "node": "Map Fields",
57
+ "type": "main",
58
+ "index": 0
59
+ }
60
+ ]
61
+ ]
62
+ },
63
+ "Map Fields": {
64
+ "main": [
65
+ [
66
+ {
67
+ "node": "Postgres Upsert",
68
+ "type": "main",
69
+ "index": 0
70
+ }
71
+ ]
72
+ ]
73
+ }
74
+ },
75
+ "active": false
76
+ }