Files
vision-server/routes/jobs.js
khalid@traclabs.com 270e088c15 First cut
2026-04-18 15:22:12 -05:00

79 lines
2.9 KiB
JavaScript

import { Job } from '../db/models.js';
import { enqueueJob } from '../jobs/queue.js';
import { registerClient } from '../ws/broadcast.js';
import { findModel, DEFAULT_MODEL_ID } from '../models.js';
/**
* @param {import('fastify').FastifyInstance} fastify
*/
export async function jobRoutes(fastify) {
// ------------------------------------------------------------------
// WebSocket — all clients connect here to receive live job updates
// ------------------------------------------------------------------
fastify.get('/ws', { websocket: true }, (socket) => {
registerClient(socket);
Job.findAll({ order: [['createdAt', 'DESC']], limit: 100 })
.then((jobs) => {
if (socket.readyState === 1) {
socket.send(JSON.stringify({ type: 'init', jobs: jobs.map((j) => j.toJSON()) }));
}
})
.catch(() => {});
});
// ------------------------------------------------------------------
// POST /api/jobs
// Multipart form fields: prompt (text), modelId (text), image (file)
// ------------------------------------------------------------------
fastify.post('/api/jobs', async (req, reply) => {
const data = await req.file();
if (!data) return reply.status(400).send({ error: 'No file uploaded' });
const fields = data.fields;
const prompt = (fields.prompt?.value ?? '').trim();
const modelId = (fields.modelId?.value ?? DEFAULT_MODEL_ID).trim();
if (!prompt) return reply.status(400).send({ error: 'prompt is required' });
// Validate that the submitted model ID is in our allowed list
const modelMeta = findModel(modelId);
if (!modelMeta) {
return reply.status(400).send({ error: `Unknown model: ${modelId}` });
}
const chunks = [];
for await (const chunk of data.file) chunks.push(chunk);
const buffer = Buffer.concat(chunks);
const mimeType = data.mimetype || 'image/jpeg';
const imageDataUrl = `data:${mimeType};base64,${buffer.toString('base64')}`;
const job = await Job.create({
prompt,
imageDataUrl,
imageMimeType: mimeType,
model: modelId,
});
enqueueJob(job.id);
return reply.status(201).send(job.toJSON());
});
// ------------------------------------------------------------------
// GET /api/jobs
// ------------------------------------------------------------------
fastify.get('/api/jobs', async (_req, reply) => {
const jobs = await Job.findAll({ order: [['createdAt', 'DESC']], limit: 100 });
return reply.send(jobs.map((j) => j.toJSON()));
});
// ------------------------------------------------------------------
// GET /api/jobs/:id
// ------------------------------------------------------------------
fastify.get('/api/jobs/:id', async (req, reply) => {
const job = await Job.findByPk(req.params.id);
if (!job) return reply.status(404).send({ error: 'Not found' });
return reply.send(job.toJSON());
});
}