Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
google-labs-jules[bot]
commited on
Commit
·
333834a
1
Parent(s):
d33c618
feat: Comprehensive infrastructure audit and optimization
Browse files- .dockerignore +11 -0
- .github/workflows/backup-workflows.yml +2 -5
- .github/workflows/sync-knowledge.yml +3 -2
- .gitignore +35 -0
- README.md +83 -413
- config/{.env → .env.example} +31 -29
- docker/Dockerfile +7 -0
- docker/docker-compose.yml +13 -0
- scripts/backup.sh +26 -4
- scripts/restore.sh +61 -5
- scripts/sync-knowledge.mjs +47 -17
- scripts/test-infrastructure.sh +12 -27
- supabase/schema.sql +14 -0
- test_output.log +3 -0
.dockerignore
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.git
|
2 |
+
.github
|
3 |
+
.vscode
|
4 |
+
.DS_Store
|
5 |
+
README.md
|
6 |
+
LICENSE
|
7 |
+
SECURITY.md
|
8 |
+
knowledge/
|
9 |
+
node_modules
|
10 |
+
npm-debug.log
|
11 |
+
config/.env
|
.github/workflows/backup-workflows.yml
CHANGED
@@ -8,14 +8,11 @@ on:
|
|
8 |
jobs:
|
9 |
backup:
|
10 |
runs-on: ubuntu-latest
|
|
|
|
|
11 |
steps:
|
12 |
- uses: actions/checkout@v4
|
13 |
|
14 |
-
- name: Install Postgres client
|
15 |
-
run: |
|
16 |
-
sudo apt-get update
|
17 |
-
sudo apt-get install -y postgresql-client
|
18 |
-
|
19 |
- name: Run backup script
|
20 |
env:
|
21 |
DB_HOST: ${{ secrets.DB_HOST }}
|
|
|
8 |
jobs:
|
9 |
backup:
|
10 |
runs-on: ubuntu-latest
|
11 |
+
container:
|
12 |
+
image: postgres:15
|
13 |
steps:
|
14 |
- uses: actions/checkout@v4
|
15 |
|
|
|
|
|
|
|
|
|
|
|
16 |
- name: Run backup script
|
17 |
env:
|
18 |
DB_HOST: ${{ secrets.DB_HOST }}
|
.github/workflows/sync-knowledge.yml
CHANGED
@@ -15,9 +15,10 @@ jobs:
|
|
15 |
uses: actions/setup-node@v4
|
16 |
with:
|
17 |
node-version: 20
|
|
|
18 |
|
19 |
-
- name: Install
|
20 |
-
run: npm ci
|
21 |
|
22 |
- name: Run sync script
|
23 |
env:
|
|
|
15 |
uses: actions/setup-node@v4
|
16 |
with:
|
17 |
node-version: 20
|
18 |
+
cache: 'npm'
|
19 |
|
20 |
+
- name: Install dependencies
|
21 |
+
run: npm ci
|
22 |
|
23 |
- name: Run sync script
|
24 |
env:
|
.gitignore
ADDED
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
2 |
+
|
3 |
+
# Dependencies
|
4 |
+
/node_modules
|
5 |
+
/.pnp
|
6 |
+
.pnp.js
|
7 |
+
|
8 |
+
# Testing
|
9 |
+
/coverage
|
10 |
+
|
11 |
+
# Local-first knowledge base
|
12 |
+
/knowledge
|
13 |
+
|
14 |
+
# Backups (store off-site)
|
15 |
+
/workflows/backup/*
|
16 |
+
!/workflows/backup/.keep
|
17 |
+
|
18 |
+
# Environment variables
|
19 |
+
.env
|
20 |
+
.env.*
|
21 |
+
!.env.example
|
22 |
+
|
23 |
+
# OS-specific
|
24 |
+
.DS_Store
|
25 |
+
*.swo
|
26 |
+
*~
|
27 |
+
*.swp
|
28 |
+
|
29 |
+
# Logs
|
30 |
+
npm-debug.log*
|
31 |
+
yarn-debug.log*
|
32 |
+
yarn-error.log*
|
33 |
+
|
34 |
+
# VSCode
|
35 |
+
.vscode/
|
README.md
CHANGED
@@ -1,95 +1,67 @@
|
|
1 |
# n8n Infrastructure Repository
|
2 |
|
|
|
|
|
|
|
3 |
A comprehensive, production-ready infrastructure setup for deploying n8n automation platform on Hugging Face Spaces with AI integrations and automated knowledge management.
|
4 |
|
5 |
## 🚀 Features
|
6 |
|
7 |
### Core Platform
|
8 |
-
|
9 |
-
- **
|
10 |
-
- **
|
11 |
-
- **
|
12 |
-
- **ChromaDB**: Vector store for embeddings and AI-powered search
|
13 |
|
14 |
### AI & Automation
|
15 |
-
|
16 |
-
- **
|
17 |
-
- **
|
18 |
-
- **
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
- **
|
24 |
-
- **
|
25 |
-
- **Knowledge Sync**: Multi-repository content synchronization
|
26 |
-
- **Health Monitoring**: Container health checks and alerting
|
27 |
|
28 |
## 📋 Prerequisites
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
3. **Supabase Account** with PostgreSQL database
|
35 |
-
4. **Git** and **Docker** installed locally
|
36 |
-
|
37 |
-
### Required Secrets
|
38 |
-
|
39 |
-
Configure these secrets in your GitHub repository settings:
|
40 |
-
|
41 |
-
```bash
|
42 |
-
# Hugging Face
|
43 |
-
HF_USERNAME=your-huggingface-username
|
44 |
-
HF_TOKEN=your-hf-token
|
45 |
-
HF_SPACE_NAME=n8n-automation
|
46 |
-
|
47 |
-
# Database
|
48 |
-
DB_POSTGRESDB_HOST=your-project.supabase.co
|
49 |
-
DB_POSTGRESDB_USER=postgres
|
50 |
-
DB_POSTGRESDB_PASSWORD=your-database-password
|
51 |
-
DB_POSTGRESDB_DATABASE=postgres
|
52 |
-
|
53 |
-
# n8n Configuration
|
54 |
-
N8N_ENCRYPTION_KEY=your-32-character-encryption-key
|
55 |
-
N8N_USER_MANAGEMENT_JWT_SECRET=your-jwt-secret
|
56 |
-
WEBHOOK_URL=https://your-username-n8n-automation.hf.space
|
57 |
-
|
58 |
-
# AI Services (Optional)
|
59 |
-
OPENAI_API_KEY=sk-your-openai-key
|
60 |
-
ANTHROPIC_API_KEY=sk-ant-your-anthropic-key
|
61 |
-
GOOGLE_PROJECT_ID=your-gcp-project
|
62 |
-
```
|
63 |
|
64 |
## 🛠️ Quick Start
|
65 |
|
66 |
### 1. Repository Setup
|
67 |
-
|
68 |
```bash
|
69 |
# Clone the repository
|
70 |
git clone https://github.com/your-username/n8n-infra.git
|
71 |
cd n8n-infra
|
72 |
|
73 |
-
# Create environment configuration
|
74 |
-
cp config/.env.example
|
75 |
-
|
|
|
|
|
76 |
```
|
77 |
|
78 |
### 2. Local Development
|
79 |
-
|
80 |
```bash
|
81 |
# Start the full stack locally
|
82 |
-
docker
|
83 |
|
84 |
# Check service status
|
85 |
-
docker
|
86 |
|
87 |
# View logs
|
88 |
-
docker
|
89 |
```
|
90 |
|
91 |
### 3. Hugging Face Deployment
|
92 |
-
|
93 |
```bash
|
94 |
# Trigger deployment via GitHub Actions
|
95 |
git push origin main
|
@@ -99,196 +71,77 @@ gh workflow run deploy-to-hf.yml
|
|
99 |
```
|
100 |
|
101 |
## 📊 Database Setup
|
|
|
102 |
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
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(),
|
117 |
-
content_id TEXT NOT NULL,
|
118 |
-
collection_name TEXT NOT NULL,
|
119 |
-
content TEXT NOT NULL,
|
120 |
-
embedding VECTOR(384),
|
121 |
-
metadata JSONB DEFAULT '{}',
|
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);
|
146 |
-
```
|
147 |
-
|
148 |
-
## 🤖 AI Integration Guide
|
149 |
-
|
150 |
-
### LangChain Workflows
|
151 |
-
|
152 |
-
The platform supports advanced LangChain workflows:
|
153 |
-
|
154 |
-
```javascript
|
155 |
-
// Example: Knowledge-based Q&A workflow
|
156 |
-
{
|
157 |
-
"nodes": [
|
158 |
-
{
|
159 |
-
"name": "Vector Search",
|
160 |
-
"type": "n8n-nodes-vector-store",
|
161 |
-
"parameters": {
|
162 |
-
"operation": "similarity_search",
|
163 |
-
"query": "{{ $json.question }}",
|
164 |
-
"collection": "n8n",
|
165 |
-
"top_k": 5
|
166 |
-
}
|
167 |
-
},
|
168 |
-
{
|
169 |
-
"name": "LangChain QA",
|
170 |
-
"type": "@n8n/n8n-nodes-langchain",
|
171 |
-
"parameters": {
|
172 |
-
"chain_type": "question_answering",
|
173 |
-
"context": "{{ $json.vector_results }}",
|
174 |
-
"question": "{{ $json.question }}"
|
175 |
-
}
|
176 |
-
}
|
177 |
-
]
|
178 |
-
}
|
179 |
-
```
|
180 |
-
|
181 |
-
### Custom AI Nodes
|
182 |
-
|
183 |
-
Install additional AI nodes:
|
184 |
-
|
185 |
-
```bash
|
186 |
-
# Install in running container
|
187 |
-
docker exec n8n-automation npm install n8n-nodes-google-vertex-ai
|
188 |
-
docker exec n8n-automation npm install n8n-nodes-openai-advanced
|
189 |
-
|
190 |
-
# Restart to load new nodes
|
191 |
-
docker-compose -f docker/docker-compose.yml restart n8n
|
192 |
-
```
|
193 |
-
|
194 |
-
## 🗄️ Knowledge Management
|
195 |
-
|
196 |
-
### Automated Synchronization
|
197 |
-
|
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
|
205 |
-
|
206 |
-
```bash
|
207 |
-
# Sync specific collection
|
208 |
-
./scripts/sync-knowledge.sh
|
209 |
-
|
210 |
-
# Or trigger via GitHub Actions
|
211 |
-
gh workflow run sync-knowledge.yml -f collections=n8n,midjourney-prompt
|
212 |
-
```
|
213 |
-
|
214 |
-
### Vector Search Setup
|
215 |
-
|
216 |
-
Query the knowledge base in n8n workflows:
|
217 |
-
|
218 |
-
```javascript
|
219 |
-
// Vector similarity search node configuration
|
220 |
-
{
|
221 |
-
"collection": "n8n",
|
222 |
-
"query": "How to create webhook workflows",
|
223 |
-
"top_k": 3,
|
224 |
-
"score_threshold": 0.7
|
225 |
-
}
|
226 |
-
```
|
227 |
|
228 |
## 💾 Backup & Recovery
|
229 |
|
230 |
### Automated Backups
|
|
|
|
|
|
|
|
|
231 |
|
232 |
-
|
|
|
|
|
|
|
|
|
233 |
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
- Knowledge base content
|
238 |
-
- Vector embeddings
|
239 |
|
240 |
-
###
|
|
|
241 |
|
242 |
-
|
243 |
-
# Create full backup
|
244 |
-
./scripts/backup.sh custom-backup-name
|
245 |
|
246 |
-
|
247 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
248 |
|
249 |
-
#
|
250 |
-
./scripts/restore.sh
|
251 |
-
```
|
|
|
|
|
|
|
|
|
|
|
|
|
252 |
|
253 |
-
|
|
|
254 |
|
255 |
-
- **
|
256 |
-
- **
|
257 |
-
- **
|
|
|
258 |
|
259 |
## 🔧 Maintenance
|
260 |
|
261 |
### Health Monitoring
|
262 |
-
|
263 |
```bash
|
264 |
-
# Check container health
|
265 |
-
docker
|
266 |
|
267 |
# View application logs
|
268 |
-
docker
|
269 |
-
|
270 |
-
# Monitor vector store
|
271 |
-
curl http://localhost:8000/api/v1/heartbeat
|
272 |
```
|
273 |
|
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
|
287 |
-
SET ivfflat.probes = 10;
|
288 |
-
```
|
289 |
-
|
290 |
-
**Container Resources**:
|
291 |
-
|
292 |
```yaml
|
293 |
# docker-compose.yml resource limits
|
294 |
services:
|
@@ -303,193 +156,10 @@ services:
|
|
303 |
memory: 2G
|
304 |
```
|
305 |
|
306 |
-
## 🔒 Security
|
307 |
-
|
308 |
-
### SSL Configuration
|
309 |
-
|
310 |
-
- All database connections use SSL encryption
|
311 |
-
- Webhook URLs must use HTTPS
|
312 |
-
- Container communication over encrypted networks
|
313 |
-
|
314 |
-
### Credential Management
|
315 |
-
|
316 |
-
```bash
|
317 |
-
# Credentials are encrypted by n8n
|
318 |
-
# Store sensitive files in config/credentials/
|
319 |
-
mkdir -p config/credentials
|
320 |
-
echo '{}' > config/credentials/google-service-account.json
|
321 |
-
|
322 |
-
# Set proper permissions
|
323 |
-
chmod 600 config/credentials/*
|
324 |
-
```
|
325 |
-
|
326 |
-
### Environment Security
|
327 |
-
|
328 |
-
- Never commit `.env` files
|
329 |
-
- Use GitHub Secrets for sensitive data
|
330 |
-
- Rotate encryption keys regularly
|
331 |
-
- Enable Supabase RLS policies
|
332 |
-
|
333 |
-
## 🚨 Troubleshooting
|
334 |
-
|
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"
|
342 |
-
|
343 |
-
# Check n8n logs
|
344 |
-
docker logs n8n-automation --tail 50
|
345 |
-
|
346 |
-
# Verify webhook connectivity
|
347 |
-
curl -I "$WEBHOOK_URL/healthz"
|
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"
|
355 |
-
|
356 |
-
# View GitHub Actions logs
|
357 |
-
gh run list --workflow=deploy-to-hf.yml
|
358 |
-
gh run view [run-id] --log
|
359 |
-
```
|
360 |
-
|
361 |
-
**Knowledge Sync Problems**:
|
362 |
-
|
363 |
-
```bash
|
364 |
-
# Manual knowledge sync debug
|
365 |
-
./scripts/sync-knowledge.sh
|
366 |
-
echo $? # Should return 0 for success
|
367 |
-
|
368 |
-
# Check embedding generation
|
369 |
-
python3 -c "
|
370 |
-
import json
|
371 |
-
with open('knowledge/n8n/n8n_embeddings.json') as f:
|
372 |
-
data = json.load(f)
|
373 |
-
print(f'Embeddings loaded: {len(data)} documents')
|
374 |
-
"
|
375 |
-
```
|
376 |
-
|
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
|
395 |
-
REINDEX INDEX idx_embeddings_vector;
|
396 |
-
```
|
397 |
-
|
398 |
-
## 📈 Scaling
|
399 |
-
|
400 |
-
### Horizontal Scaling
|
401 |
-
|
402 |
-
- Multiple n8n instances with queue mode
|
403 |
-
- Load balancer configuration
|
404 |
-
- Distributed vector store
|
405 |
-
|
406 |
-
### Performance Monitoring
|
407 |
-
|
408 |
-
- Enable n8n metrics: `N8N_METRICS=true`
|
409 |
-
- Database query monitoring
|
410 |
-
- Vector search performance tracking
|
411 |
-
- Container resource utilization
|
412 |
-
|
413 |
## 🔄 CI/CD Pipeline
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
- **Push to main**: Automatic deployment
|
418 |
-
- **Scheduled**: Daily backups and knowledge sync
|
419 |
-
- **Manual**: On-demand operations via GitHub Actions
|
420 |
-
|
421 |
-
### Pipeline Stages
|
422 |
-
|
423 |
-
1. **Build**: Docker image creation and testing
|
424 |
-
2. **Test**: Health checks and validation
|
425 |
-
3. **Deploy**: Hugging Face Spaces deployment
|
426 |
-
4. **Monitor**: Post-deployment verification
|
427 |
-
|
428 |
-
## 📝 Contributing
|
429 |
-
|
430 |
-
1. Fork the repository
|
431 |
-
2. Create feature branch: `git checkout -b feature/new-capability`
|
432 |
-
3. Commit changes: `git commit -am 'Add new capability'`
|
433 |
-
4. Push branch: `git push origin feature/new-capability`
|
434 |
-
5. Submit pull request
|
435 |
-
|
436 |
-
### Development Workflow
|
437 |
-
|
438 |
-
```bash
|
439 |
-
# Local development
|
440 |
-
docker-compose -f docker/docker-compose.yml up --build
|
441 |
-
|
442 |
-
# Run tests
|
443 |
-
./scripts/test-infrastructure.sh
|
444 |
-
|
445 |
-
# Deploy to staging
|
446 |
-
gh workflow run deploy-to-hf.yml -f force_deploy=true
|
447 |
-
```
|
448 |
-
|
449 |
-
## 📞 Support
|
450 |
-
|
451 |
-
- **Issues**: GitHub Issues
|
452 |
-
- **Documentation**: [n8n Documentation](https://docs.n8n.io)
|
453 |
-
- **Community**: [n8n Community](https://community.n8n.io)
|
454 |
-
|
455 |
-
## 📄 License
|
456 |
-
|
457 |
-
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
|
458 |
-
|
459 |
-
---
|
460 |
-
|
461 |
-
**⚡ Pro Tips**:
|
462 |
-
|
463 |
-
1. **Performance**: Use queue mode for high-volume workflows
|
464 |
-
2. **Security**: Regular credential rotation and access reviews
|
465 |
-
3. **Monitoring**: Set up alerts for failed workflows and system health
|
466 |
-
4. **Backup**: Test restore procedures regularly
|
467 |
-
5. **Knowledge**: Keep your knowledge base updated with latest best practices
|
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)
|
|
|
1 |
# n8n Infrastructure Repository
|
2 |
|
3 |
+
> **⚠️ Security Warning**
|
4 |
+
> A `.env` file with sensitive credentials was previously committed to this repository. Although the file has been removed, the credentials may still be present in the Git history. **It is crucial that you scrub the Git history of this repository and rotate all exposed secrets (API keys, database passwords, etc.) immediately.** Tools like [bfg-repo-cleaner](https://rtyley.github.io/bfg-repo-cleaner/) can help with this process.
|
5 |
+
|
6 |
A comprehensive, production-ready infrastructure setup for deploying n8n automation platform on Hugging Face Spaces with AI integrations and automated knowledge management.
|
7 |
|
8 |
## 🚀 Features
|
9 |
|
10 |
### Core Platform
|
11 |
+
- **n8n**: Self-hosted workflow automation platform.
|
12 |
+
- **Hugging Face Spaces**: Docker-based deployment with automatic scaling.
|
13 |
+
- **Supabase PostgreSQL**: SSL-encrypted database with pgvector extension.
|
14 |
+
- **ChromaDB**: Vector store for embeddings and AI-powered search.
|
|
|
15 |
|
16 |
### AI & Automation
|
17 |
+
- **LangChain Integration**: Advanced AI workflow capabilities.
|
18 |
+
- **Multi-Model Support**: OpenAI GPT, Anthropic Claude, Google Vertex AI.
|
19 |
+
- **Vector Knowledge Base**: Automated content ingestion with embeddings.
|
20 |
+
- **Community Nodes**: Extended functionality with custom AI nodes.
|
21 |
+
|
22 |
+
### DevOps & Security
|
23 |
+
- **GitHub Actions CI/CD**: Automated deployment and maintenance.
|
24 |
+
- **Optimized Docker Setup**: Non-root user and healthchecks for enhanced security and reliability.
|
25 |
+
- **Automated Full Backups**: Daily backups of database, workflows, and credentials.
|
26 |
+
- **Database Security**: Row Level Security (RLS) enabled by default.
|
27 |
+
- **Knowledge Sync**: Multi-repository content synchronization.
|
|
|
28 |
|
29 |
## 📋 Prerequisites
|
30 |
|
31 |
+
- **GitHub Account**
|
32 |
+
- **Hugging Face Account**
|
33 |
+
- **Supabase Account**
|
34 |
+
- **Git** and **Docker** installed locally
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
## 🛠️ Quick Start
|
37 |
|
38 |
### 1. Repository Setup
|
|
|
39 |
```bash
|
40 |
# Clone the repository
|
41 |
git clone https://github.com/your-username/n8n-infra.git
|
42 |
cd n8n-infra
|
43 |
|
44 |
+
# Create your local environment configuration from the example
|
45 |
+
cp config/.env.example config/.env
|
46 |
+
|
47 |
+
# Edit config/.env with your actual values.
|
48 |
+
# NEVER commit this file to Git.
|
49 |
```
|
50 |
|
51 |
### 2. Local Development
|
|
|
52 |
```bash
|
53 |
# Start the full stack locally
|
54 |
+
docker compose -f docker/docker-compose.yml up -d
|
55 |
|
56 |
# Check service status
|
57 |
+
docker compose -f docker/docker-compose.yml ps
|
58 |
|
59 |
# View logs
|
60 |
+
docker compose -f docker/docker-compose.yml logs -f n8n
|
61 |
```
|
62 |
|
63 |
### 3. Hugging Face Deployment
|
64 |
+
The repository is configured to automatically deploy to a Hugging Face Space on every push to the `main` branch.
|
65 |
```bash
|
66 |
# Trigger deployment via GitHub Actions
|
67 |
git push origin main
|
|
|
71 |
```
|
72 |
|
73 |
## 📊 Database Setup
|
74 |
+
The authoritative schema is defined in `supabase/schema.sql`. It is recommended to apply this schema to your Supabase project via the Supabase UI SQL Editor or by using Supabase migrations.
|
75 |
|
76 |
+
Key features of the schema include:
|
77 |
+
- A `knowledge` schema to encapsulate all knowledge base tables.
|
78 |
+
- `documents` and `embeddings` tables for storing content and its vector embeddings.
|
79 |
+
- A `vector_l2_ops` index on the `embeddings` table for efficient similarity search.
|
80 |
+
- **Row Level Security (RLS)** enabled on all tables to control data access. By default, data is public for reading, but only the `service_role` can write data.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
81 |
|
82 |
## 💾 Backup & Recovery
|
83 |
|
84 |
### Automated Backups
|
85 |
+
The `.github/workflows/backup-workflows.yml` GitHub Action runs nightly to create a full backup of your n8n instance. Each backup is a `.tar.gz` archive that includes:
|
86 |
+
- A full dump of the PostgreSQL database.
|
87 |
+
- A JSON export of all your n8n workflows.
|
88 |
+
- A copy of your `config` directory, which contains n8n credentials and settings.
|
89 |
|
90 |
+
### Manual Backup
|
91 |
+
To create a backup manually, you can run the `backup.sh` script. This requires you to have the necessary environment variables set (see `config/.env.example`).
|
92 |
+
```bash
|
93 |
+
# Make sure the script is executable
|
94 |
+
chmod +x scripts/backup.sh
|
95 |
|
96 |
+
# Run the script
|
97 |
+
./scripts/backup.sh
|
98 |
+
```
|
|
|
|
|
99 |
|
100 |
+
### Restore from Backup
|
101 |
+
To restore your n8n instance from a backup, use the `restore.sh` script.
|
102 |
|
103 |
+
**Warning:** This process will overwrite your existing database and configuration.
|
|
|
|
|
104 |
|
105 |
+
1. **Stop your n8n container** to prevent data corruption.
|
106 |
+
```bash
|
107 |
+
docker compose -f docker/docker-compose.yml stop n8n
|
108 |
+
```
|
109 |
+
2. Run the `restore.sh` script, providing the path to your backup file.
|
110 |
+
```bash
|
111 |
+
# Make sure the script is executable
|
112 |
+
chmod +x scripts/restore.sh
|
113 |
|
114 |
+
# Run the restore script
|
115 |
+
BACKUP_FILE=workflows/backup/n8n-backup-YYYYMMDD-HHMMSS.tar.gz ./scripts/restore.sh
|
116 |
+
```
|
117 |
+
3. The script will guide you through the process. It will restore the database and the `config` directory.
|
118 |
+
4. For workflows, the script will provide a `restored_workflows_*.json` file. You will need to import this file manually via the n8n UI or by using the `n8n-cli`.
|
119 |
+
5. **Restart your n8n container.**
|
120 |
+
```bash
|
121 |
+
docker compose -f docker/docker-compose.yml start n8n
|
122 |
+
```
|
123 |
|
124 |
+
## 🔒 Security
|
125 |
+
This repository has been optimized with security in mind.
|
126 |
|
127 |
+
- **Credential Management**: A `.gitignore` file is included to prevent committing sensitive files like `.env`. An example file `config/.env.example` is provided.
|
128 |
+
- **Container Security**: The `Dockerfile` is configured to run n8n as a non-root user, reducing the container's attack surface.
|
129 |
+
- **Database Security**: Row Level Security is enabled in the database schema (`supabase/schema.sql`).
|
130 |
+
- **Secret Rotation**: As mentioned in the security warning, it is critical to rotate any secrets that may have been exposed in the Git history.
|
131 |
|
132 |
## 🔧 Maintenance
|
133 |
|
134 |
### Health Monitoring
|
|
|
135 |
```bash
|
136 |
+
# Check container health (includes a healthcheck)
|
137 |
+
docker compose -f docker/docker-compose.yml ps
|
138 |
|
139 |
# View application logs
|
140 |
+
docker compose -f docker/docker-compose.yml logs -f n8n
|
|
|
|
|
|
|
141 |
```
|
142 |
|
143 |
### Performance Tuning
|
144 |
+
**Container Resources**: Resource limits are defined in `docker-compose.yml` to prevent resource exhaustion during local development.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
145 |
```yaml
|
146 |
# docker-compose.yml resource limits
|
147 |
services:
|
|
|
156 |
memory: 2G
|
157 |
```
|
158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
159 |
## 🔄 CI/CD Pipeline
|
160 |
+
The CI/CD pipelines are defined in the `.github/workflows` directory and are optimized for:
|
161 |
+
- **Efficiency**: The backup workflow uses a pre-built Docker container, and the knowledge sync workflow uses dependency caching to speed up execution.
|
162 |
+
- **Reliability**: The knowledge sync workflow uses `npm ci` for deterministic builds.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
163 |
|
164 |
---
|
165 |
+
_This README has been updated to reflect the infrastructure audit and optimization._
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config/{.env → .env.example}
RENAMED
@@ -1,10 +1,13 @@
|
|
1 |
# n8n Infrastructure Environment Configuration
|
2 |
-
# Copy this file to .env and fill in your actual values
|
|
|
3 |
|
4 |
# ===== CORE N8N CONFIGURATION =====
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
8 |
N8N_PUBLIC_API_DISABLED=false
|
9 |
N8N_LOG_LEVEL=info
|
10 |
N8N_METRICS=true
|
@@ -14,61 +17,60 @@ EXECUTIONS_DATA_SAVE_ON_ERROR=all
|
|
14 |
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
|
15 |
EXECUTIONS_DATA_PRUNE=true
|
16 |
EXECUTIONS_DATA_MAX_AGE=336
|
17 |
-
WEBHOOK_URL=https://
|
18 |
|
19 |
-
# ===== DATABASE CONFIGURATION =====
|
|
|
20 |
DB_TYPE=postgresdb
|
21 |
-
DB_POSTGRESDB_HOST=
|
22 |
DB_POSTGRESDB_PORT=6543
|
23 |
DB_POSTGRESDB_DATABASE=postgres
|
24 |
-
DB_POSTGRESDB_USER=postgres
|
25 |
DB_POSTGRESDB_SCHEMA=public
|
26 |
-
# NOTE: Keep DB password only in HF Space Secrets (runtime)
|
27 |
DB_POSTGRESDB_PASSWORD=
|
28 |
DB_POSTGRESDB_SSL=true
|
29 |
DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=false
|
30 |
|
31 |
# ===== DEPLOYMENT CONFIGURATION =====
|
32 |
-
|
33 |
HF_TOKEN=
|
34 |
-
HF_SPACE_NAME=
|
35 |
-
# NOTE: Move GITHUB_TOKEN to GitHub Actions Secrets (not used by runtime container)
|
36 |
GITHUB_TOKEN=
|
37 |
|
38 |
# ===== AI INTEGRATIONS =====
|
39 |
-
GOOGLE_PROJECT_ID=peppy-flame-468203-e0
|
40 |
-
GOOGLE_CREDENTIALS_PATH=/home/node/.n8n/credentials/google-service-account.json
|
41 |
-
GOOGLE_APPLICATION_CREDENTIALS=/home/node/.n8n/credentials/google-service-account.json
|
42 |
# NOTE: Keep AI/API keys in HF Space Secrets (runtime) and/or GitHub Actions Secrets
|
43 |
OPENAI_API_KEY=
|
44 |
ANTHROPIC_API_KEY=
|
45 |
-
|
46 |
-
|
|
|
|
|
47 |
|
48 |
-
# ===== VECTOR STORE CONFIGURATION =====
|
49 |
CHROMA_AUTH_TOKEN=
|
50 |
CHROMA_HOST=api.chroma.com
|
51 |
CHROMA_PORT=443
|
52 |
|
53 |
# ===== KNOWLEDGE BASE SYNC =====
|
|
|
54 |
KB_REPO_N8N=https://github.com/danilonovaisv/CHATGPT-knowledge-base.git
|
55 |
KB_BRANCH_N8N=main
|
56 |
-
|
57 |
-
|
58 |
-
KB_PATH_MIDJOURNEY=projects/midjorney-prompt
|
59 |
|
60 |
# ===== MONITORING AND LOGGING =====
|
61 |
-
|
62 |
-
N8N_METRICS=true
|
63 |
-
SENTRY_DSN=your-sentry-dsn (optional)
|
64 |
|
65 |
# ===== BACKUP CONFIGURATION =====
|
66 |
-
|
67 |
-
|
68 |
BACKUP_ENCRYPTION_PASSWORD=
|
69 |
|
70 |
# ===== SECURITY =====
|
71 |
-
|
72 |
-
|
|
|
|
|
73 |
RATE_LIMIT_WINDOW=15
|
74 |
-
RATE_LIMIT_MAX=100
|
|
|
1 |
# n8n Infrastructure Environment Configuration
|
2 |
+
# Copy this file to .env and fill in your actual values.
|
3 |
+
# NEVER commit the .env file to version control.
|
4 |
|
5 |
# ===== CORE N8N CONFIGURATION =====
|
6 |
+
# Generate with `openssl rand -hex 32`
|
7 |
+
N8N_ENCRYPTION_KEY=
|
8 |
+
# Generate with `openssl rand -hex 32`
|
9 |
+
N8N_USER_MANAGEMENT_JWT_SECRET=
|
10 |
+
N8N_HOST=your-n8n-host.hf.space
|
11 |
N8N_PUBLIC_API_DISABLED=false
|
12 |
N8N_LOG_LEVEL=info
|
13 |
N8N_METRICS=true
|
|
|
17 |
EXECUTIONS_DATA_SAVE_ON_SUCCESS=none
|
18 |
EXECUTIONS_DATA_PRUNE=true
|
19 |
EXECUTIONS_DATA_MAX_AGE=336
|
20 |
+
WEBHOOK_URL=https://your-n8n-host.hf.space/
|
21 |
|
22 |
+
# ===== DATABASE CONFIGURATION (SUPABASE) =====
|
23 |
+
# Find these in your Supabase project settings
|
24 |
DB_TYPE=postgresdb
|
25 |
+
DB_POSTGRESDB_HOST=
|
26 |
DB_POSTGRESDB_PORT=6543
|
27 |
DB_POSTGRESDB_DATABASE=postgres
|
28 |
+
DB_POSTGRESDB_USER=postgres
|
29 |
DB_POSTGRESDB_SCHEMA=public
|
30 |
+
# NOTE: Keep DB password only in HF Space Secrets (runtime) or a local .env file.
|
31 |
DB_POSTGRESDB_PASSWORD=
|
32 |
DB_POSTGRESDB_SSL=true
|
33 |
DB_POSTGRESDB_SSL_REJECT_UNAUTHORIZED=false
|
34 |
|
35 |
# ===== DEPLOYMENT CONFIGURATION =====
|
36 |
+
# NOTE: These should be GitHub Actions Secrets, not used by the runtime container.
|
37 |
HF_TOKEN=
|
38 |
+
HF_SPACE_NAME=your-username/your-space-name
|
|
|
39 |
GITHUB_TOKEN=
|
40 |
|
41 |
# ===== AI INTEGRATIONS =====
|
|
|
|
|
|
|
42 |
# NOTE: Keep AI/API keys in HF Space Secrets (runtime) and/or GitHub Actions Secrets
|
43 |
OPENAI_API_KEY=
|
44 |
ANTHROPIC_API_KEY=
|
45 |
+
GOOGLE_PROJECT_ID=
|
46 |
+
# The following are used if you mount a service account key file
|
47 |
+
GOOGLE_CREDENTIALS_PATH=/home/node/.n8n/credentials/google-service-account.json
|
48 |
+
GOOGLE_APPLICATION_CREDENTIALS=/home/node/.n8n/credentials/google-service-account.json
|
49 |
|
50 |
+
# ===== VECTOR STORE CONFIGURATION (CHROMA) =====
|
51 |
CHROMA_AUTH_TOKEN=
|
52 |
CHROMA_HOST=api.chroma.com
|
53 |
CHROMA_PORT=443
|
54 |
|
55 |
# ===== KNOWLEDGE BASE SYNC =====
|
56 |
+
# The repository to sync knowledge from
|
57 |
KB_REPO_N8N=https://github.com/danilonovaisv/CHATGPT-knowledge-base.git
|
58 |
KB_BRANCH_N8N=main
|
59 |
+
# Comma-separated list of directories inside the repo to sync
|
60 |
+
KB_PATH_N8N=projects/n8n,projects/videos-e-animacoes,projects/midjorney-prompt
|
|
|
61 |
|
62 |
# ===== MONITORING AND LOGGING =====
|
63 |
+
SENTRY_DSN=
|
|
|
|
|
64 |
|
65 |
# ===== BACKUP CONFIGURATION =====
|
66 |
+
# API key for the backup script to access n8n
|
67 |
+
N8N_API_KEY=
|
68 |
BACKUP_ENCRYPTION_PASSWORD=
|
69 |
|
70 |
# ===== SECURITY =====
|
71 |
+
# A comma-separated list of allowed origins for the n8n UI
|
72 |
+
ALLOWED_ORIGINS=https://your-n8n-host.hf.space
|
73 |
+
# Generate with `openssl rand -hex 32`
|
74 |
+
CSRF_SECRET=
|
75 |
RATE_LIMIT_WINDOW=15
|
76 |
+
RATE_LIMIT_MAX=100
|
docker/Dockerfile
CHANGED
@@ -19,6 +19,13 @@ ENV QUEUE_BULL_REDIS_DISABLED=true
|
|
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=
|
|
|
19 |
ENV N8N_METRICS=true
|
20 |
ENV QUEUE_HEALTH_CHECK_ACTIVE=true
|
21 |
|
22 |
+
# Add healthcheck
|
23 |
+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
24 |
+
CMD curl -f "http://localhost:${N8N_PORT:-5678}/healthz" || exit 1
|
25 |
+
|
26 |
+
# Switch to non-root user for security
|
27 |
+
USER node
|
28 |
+
|
29 |
# Database (set via Secrets in HF Space)
|
30 |
# ENV DB_TYPE=postgresdb
|
31 |
# ENV DB_POSTGRESDB_HOST=
|
docker/docker-compose.yml
CHANGED
@@ -31,3 +31,16 @@ services:
|
|
31 |
- ./config:/config
|
32 |
- ./workflows:/workflows
|
33 |
restart: unless-stopped
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
31 |
- ./config:/config
|
32 |
- ./workflows:/workflows
|
33 |
restart: unless-stopped
|
34 |
+
deploy:
|
35 |
+
resources:
|
36 |
+
limits:
|
37 |
+
cpus: "2.0"
|
38 |
+
memory: 4G
|
39 |
+
reservations:
|
40 |
+
cpus: "1.0"
|
41 |
+
memory: 2G
|
42 |
+
healthcheck:
|
43 |
+
test: ["CMD-SHELL", "curl -f http://localhost:5678/healthz"]
|
44 |
+
interval: 30s
|
45 |
+
timeout: 10s
|
46 |
+
retries: 3
|
scripts/backup.sh
CHANGED
@@ -10,14 +10,36 @@ set -euo pipefail
|
|
10 |
: "${N8N_API_KEY?Missing N8N_API_KEY}"
|
11 |
|
12 |
TS=$(date +%Y%m%d-%H%M%S)
|
13 |
-
|
|
|
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}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
22 |
|
23 |
-
echo "==> Done.
|
|
|
10 |
: "${N8N_API_KEY?Missing N8N_API_KEY}"
|
11 |
|
12 |
TS=$(date +%Y%m%d-%H%M%S)
|
13 |
+
BACKUP_NAME="n8n-backup-${TS}"
|
14 |
+
OUTDIR="workflows/backup/${BACKUP_NAME}"
|
15 |
mkdir -p "$OUTDIR"
|
16 |
|
17 |
+
# Use .pgpass for security
|
18 |
+
PGPASS_FILE=$(mktemp)
|
19 |
+
trap 'rm -f "$PGPASS_FILE"' EXIT # Ensure cleanup
|
20 |
+
echo "${DB_HOST}:${DB_PORT}:${DB_NAME}:${DB_USER}:${DB_PASSWORD}" > "${PGPASS_FILE}"
|
21 |
+
chmod 600 "${PGPASS_FILE}"
|
22 |
+
export PGPASSFILE="${PGPASS_FILE}"
|
23 |
+
|
24 |
echo "==> Dumping Postgres (Supabase) ..."
|
|
|
25 |
pg_dump -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" -F c -Z 5 -f "${OUTDIR}/db.dump"
|
26 |
|
27 |
echo "==> Exporting n8n workflows ..."
|
28 |
+
curl -sS -H "X-N8N-API-KEY: ${N8N_API_KEY}" "${N8N_BASE_URL}/rest/workflows" > "${OUTDIR}/workflows.json"
|
29 |
+
|
30 |
+
echo "==> Backing up n8n config and credentials ..."
|
31 |
+
# We assume the script is run from the repo root, and config is in ./config
|
32 |
+
if [ -d "config" ]; then
|
33 |
+
# Exclude .env if it exists, as it's for local dev
|
34 |
+
rsync -av --exclude '.env' config/ "${OUTDIR}/config/"
|
35 |
+
else
|
36 |
+
echo "Warning: 'config' directory not found. Skipping credentials backup."
|
37 |
+
fi
|
38 |
+
|
39 |
+
echo "==> Creating backup archive ..."
|
40 |
+
tar -czf "workflows/backup/${BACKUP_NAME}.tar.gz" -C "workflows/backup" "${BACKUP_NAME}"
|
41 |
+
|
42 |
+
# Clean up temporary directory
|
43 |
+
rm -rf "${OUTDIR}"
|
44 |
|
45 |
+
echo "==> Done. Backup created at workflows/backup/${BACKUP_NAME}.tar.gz"
|
scripts/restore.sh
CHANGED
@@ -6,9 +6,65 @@ set -euo pipefail
|
|
6 |
: "${DB_NAME?Missing DB_NAME}"
|
7 |
: "${DB_USER?Missing DB_USER}"
|
8 |
: "${DB_PASSWORD?Missing DB_PASSWORD}"
|
9 |
-
: "${
|
10 |
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
: "${DB_NAME?Missing DB_NAME}"
|
7 |
: "${DB_USER?Missing DB_USER}"
|
8 |
: "${DB_PASSWORD?Missing DB_PASSWORD}"
|
9 |
+
: "${BACKUP_FILE?Usage: BACKUP_FILE=/path/to/backup.tar.gz ./scripts/restore.sh}"
|
10 |
|
11 |
+
if [ ! -f "${BACKUP_FILE}" ]; then
|
12 |
+
echo "Error: Backup file not found at ${BACKUP_FILE}"
|
13 |
+
exit 1
|
14 |
+
fi
|
15 |
+
|
16 |
+
# Extract backup name from file path
|
17 |
+
BACKUP_NAME=$(basename "${BACKUP_FILE}" .tar.gz)
|
18 |
+
TMP_DIR="workflows/backup/${BACKUP_NAME}"
|
19 |
+
|
20 |
+
# 1. Extract backup
|
21 |
+
echo "==> Extracting backup archive ..."
|
22 |
+
mkdir -p "${TMP_DIR}"
|
23 |
+
tar -xzf "${BACKUP_FILE}" -C "workflows/backup"
|
24 |
+
|
25 |
+
DUMP_FILE="${TMP_DIR}/db.dump"
|
26 |
+
if [ ! -f "${DUMP_FILE}" ]; then
|
27 |
+
echo "Error: db.dump not found in backup archive."
|
28 |
+
rm -rf "${TMP_DIR}"
|
29 |
+
exit 1
|
30 |
+
fi
|
31 |
+
|
32 |
+
# 2. Restore Database
|
33 |
+
# Use .pgpass for security
|
34 |
+
PGPASS_FILE=$(mktemp)
|
35 |
+
trap 'rm -f "$PGPASS_FILE" "$TMP_DIR"' EXIT # Ensure cleanup
|
36 |
+
echo "${DB_HOST}:${DB_PORT}:${DB_NAME}:${DB_USER}:${DB_PASSWORD}" > "${PGPASS_FILE}"
|
37 |
+
chmod 600 "${PGPASS_FILE}"
|
38 |
+
export PGPASSFILE="${PGPASS_FILE}"
|
39 |
+
|
40 |
+
echo "==> Restoring Postgres database ..."
|
41 |
+
pg_restore -h "${DB_HOST}" -p "${DB_PORT}" -U "${DB_USER}" -d "${DB_NAME}" --clean --if-exists "${DUMP_FILE}"
|
42 |
+
|
43 |
+
# 3. Restore Config
|
44 |
+
echo "==> Restoring n8n config and credentials ..."
|
45 |
+
if [ -d "${TMP_DIR}/config" ]; then
|
46 |
+
# Important: Stop n8n before replacing config files to avoid corruption
|
47 |
+
echo "Make sure the n8n container is stopped before proceeding."
|
48 |
+
read -p "Press enter to continue"
|
49 |
+
|
50 |
+
rsync -av --delete "${TMP_DIR}/config/" "config/"
|
51 |
+
echo "Config restored. Please restart the n8n container."
|
52 |
+
else
|
53 |
+
echo "Warning: 'config' directory not found in backup. Skipping."
|
54 |
+
fi
|
55 |
+
|
56 |
+
# 4. Restore Workflows
|
57 |
+
echo "==> Restoring n8n workflows ..."
|
58 |
+
if [ -f "${TMP_DIR}/workflows.json" ]; then
|
59 |
+
# n8n can automatically load workflows from the /workflows directory
|
60 |
+
# We need to split the JSON array into individual files
|
61 |
+
# This is complex in bash, so we'll provide the file for manual import
|
62 |
+
# and add instructions in the README
|
63 |
+
cp "${TMP_DIR}/workflows.json" "workflows/restored_workflows_${BACKUP_NAME}.json"
|
64 |
+
echo "Workflows JSON saved to workflows/restored_workflows_${BACKUP_NAME}.json"
|
65 |
+
echo "Please import them manually via the n8n UI or use the n8n-cli."
|
66 |
+
else
|
67 |
+
echo "Warning: 'workflows.json' not found in backup. Skipping."
|
68 |
+
fi
|
69 |
+
|
70 |
+
echo "==> Restore complete."
|
scripts/sync-knowledge.mjs
CHANGED
@@ -2,7 +2,7 @@
|
|
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 {
|
6 |
import fs from 'node:fs';
|
7 |
import path from 'node:path';
|
8 |
import process from 'node:process';
|
@@ -27,13 +27,23 @@ const supabase = createClient(SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY);
|
|
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 |
-
|
34 |
} else {
|
35 |
console.log('Pulling KB repo...');
|
36 |
-
|
37 |
}
|
38 |
|
39 |
const dirs = KNOWLEDGE_DIRS.split(',').map(s => s.trim());
|
@@ -46,11 +56,19 @@ async function upsertDoc(pth, content) {
|
|
46 |
|
47 |
// Upsert document
|
48 |
const { data: doc, error: docErr } = await supabase
|
49 |
-
.from('
|
50 |
-
.upsert({ path: pth, title, content, hash }, { onConflict: 'path' })
|
51 |
-
.select()
|
52 |
.single();
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
if (openai) {
|
56 |
// Embedding
|
@@ -62,15 +80,18 @@ async function upsertDoc(pth, content) {
|
|
62 |
const vector = emb.data[0].embedding;
|
63 |
|
64 |
const { error: embErr } = await supabase
|
65 |
-
.from('
|
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)) {
|
@@ -79,17 +100,26 @@ async function main() {
|
|
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 |
-
|
89 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
90 |
}
|
91 |
}
|
92 |
-
console.log(
|
|
|
|
|
|
|
93 |
}
|
94 |
|
95 |
main().catch(err => { console.error(err); process.exit(1); });
|
|
|
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 { spawnSync } from 'node:child_process';
|
6 |
import fs from 'node:fs';
|
7 |
import path from 'node:path';
|
8 |
import process from 'node:process';
|
|
|
27 |
const workdir = path.resolve('knowledge');
|
28 |
if (!fs.existsSync(workdir)) fs.mkdirSync(workdir, { recursive: true });
|
29 |
|
30 |
+
function runCommand(command, args, options = {}) {
|
31 |
+
const result = spawnSync(command, args, { stdio: 'inherit', ...options });
|
32 |
+
if (result.error) {
|
33 |
+
throw result.error;
|
34 |
+
}
|
35 |
+
if (result.status !== 0) {
|
36 |
+
throw new Error(`Command failed: ${command} ${args.join(' ')}`);
|
37 |
+
}
|
38 |
+
}
|
39 |
+
|
40 |
const repoDir = path.join(workdir, 'CHATGPT-knowledge-base');
|
41 |
if (!fs.existsSync(repoDir)) {
|
42 |
console.log('Cloning KB repo...');
|
43 |
+
runCommand('git', ['clone', '--depth', '1', KNOWLEDGE_REPO_URL, repoDir]);
|
44 |
} else {
|
45 |
console.log('Pulling KB repo...');
|
46 |
+
runCommand('git', ['pull'], { cwd: repoDir });
|
47 |
}
|
48 |
|
49 |
const dirs = KNOWLEDGE_DIRS.split(',').map(s => s.trim());
|
|
|
56 |
|
57 |
// Upsert document
|
58 |
const { data: doc, error: docErr } = await supabase
|
59 |
+
.from('documents')
|
60 |
+
.upsert({ path: pth, title, content, hash, updated_at: new Date() }, { onConflict: 'path' })
|
61 |
+
.select('id, hash')
|
62 |
.single();
|
63 |
+
|
64 |
+
if (docErr) throw new Error(`Supabase doc upsert error: ${docErr.message}`);
|
65 |
+
if (!doc) throw new Error('Upsert did not return a document.');
|
66 |
+
|
67 |
+
// If hash is the same, skip embedding
|
68 |
+
if (doc.hash === hash && !process.env.FORCE_REEMBED) {
|
69 |
+
console.log(`Skipping ${pth} (content unchanged)`);
|
70 |
+
return;
|
71 |
+
}
|
72 |
|
73 |
if (openai) {
|
74 |
// Embedding
|
|
|
80 |
const vector = emb.data[0].embedding;
|
81 |
|
82 |
const { error: embErr } = await supabase
|
83 |
+
.from('embeddings')
|
84 |
+
.upsert({ doc_id: doc.id, embedding: vector, model: 'text-embedding-3-large' }, { onConflict: 'doc_id' });
|
85 |
+
if (embErr) throw new Error(`Supabase embedding upsert error: ${embErr.message}`);
|
86 |
} else {
|
87 |
console.warn('OPENAI_API_KEY not set, skipping embeddings for', pth);
|
88 |
}
|
89 |
}
|
90 |
|
91 |
async function main() {
|
92 |
+
let successCount = 0;
|
93 |
+
let errorCount = 0;
|
94 |
+
|
95 |
for (const rel of dirs) {
|
96 |
const abs = path.join(repoDir, rel);
|
97 |
if (!fs.existsSync(abs)) {
|
|
|
100 |
}
|
101 |
const entries = await fs.promises.readdir(abs, { withFileTypes: true });
|
102 |
for (const ent of entries) {
|
103 |
+
if (ent.isDirectory() || !/\.(md|markdown|json|txt)$/i.test(ent.name)) {
|
104 |
+
continue;
|
105 |
+
}
|
106 |
const full = path.join(abs, ent.name);
|
|
|
|
|
|
|
|
|
107 |
const repoRelPath = path.relative(repoDir, full);
|
108 |
+
try {
|
109 |
+
const content = await fs.promises.readFile(full, 'utf8');
|
110 |
+
console.log('Ingesting:', repoRelPath);
|
111 |
+
await upsertDoc(repoRelPath, content);
|
112 |
+
successCount++;
|
113 |
+
} catch (err) {
|
114 |
+
console.error(`Failed to process ${repoRelPath}: ${err.message}`);
|
115 |
+
errorCount++;
|
116 |
+
}
|
117 |
}
|
118 |
}
|
119 |
+
console.log(`\nSync complete. ${successCount} processed, ${errorCount} errors.`);
|
120 |
+
if (errorCount > 0) {
|
121 |
+
process.exit(1);
|
122 |
+
}
|
123 |
}
|
124 |
|
125 |
main().catch(err => { console.error(err); process.exit(1); });
|
scripts/test-infrastructure.sh
CHANGED
@@ -32,8 +32,9 @@ run_test() {
|
|
32 |
|
33 |
((TESTS_TOTAL++))
|
34 |
log_test "Running: $test_name"
|
|
|
35 |
|
36 |
-
if eval "$test_command"
|
37 |
echo " ✅ PASSED"
|
38 |
((TESTS_PASSED++))
|
39 |
return 0
|
@@ -53,10 +54,10 @@ fi
|
|
53 |
test_docker() {
|
54 |
log_info "Testing Docker configuration..."
|
55 |
|
56 |
-
run_test "Docker daemon accessible" "docker --version"
|
57 |
-
run_test "Docker Compose available" "docker
|
58 |
-
run_test "Dockerfile syntax" "
|
59 |
-
run_test "Docker Compose syntax" "docker
|
60 |
}
|
61 |
|
62 |
# Test environment configuration
|
@@ -64,7 +65,7 @@ test_environment() {
|
|
64 |
log_info "Testing environment configuration..."
|
65 |
|
66 |
run_test "Environment example exists" "[[ -f config/.env.example ]]"
|
67 |
-
run_test "Required directories exist" "[[ -d workflows && -d
|
68 |
|
69 |
if [[ -f .env ]]; then
|
70 |
run_test "Environment file loaded" "[[ -n \${N8N_ENCRYPTION_KEY:-} ]]"
|
@@ -80,10 +81,9 @@ test_scripts() {
|
|
80 |
|
81 |
run_test "Backup script executable" "[[ -x scripts/backup.sh ]]"
|
82 |
run_test "Restore script executable" "[[ -x scripts/restore.sh ]]"
|
83 |
-
run_test "Sync script
|
84 |
run_test "Backup script syntax" "bash -n scripts/backup.sh"
|
85 |
run_test "Restore script syntax" "bash -n scripts/restore.sh"
|
86 |
-
run_test "Sync script syntax" "bash -n scripts/sync-knowledge.sh"
|
87 |
}
|
88 |
|
89 |
# Test GitHub Actions
|
@@ -117,32 +117,18 @@ test_database() {
|
|
117 |
fi
|
118 |
}
|
119 |
|
120 |
-
# Test knowledge base
|
121 |
-
test_knowledge() {
|
122 |
-
log_info "Testing knowledge base configuration..."
|
123 |
-
|
124 |
-
run_test "Knowledge directories exist" "[[ -d knowledge/n8n && -d knowledge/videos-e-animacoes && -d knowledge/midjourney-prompt ]]"
|
125 |
-
|
126 |
-
# Test Python dependencies for embedding generation
|
127 |
-
if command -v python3 > /dev/null; then
|
128 |
-
run_test "Python available" "python3 --version"
|
129 |
-
run_test "Required Python packages" "python3 -c 'import sentence_transformers, json, hashlib'"
|
130 |
-
fi
|
131 |
-
}
|
132 |
-
|
133 |
# Integration tests
|
134 |
test_integration() {
|
135 |
log_info "Running integration tests..."
|
136 |
|
137 |
# Test if we can start services locally
|
138 |
-
if run_test "Start services locally" "docker
|
139 |
sleep 30
|
140 |
|
141 |
-
run_test "n8n health endpoint" "curl -f http://localhost:
|
142 |
-
run_test "Vector store endpoint" "curl -f http://localhost:8000/api/v1/heartbeat"
|
143 |
|
144 |
# Cleanup
|
145 |
-
docker
|
146 |
else
|
147 |
log_error "Failed to start services - skipping integration tests"
|
148 |
fi
|
@@ -161,10 +147,9 @@ main() {
|
|
161 |
test_scripts
|
162 |
test_github_actions
|
163 |
test_database
|
164 |
-
test_knowledge
|
165 |
|
166 |
# Skip integration tests if Docker unavailable
|
167 |
-
if docker --version > /dev/null 2>&1; then
|
168 |
test_integration
|
169 |
else
|
170 |
log_warn "Docker not available - skipping integration tests"
|
|
|
32 |
|
33 |
((TESTS_TOTAL++))
|
34 |
log_test "Running: $test_name"
|
35 |
+
echo " Executing: $test_command"
|
36 |
|
37 |
+
if eval "$test_command"; then
|
38 |
echo " ✅ PASSED"
|
39 |
((TESTS_PASSED++))
|
40 |
return 0
|
|
|
54 |
test_docker() {
|
55 |
log_info "Testing Docker configuration..."
|
56 |
|
57 |
+
run_test "Docker daemon accessible" "sudo docker --version"
|
58 |
+
run_test "Docker Compose available" "sudo docker compose version"
|
59 |
+
run_test "Dockerfile syntax" "sudo docker build -f docker/Dockerfile -t n8n-test-build ."
|
60 |
+
run_test "Docker Compose syntax" "sudo docker compose -f docker/docker-compose.yml config"
|
61 |
}
|
62 |
|
63 |
# Test environment configuration
|
|
|
65 |
log_info "Testing environment configuration..."
|
66 |
|
67 |
run_test "Environment example exists" "[[ -f config/.env.example ]]"
|
68 |
+
run_test "Required directories exist" "[[ -d workflows && -d scripts ]]"
|
69 |
|
70 |
if [[ -f .env ]]; then
|
71 |
run_test "Environment file loaded" "[[ -n \${N8N_ENCRYPTION_KEY:-} ]]"
|
|
|
81 |
|
82 |
run_test "Backup script executable" "[[ -x scripts/backup.sh ]]"
|
83 |
run_test "Restore script executable" "[[ -x scripts/restore.sh ]]"
|
84 |
+
run_test "Sync script syntax" "bash -n scripts/sync-knowledge.sh"
|
85 |
run_test "Backup script syntax" "bash -n scripts/backup.sh"
|
86 |
run_test "Restore script syntax" "bash -n scripts/restore.sh"
|
|
|
87 |
}
|
88 |
|
89 |
# Test GitHub Actions
|
|
|
117 |
fi
|
118 |
}
|
119 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
120 |
# Integration tests
|
121 |
test_integration() {
|
122 |
log_info "Running integration tests..."
|
123 |
|
124 |
# Test if we can start services locally
|
125 |
+
if run_test "Start services locally" "sudo docker compose -f docker/docker-compose.yml up -d --build"; then
|
126 |
sleep 30
|
127 |
|
128 |
+
run_test "n8n health endpoint" "curl -f http://localhost:5678/healthz"
|
|
|
129 |
|
130 |
# Cleanup
|
131 |
+
sudo docker compose -f docker/docker-compose.yml down > /dev/null 2>&1
|
132 |
else
|
133 |
log_error "Failed to start services - skipping integration tests"
|
134 |
fi
|
|
|
147 |
test_scripts
|
148 |
test_github_actions
|
149 |
test_database
|
|
|
150 |
|
151 |
# Skip integration tests if Docker unavailable
|
152 |
+
if sudo docker --version > /dev/null 2>&1; then
|
153 |
test_integration
|
154 |
else
|
155 |
log_warn "Docker not available - skipping integration tests"
|
supabase/schema.sql
CHANGED
@@ -30,3 +30,17 @@ 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;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
33 |
+
|
34 |
+
-- ==> RLS (Row Level Security)
|
35 |
+
-- 1. Enable RLS
|
36 |
+
alter table knowledge.documents enable row level security;
|
37 |
+
alter table knowledge.embeddings enable row level security;
|
38 |
+
|
39 |
+
-- 2. Create policies
|
40 |
+
-- Allow public read access to all
|
41 |
+
create policy "Allow public read access" on knowledge.documents for select using (true);
|
42 |
+
create policy "Allow public read access" on knowledge.embeddings for select using (true);
|
43 |
+
|
44 |
+
-- Allow service_role (server-side) to perform all actions
|
45 |
+
create policy "Allow all for service_role" on knowledge.documents for all to service_role with check (true);
|
46 |
+
create policy "Allow all for service_role" on knowledge.embeddings for all to service_role with check (true);
|
test_output.log
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
[0;32m[INFO][0m 🧪 Starting n8n Infrastructure Test Suite
|
2 |
+
================================================
|
3 |
+
[0;32m[INFO][0m Testing Docker configuration...
|