### 🔧 Technical Solution - **API Key Authentication**: Migrated from Authelia session auth to X-N8N-API-KEY header authentication - **CORS Resolution**: Eliminated preflight failures by removing authentication redirects from webhook endpoints - **Error Handling**: Added graceful fallback for empty N8N responses with intelligent question generation - **Type Safety**: Updated TypeScript interfaces for enhanced response format support ### 🛡️ Security Enhancements - **Maintained Security**: N8N UI still protected by Authelia while webhooks use API key authentication - **Audit Trail**: All webhook requests logged with API key identification for security monitoring - **Rate Limiting**: Applied through Traefik middleware to prevent API abuse - **Easy Key Rotation**: API keys can be changed instantly without affecting user sessions ### 📱 Application Updates - **N8nSyncService**: Complete migration from Authelia to API key authentication - **CV Upload Component**: Simplified flow without authentication popups for N8N integration - **Fallback System**: Intelligent question generation based on CV content when N8N unavailable - **User Experience**: Seamless PDF upload to analysis workflow without CORS barriers ### 🐳 Docker Configuration - **Multi-stage Build**: Optimized Dockerfile with Node.js 20 and nginx:alpine - **Docker Compose**: Complete service orchestration with port 3007 mapping - **Nginx Configuration**: Custom MIME types for PDF.js worker files and SPA routing - **SSL Integration**: Traefik labels for automatic HTTPS with proper CORS headers ### 🧪 Testing Results - ✅ **PDF Processing**: Successfully extracts text from uploaded CVs (2871+ characters) - ✅ **CORS Success**: OPTIONS and POST requests work without authentication redirects - ✅ **Webhook Integration**: Connects to N8N with X-N8N-API-KEY header - ✅ **Fallback Questions**: Generates contextual questions when N8N workflow unavailable - ✅ **Type Safety**: No TypeScript compilation errors with updated interfaces ### 💡 Intelligent Fallback Features - **Technical Questions**: Generated based on actual CV skills (e.g., JavaScript experience) - **Behavioral Questions**: Standard problem-solving and teamwork assessments - **Experience-Specific**: Company and role-specific questions from work history - **Career Development**: Growth and motivation questions tailored to experience level ### 🔗 Integration Points - **Environment Config**: Added N8N API key and base URL configuration - **Service Communication**: Direct HTTP with API key headers (no session dependency) - **Response Handling**: Support for both N8N workflow responses and local fallback - **Error Recovery**: Graceful degradation when external services unavailable 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
154 lines
4.1 KiB
JSON
154 lines
4.1 KiB
JSON
{
|
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
|
"version": 1,
|
|
"newProjectRoot": "projects",
|
|
"projects": {
|
|
"interview-assistant": {
|
|
"projectType": "application",
|
|
"schematics": {
|
|
"@schematics/angular:component": {
|
|
"style": "scss"
|
|
}
|
|
},
|
|
"root": "",
|
|
"sourceRoot": "src",
|
|
"prefix": "app",
|
|
"architect": {
|
|
"build": {
|
|
"builder": "@angular/build:application",
|
|
"options": {
|
|
"browser": "src/main.ts",
|
|
"polyfills": [
|
|
"zone.js"
|
|
],
|
|
"tsConfig": "tsconfig.app.json",
|
|
"inlineStyleLanguage": "scss",
|
|
"assets": [
|
|
{
|
|
"glob": "**/*",
|
|
"input": "public"
|
|
},
|
|
{
|
|
"glob": "**/*",
|
|
"input": "node_modules/pdfjs-dist/build",
|
|
"output": "pdfjs-dist/build"
|
|
}
|
|
],
|
|
"styles": [
|
|
"src/styles.scss"
|
|
]
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"budgets": [
|
|
{
|
|
"type": "initial",
|
|
"maximumWarning": "2MB",
|
|
"maximumError": "5MB"
|
|
},
|
|
{
|
|
"type": "anyComponentStyle",
|
|
"maximumWarning": "20kB",
|
|
"maximumError": "50kB"
|
|
}
|
|
],
|
|
"outputHashing": "all"
|
|
},
|
|
"development": {
|
|
"optimization": false,
|
|
"extractLicenses": false,
|
|
"sourceMap": true
|
|
}
|
|
},
|
|
"defaultConfiguration": "production"
|
|
},
|
|
"serve": {
|
|
"builder": "@angular/build:dev-server",
|
|
"configurations": {
|
|
"production": {
|
|
"buildTarget": "interview-assistant:build:production"
|
|
},
|
|
"development": {
|
|
"buildTarget": "interview-assistant:build:development"
|
|
}
|
|
},
|
|
"defaultConfiguration": "development"
|
|
},
|
|
"extract-i18n": {
|
|
"builder": "@angular/build:extract-i18n"
|
|
},
|
|
"test": {
|
|
"builder": "@angular/build:karma",
|
|
"options": {
|
|
"polyfills": [
|
|
"zone.js",
|
|
"zone.js/testing"
|
|
],
|
|
"tsConfig": "tsconfig.spec.json",
|
|
"inlineStyleLanguage": "scss",
|
|
"assets": [
|
|
{
|
|
"glob": "**/*",
|
|
"input": "public"
|
|
}
|
|
],
|
|
"styles": [
|
|
"src/styles.scss"
|
|
]
|
|
}
|
|
},
|
|
"cypress-run": {
|
|
"builder": "@cypress/schematic:cypress",
|
|
"options": {
|
|
"devServerTarget": "interview-assistant:serve"
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"devServerTarget": "interview-assistant:serve:production"
|
|
}
|
|
}
|
|
},
|
|
"cypress-open": {
|
|
"builder": "@cypress/schematic:cypress",
|
|
"options": {
|
|
"watch": true,
|
|
"headless": false
|
|
}
|
|
},
|
|
"ct": {
|
|
"builder": "@cypress/schematic:cypress",
|
|
"options": {
|
|
"devServerTarget": "interview-assistant:serve",
|
|
"watch": true,
|
|
"headless": false,
|
|
"testingType": "component"
|
|
},
|
|
"configurations": {
|
|
"development": {
|
|
"devServerTarget": "interview-assistant:serve:development"
|
|
}
|
|
}
|
|
},
|
|
"e2e": {
|
|
"builder": "@cypress/schematic:cypress",
|
|
"options": {
|
|
"devServerTarget": "interview-assistant:serve",
|
|
"watch": true,
|
|
"headless": false
|
|
},
|
|
"configurations": {
|
|
"production": {
|
|
"devServerTarget": "interview-assistant:serve:production"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"cli": {
|
|
"schematicCollections": [
|
|
"@cypress/schematic",
|
|
"@schematics/angular"
|
|
]
|
|
}
|
|
} |