API Endpoints
Il worker espone diversi endpoint HTTP per la gestione delle operazioni di elaborazione, monitoraggio e controllo del sistema.
Architettura API
Express Server
- Porta: 3300
- Middleware: CORS, JWT Authentication, Multer
- Logging: Winston per tracciamento richieste
- Validation: Zod per validazione input
Configurazione Base
/server.ts
// Configurazione Express
const app = express()
const PORT = 3300
// Middleware
app.use(cors(corsOptions))
app.use(express.json({ limit: '50mb' }))
app.use(express.urlencoded({ extended: true, limit: '50mb' }))
Endpoint Pubblici
Health Check
GET /health
Descrizione: Verifica stato del worker
Response:
{
"status": "healthy",
"timestamp": "2024-01-15T10:30:00Z",
"uptime": 3600,
"memory": {
"used": "45.2MB",
"free": "1.2GB"
}
}
Endpoint Autenticati
Gestione Task
Aggiunta Task
POST /tasks/add
Authorization: Bearer <jwt_token>
Content-Type: multipart/form-data
Descrizione: Aggiunge un nuovo task di elaborazione
Body:
Per multipart/form-data (file upload):
{
collectionName: string
// + file nel form data
}
Per JSON requests:
{
type: 'embed:files' | 'embed:urls' | 'embed:products' | 'chat:message' | 'query:embedding'
payload: {
// Payload specifico per ogni tipo
}
}
Esempi di payload per ogni tipo:
/src/services/tasksService.ts
// Files embedding
{
type: 'embed:files',
payload: {
collectionName: 'my-collection'
}
}
// URLs embedding
{
type: 'embed:urls',
payload: {
collectionName: 'my-collection',
urls: ['https://example.com', 'https://test.com'],
urlType: 'single' | 'multiple' | 'sitemap',
userId: 123
}
}
// Products embedding
{
type: 'embed:products',
payload: {
collectionName: 'my-collection',
websiteUrl: 'https://shop.example.com',
agentUuid: 'uuid-here'
}
}
// Chat message
{
type: 'chat:message',
payload: {
messages: [...],
message_uuid: 'uuid',
model: 'gpt-4',
conversationUuid: 'uuid',
tokens: { input_tokens: 100, output_tokens: 50, total_tokens: 150, cache_read: 0 }
}
}
// Query embedding
{
type: 'query:embedding',
payload: {
query: 'search query here'
}
}
Response:
{
"success": true,
"task_id": "uuid",
"status": "queued",
"message": "Task added successfully"
}
Eliminazione Task
DELETE /tasks/:id
Authorization: Bearer <jwt_token>
Descrizione: Elimina un task specifico
Response:
{
"success": true,
"message": "Task deleted successfully"
}
Stato Task
GET /tasks/:id
Authorization: Bearer <jwt_token>
Descrizione: Ottiene lo stato di un task
Response:
{
"task_id": "uuid",
"status": "completed",
"progress": 100,
"created_at": "2024-01-15T10:30:00Z",
"completed_at": "2024-01-15T10:35:00Z",
"result": {
"embeddings_generated": 150,
"chunks_processed": 75
}
}
Gestione Collection
Creazione Collection
POST /collections
Authorization: Bearer <jwt_token>
Content-Type: application/json
Body:
{
"collection_name": "my-collection",
"vector_size": 768,
"distance": "Cosine"
}
Response:
{
"success": true,
"collection_name": "my-collection",
"message": "Collection created successfully"
}
Eliminazione Collection
DELETE /collections/:collectionName
Authorization: Bearer <jwt_token>
Response:
{
"success": true,
"message": "Collection deleted successfully"
}
Eliminazione Risorsa da Collection
DELETE /collections/:collectionName/:resourceUuid
Authorization: Bearer <jwt_token>
Response:
{
"success": true,
"message": "Resource deleted from collection"
}
Monitoraggio Code
Stato Code
GET /queue/tasks/:taskUuid
Authorization: Bearer <jwt_token>
Descrizione: Ottiene informazioni dettagliate su un task nella coda
Response:
{
"task_uuid": "uuid",
"status": "processing",
"workflow": "resource-embedding-workflow",
"progress": 65,
"started_at": "2024-01-15T10:30:00Z",
"estimated_completion": "2024-01-15T10:35:00Z",
"logs": [
{
"timestamp": "2024-01-15T10:30:15Z",
"level": "info",
"message": "Processing file: document.pdf"
}
]
}
Autenticazione
JWT Authentication
/server.ts
// Middleware di autenticazione
const authenticateJWT = (req: Request, res: Response, next: NextFunction) => {
const authHeader = req.headers.authorization
const token = authHeader && authHeader.split(' ')[1]
if (!token) {
return res.status(401).json({ error: 'Access token required' })
}
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET)
req.user = decoded
next()
} catch (error) {
return res.status(403).json({ error: 'Invalid token' })
}
}
Endpoint Pubblici
/server.ts
// Endpoint che non richiedono autenticazione
const publicEndpoints = ['/health']
Validazione Input
Schema Validazione
/server.ts
// Schema per task ID
const taskIdSchema = z.object({
id: z.uuid('Task ID must be a valid UUID format'),
})
// Schema per collection
const collectionSchema = z.object({
collection_name: z.string().min(1).max(100),
vector_size: z.number().min(1).max(4096),
distance: z.enum(['Cosine', 'Euclidean', 'Dot']),
})
Middleware Validazione
/server.ts
// Middleware di validazione
const validateRequest = (schema: z.ZodSchema) => {
return (req: Request, res: Response, next: NextFunction) => {
try {
schema.parse(req.params)
next()
} catch (error) {
return res.status(400).json({
error: 'Validation failed',
details: error.errors,
})
}
}
}
Gestione File Upload
Configurazione Multer
/src/services/tasksService.ts
// Configurazione Multer per upload file
const upload = multer({
storage: multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads/')
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9)
cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname))
},
}),
limits: {
fileSize: 50 * 1024 * 1024, // 50MB
files: 1,
},
fileFilter: (req, file, cb) => {
const allowedTypes = [
'application/pdf',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'text/plain',
'text/csv',
'application/json',
]
if (allowedTypes.includes(file.mimetype)) {
cb(null, true)
} else {
cb(new Error('File type not allowed'), false)
}
},
})
Gestione Errori Upload
/src/services/tasksService.ts
// Gestione errori Multer
const handleMulterError = (error: any, req: Request, res: Response, next: NextFunction) => {
if (error instanceof multer.MulterError) {
if (error.code === 'LIMIT_FILE_SIZE') {
return res.status(400).json({
error: 'File too large',
message: 'Maximum file size is 50MB',
})
}
if (error.code === 'LIMIT_FILE_COUNT') {
return res.status(400).json({
error: 'Too many files',
message: 'Maximum 1 file allowed',
})
}
}
if (error.message === 'File type not allowed') {
return res.status(400).json({
error: 'Invalid file type',
message: 'Only PDF, DOCX, TXT, CSV, and JSON files are allowed',
})
}
next(error)
}
CORS Configuration
Configurazione CORS
/server.ts
// Configurazione CORS
const corsOptions = {
origin: true, // Allow all origins in development
methods: ['GET', 'POST', 'DELETE', 'OPTIONS'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
optionsSuccessStatus: 200,
}
Error Handling
Global Error Handler
/server.ts
// Gestione errori globale
app.use((error: any, req: Request, res: Response, next: NextFunction) => {
logger.error('Unhandled error', {
error: error.message,
stack: error.stack,
url: req.url,
method: req.method,
})
res.status(500).json({
error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? error.message : 'Something went wrong',
})
})
Errori Specifici
/src/utilities/validation.ts
// Errori personalizzati
class ValidationError extends Error {
constructor(message: string, public field: string) {
super(message)
this.name = 'ValidationError'
}
}
class TaskNotFoundError extends Error {
constructor(taskId: string) {
super(`Task ${taskId} not found`)
this.name = 'TaskNotFoundError'
}
}
Rate Limiting
Rate Limiting per Endpoint
/server.ts
// Rate limiting per endpoint specifici
const rateLimit = require('express-rate-limit')
const taskRateLimit = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minuti
max: 100, // 100 richieste per finestra
message: 'Too many task requests, please try again later',
})
app.use('/tasks/add', taskRateLimit)
Logging e Monitoring
Request Logging
/server.ts
// Logging richieste
app.use((req: Request, res: Response, next: NextFunction) => {
const start = Date.now()
res.on('finish', () => {
const duration = Date.now() - start
logger.info('HTTP Request', {
method: req.method,
url: req.url,
status: res.statusCode,
duration: `${duration}ms`,
userAgent: req.get('User-Agent'),
ip: req.ip,
})
})
next()
})
Health Check Dettagliato
/server.ts
// Health check con metriche dettagliate
app.get('/health', (req: Request, res: Response) => {
const memoryUsage = process.memoryUsage()
const uptime = process.uptime()
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: `${Math.floor(uptime)}s`,
memory: {
used: `${Math.round(memoryUsage.heapUsed / 1024 / 1024)}MB`,
free: `${Math.round(memoryUsage.heapTotal / 1024 / 1024)}MB`,
total: `${Math.round(memoryUsage.rss / 1024 / 1024)}MB`,
},
environment: process.env.NODE_ENV,
version: process.env.npm_package_version,
})
})
Testing API
Test Endpoint
/tests/api-endpoints.test.ts
// Test per endpoint API
describe('API Endpoints', () => {
it('should return health status', async () => {
const response = await request(app).get('/health').expect(200)
expect(response.body.status).toBe('healthy')
})
it('should require authentication for protected endpoints', async () => {
await request(app).post('/tasks/add').expect(401)
})
})