Passa al contenuto principale

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)
})
})