First cut
This commit is contained in:
78
routes/jobs.js
Normal file
78
routes/jobs.js
Normal file
@@ -0,0 +1,78 @@
|
||||
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());
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user