Files
CreaBook/server/dist/api/covers.js
2026-04-05 03:11:25 +02:00

212 lines
8.7 KiB
JavaScript

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.coverRoutes = void 0;
const express_1 = require("express");
const multer_1 = __importDefault(require("multer"));
const path_1 = __importDefault(require("path"));
const fs_1 = __importDefault(require("fs"));
const sharp_1 = __importDefault(require("sharp"));
const axios_1 = __importDefault(require("axios"));
exports.coverRoutes = (0, express_1.Router)();
// Configure multer for file uploads
const storage = multer_1.default.diskStorage({
destination: (req, file, cb) => {
const uploadDir = path_1.default.join(process.cwd(), 'uploads', 'covers');
if (!fs_1.default.existsSync(uploadDir)) {
fs_1.default.mkdirSync(uploadDir, { recursive: true });
}
cb(null, uploadDir);
},
filename: (req, file, cb) => {
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1E9)}`;
cb(null, `cover-${uniqueSuffix}${path_1.default.extname(file.originalname)}`);
}
});
const upload = (0, multer_1.default)({
storage,
limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit
fileFilter: (req, file, cb) => {
const allowedTypes = ['image/jpeg', 'image/png', 'image/webp'];
if (allowedTypes.includes(file.mimetype)) {
cb(null, true);
}
else {
cb(new Error('Invalid file type. Only JPEG, PNG, and WEBP are allowed.'));
}
}
});
// Upload a cover image
exports.coverRoutes.post('/upload', upload.single('image'), (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
res.json({
id: path_1.default.basename(req.file.filename, path_1.default.extname(req.file.filename)),
filename: req.file.filename,
path: req.file.path,
url: `/uploads/covers/${req.file.filename}`,
size: req.file.size
});
}
catch (error) {
console.error('Upload error:', error);
res.status(500).json({
error: 'Failed to upload image',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Get all covers
exports.coverRoutes.get('/', (req, res) => {
try {
const coversDir = path_1.default.join(process.cwd(), 'uploads', 'covers');
if (!fs_1.default.existsSync(coversDir)) {
return res.json({ covers: [] });
}
const files = fs_1.default.readdirSync(coversDir)
.filter(file => /\.(jpg|jpeg|png|webp)$/i.test(file))
.map(file => ({
id: path_1.default.basename(file, path_1.default.extname(file)),
filename: file,
url: `/uploads/covers/${file}`,
createdAt: fs_1.default.statSync(path_1.default.join(coversDir, file)).mtime
}))
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
res.json({ covers: files });
}
catch (error) {
console.error('List covers error:', error);
res.status(500).json({
error: 'Failed to list covers',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Delete a cover
exports.coverRoutes.delete('/:id', (req, res) => {
try {
const coverPath = path_1.default.join(process.cwd(), 'uploads', 'covers', `${req.params.id}*`);
const coversDir = path_1.default.join(process.cwd(), 'uploads', 'covers');
if (!fs_1.default.existsSync(coversDir)) {
return res.status(404).json({ error: 'Cover not found' });
}
const files = fs_1.default.readdirSync(coversDir)
.filter(f => f.startsWith(`${req.params.id}`));
if (files.length === 0) {
return res.status(404).json({ error: 'Cover not found' });
}
files.forEach(file => {
fs_1.default.unlinkSync(path_1.default.join(coversDir, file));
});
res.json({ message: 'Cover deleted successfully' });
}
catch (error) {
console.error('Delete error:', error);
res.status(500).json({
error: 'Failed to delete cover',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Process cover image (resize, filter, etc.)
exports.coverRoutes.post('/process', upload.single('image'), async (req, res) => {
try {
if (!req.file) {
return res.status(400).json({ error: 'No file uploaded' });
}
const { width, height, filter, brightness, contrast } = req.body;
let pipeline = (0, sharp_1.default)(req.file.path);
// Resize if dimensions provided
if (width && height) {
pipeline = pipeline.resize(parseInt(width), parseInt(height), {
fit: 'cover'
});
}
// Apply filters
if (filter === 'grayscale') {
pipeline = pipeline.grayscale();
}
else if (filter === 'sepia') {
pipeline = pipeline.modulate({ saturation: 0, brightness: 1.1 });
}
// Adjust brightness and saturation (sharp doesn't have contrast)
if (brightness) {
pipeline = pipeline.modulate({ brightness: parseFloat(brightness) });
}
if (contrast) {
pipeline = pipeline.modulate({ saturation: parseFloat(contrast) });
}
const processedBuffer = await pipeline.toBuffer();
const outputPath = path_1.default.join(process.cwd(), 'uploads', 'covers', `processed-${req.file.filename}`);
await pipeline.toFile(outputPath);
res.json({
id: `processed-${path_1.default.basename(req.file.filename, path_1.default.extname(req.file.filename))}`,
filename: `processed-${req.file.filename}`,
path: outputPath,
url: `/uploads/covers/processed-${req.file.filename}`
});
}
catch (error) {
console.error('Process error:', error);
res.status(500).json({
error: 'Failed to process image',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Generate cover image using AI (Pollinations.ai - Free, no API key required)
exports.coverRoutes.post('/generate', async (req, res) => {
try {
const { prompt, genre, language } = req.body;
const targetLang = language && language.startsWith('es') ? 'es' : 'en';
if (!prompt) {
return res.status(400).json({ error: 'prompt is required' });
}
let enhancedPrompt = '';
if (targetLang === 'es') {
enhancedPrompt = genre
? `Portada de libro para una novela de ${genre}: ${prompt}, diseño profesional de portada de libro, alta calidad, ilustración detallada, 8k, obra maestra`
: `Portada de libro: ${prompt}, diseño profesional de portada de libro, alta calidad, ilustración detallada, 8k, obra maestra`;
}
else {
enhancedPrompt = genre
? `Book cover for a ${genre} novel: ${prompt}, professional book cover design, high quality, detailed illustration, 8k, masterpiece`
: `Book cover: ${prompt}, professional book cover design, high quality, detailed illustration, 8k, masterpiece`;
}
// Use Pollinations.ai free text-to-image API (no API key required)
const imageUrl = `https://image.pollinations.ai/prompt/${encodeURIComponent(enhancedPrompt)}?width=1024&height=1024&nologo=true&seed=${Date.now()}`;
// Fetch the generated image
const imageResponse = await axios_1.default.get(imageUrl, {
responseType: 'arraybuffer',
timeout: 30000
});
const buffer = Buffer.from(imageResponse.data);
const uploadsDir = path_1.default.join(process.cwd(), 'uploads', 'covers');
if (!fs_1.default.existsSync(uploadsDir)) {
fs_1.default.mkdirSync(uploadsDir, { recursive: true });
}
const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1E9)}`;
const filename = `generated-${uniqueSuffix}.png`;
const filePath = path_1.default.join(uploadsDir, filename);
fs_1.default.writeFileSync(filePath, buffer);
res.json({
id: `generated-${uniqueSuffix}`,
filename,
path: filePath,
url: `/uploads/covers/${filename}`,
generated: true
});
}
catch (error) {
console.error('Image generation error:', error);
res.status(500).json({
error: 'Failed to generate image',
details: error instanceof Error ? error.message : String(error)
});
}
});
//# sourceMappingURL=covers.js.map