Z-Image-Turbo-API / server.js
mohamedislegend4's picture
Upload 9 files
d6e90da verified
Raw
History Blame Contribute Delete
6.71 kB
/**
* Z-Image-Turbo API Wrapper - Node.js Express Version
*
* This server wraps the Gradio API and provides a simple REST endpoint
* that returns the image URL directly
*/
const express = require('express');
const cors = require('cors');
const axios = require('axios');
const bodyParser = require('body-parser');
const app = express();
const PORT = process.env.PORT || 5000;
const GRADIO_API_URL = 'https://mohamedislegend4-z-image-turbo-api.hf.space';
const MAX_POLL_ATTEMPTS = 120;
const POLL_INTERVAL = 1000; // ms
// Middleware
app.use(cors());
app.use(bodyParser.json());
// Logger
const log = (level, message, data = '') => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${level}: ${message} ${data}`);
};
/**
* Call a Gradio endpoint
*/
async function callGradioEndpoint(endpoint, params) {
const url = `${GRADIO_API_URL}/gradio_api/call/v2/${endpoint}`;
log('INFO', `Calling endpoint: ${endpoint}`, JSON.stringify(params));
try {
const response = await axios.post(url, params, {
headers: { 'Content-Type': 'application/json' },
timeout: 30000
});
log('INFO', 'Response received', JSON.stringify(response.data));
return response.data;
} catch (error) {
log('ERROR', `Failed to call endpoint: ${error.message}`);
throw error;
}
}
/**
* Poll for result
*/
async function pollResult(endpoint, eventId) {
const url = `${GRADIO_API_URL}/gradio_api/call/${endpoint}/${eventId}`;
log('INFO', `Polling result: ${endpoint}/${eventId}`);
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt++) {
try {
const response = await axios.get(url, {
timeout: 30000
});
const data = response.data;
// Check if result is ready
if (data.result && data.result !== null) {
log('INFO', `Got result on attempt ${attempt + 1}`);
return data.result;
}
log('INFO', `Attempt ${attempt + 1}: No result yet, waiting...`);
// Wait before next poll
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
} catch (error) {
log('WARN', `Poll request failed: ${error.message}`);
await new Promise(resolve => setTimeout(resolve, POLL_INTERVAL));
}
}
log('ERROR', `Polling timeout after ${MAX_POLL_ATTEMPTS} attempts`);
return null;
}
/**
* Generate image using Z-Image-Turbo
*/
async function generateImage(prompt, steps = 20, height = 512, width = 512) {
try {
const params = {
prompt,
steps: Math.min(Math.max(steps, 1), 50),
height,
width
};
// Call the endpoint
const callResponse = await callGradioEndpoint('generate', params);
const eventId = callResponse.event_id;
if (!eventId) {
throw new Error('No event_id in response');
}
// Poll for result
const result = await pollResult('generate', eventId);
if (!result) {
throw new Error('Failed to get result from API');
}
// Extract image data
if (Array.isArray(result) && result.length > 0) {
return result[0];
}
return result;
} catch (error) {
log('ERROR', `Error generating image: ${error.message}`);
throw error;
}
}
/**
* Routes
*/
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
// API Info
app.get('/api/info', (req, res) => {
res.json({
api_name: 'Z-Image-Turbo API Wrapper',
version: '1.0.0',
language: 'Node.js',
endpoints: [
{
path: '/api/generate',
method: 'POST',
description: 'Generate an image from a text prompt',
parameters: {
prompt: {
type: 'string',
required: true,
description: 'Text description of the image'
},
steps: {
type: 'integer',
required: false,
default: 20,
min: 1,
max: 50,
description: 'Number of inference steps'
},
height: {
type: 'integer',
required: false,
default: 512,
description: 'Image height in pixels'
},
width: {
type: 'integer',
required: false,
default: 512,
description: 'Image width in pixels'
}
}
}
]
});
});
// Generate image endpoint
app.post('/api/generate', async (req, res) => {
try {
const { prompt, steps = 20, height = 512, width = 512 } = req.body;
// Validate input
if (!prompt) {
return res.status(400).json({
success: false,
error: 'Missing required parameter: prompt'
});
}
log('INFO', `Generating image for: ${prompt}`);
// Generate image
const imageData = await generateImage(prompt, steps, height, width);
if (!imageData) {
return res.status(500).json({
success: false,
error: 'Failed to generate image'
});
}
// Build response
const result = {
success: true,
prompt,
steps: Math.min(Math.max(steps, 1), 50),
height,
width
};
// Add image data fields
if (typeof imageData === 'object') {
if (imageData.url) result.image_url = imageData.url;
if (imageData.path) result.image_path = imageData.path;
if (imageData.size) result.size = imageData.size;
if (imageData.mime_type) result.mime_type = imageData.mime_type;
if (imageData.orig_name) result.filename = imageData.orig_name;
}
res.json(result);
} catch (error) {
log('ERROR', `Error in /api/generate: ${error.message}`);
res.status(500).json({
success: false,
error: error.message
});
}
});
// 404 handler
app.use((req, res) => {
res.status(404).json({ error: 'Not found' });
});
// Error handler
app.use((err, req, res, next) => {
log('ERROR', `Unhandled error: ${err.message}`);
res.status(500).json({
error: 'Internal server error',
message: process.env.NODE_ENV === 'development' ? err.message : undefined
});
});
// Start server
app.listen(PORT, '0.0.0.0', () => {
log('INFO', '='.repeat(60));
log('INFO', 'Z-Image-Turbo API Wrapper (Node.js)');
log('INFO', '='.repeat(60));
log('INFO', `Gradio API URL: ${GRADIO_API_URL}`);
log('INFO', `Server running on: http://localhost:${PORT}`);
log('INFO', `Health check: http://localhost:${PORT}/health`);
log('INFO', `API info: http://localhost:${PORT}/api/info`);
log('INFO', `Generate endpoint: POST http://localhost:${PORT}/api/generate`);
log('INFO', '='.repeat(60));
});
module.exports = app;