first commit

This commit is contained in:
Ichitux
2026-04-05 03:08:53 +02:00
commit 1082d36c12
28015 changed files with 3767672 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
export declare const authRoutes: import("express-serve-static-core").Router;
//# sourceMappingURL=auth.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,UAAU,4CAAW,CAAC"}
+111
View File
@@ -0,0 +1,111 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.authRoutes = void 0;
const express_1 = require("express");
const bcryptjs_1 = __importDefault(require("bcryptjs"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const db_js_1 = require("../db.js");
exports.authRoutes = (0, express_1.Router)();
const JWT_SECRET = process.env.JWT_SECRET || 'super-secret-key';
exports.authRoutes.post('/register', async (req, res) => {
try {
const { email, password, name } = req.body;
if (!email || !password) {
return res.status(400).json({ error: 'Email and password are required' });
}
const existingUser = await db_js_1.prisma.user.findUnique({ where: { email } });
if (existingUser) {
return res.status(400).json({ error: 'Email already in use' });
}
const hashedPassword = await bcryptjs_1.default.hash(password, 10);
const user = await db_js_1.prisma.user.create({
data: {
email,
password: hashedPassword,
name: name || email.split('@')[0],
role: email === 'admin@admin.com' || email === 'admin' ? 'ADMIN' : 'USER',
},
});
const token = jsonwebtoken_1.default.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '7d' });
await db_js_1.prisma.session.create({
data: {
userId: user.id,
token,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 7 days
}
});
res.json({ token, user: { id: user.id, email: user.email, name: user.name, role: user.role } });
}
catch (error) {
console.error('Register error:', error);
res.status(500).json({ error: 'Failed to register', details: String(error) });
}
});
exports.authRoutes.post('/login', async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.status(400).json({ error: 'Email and password are required' });
}
const user = await db_js_1.prisma.user.findUnique({ where: { email } });
if (!user) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const isValid = await bcryptjs_1.default.compare(password, user.password);
if (!isValid) {
return res.status(401).json({ error: 'Invalid credentials' });
}
const token = jsonwebtoken_1.default.sign({ userId: user.id }, JWT_SECRET, { expiresIn: '7d' });
await db_js_1.prisma.session.create({
data: {
userId: user.id,
token,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
}
});
res.json({ token, user: { id: user.id, email: user.email, name: user.name, role: user.role } });
}
catch (error) {
console.error('Login error:', error);
res.status(500).json({ error: 'Failed to login', details: String(error) });
}
});
exports.authRoutes.get('/me', async (req, res) => {
try {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'No token provided' });
}
const token = authHeader.substring(7);
const decoded = jsonwebtoken_1.default.verify(token, JWT_SECRET);
const session = await db_js_1.prisma.session.findUnique({ where: { token } });
if (!session || session.expiresAt < new Date()) {
return res.status(401).json({ error: 'Session expired' });
}
const user = await db_js_1.prisma.user.findUnique({ where: { id: decoded.userId } });
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json({ user: { id: user.id, email: user.email, name: user.name, role: user.role } });
}
catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
});
exports.authRoutes.post('/logout', async (req, res) => {
try {
const authHeader = req.headers.authorization;
if (authHeader?.startsWith('Bearer ')) {
const token = authHeader.substring(7);
await db_js_1.prisma.session.deleteMany({ where: { token } });
}
res.json({ message: 'Logged out successfully' });
}
catch (error) {
res.status(500).json({ error: 'Failed to logout' });
}
});
//# sourceMappingURL=auth.js.map
+1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
export declare const bookRoutes: import("express-serve-static-core").Router;
//# sourceMappingURL=books.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"books.d.ts","sourceRoot":"","sources":["../../src/api/books.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,UAAU,4CAAW,CAAC"}
+295
View File
@@ -0,0 +1,295 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.bookRoutes = void 0;
const express_1 = require("express");
const axios_1 = __importDefault(require("axios"));
const genreTemplates_js_1 = require("../prompts/genreTemplates.js");
exports.bookRoutes = (0, express_1.Router)();
// Get all available genre templates
exports.bookRoutes.get('/genres', (req, res) => {
const genres = Object.entries(genreTemplates_js_1.genreTemplates).map(([key, template]) => ({
id: key,
name: template.name,
description: template.description,
icon: template.icon
}));
res.json({ genres });
});
// Get a specific genre template
exports.bookRoutes.get('/genres/:genreId', (req, res) => {
const { genreId } = req.params;
const template = genreTemplates_js_1.genreTemplates[genreId];
if (!template) {
return res.status(404).json({ error: 'Genre not found' });
}
res.json({ template });
});
// Generate book outline based on genre and idea
exports.bookRoutes.post('/outline', async (req, res) => {
try {
const { genre, idea, title } = req.body;
if (!genre || !idea) {
return res.status(400).json({ error: 'genre and idea are required' });
}
const template = genreTemplates_js_1.genreTemplates[genre];
if (!template) {
return res.status(400).json({ error: 'Invalid genre' });
}
const prompt = `${template.prompts.outline}
Book Title: ${title || 'Untitled'}
Core Idea: ${idea}
Generate a detailed chapter outline following the structure: ${template.structure.join(' → ')}
Return the response in JSON format:
{
"title": "Book Title",
"genre": "${genre}",
"logline": "One sentence summary",
"chapters": [
{"number": 1, "title": "Chapter Title", "summary": "Brief description"},
...
]
}`;
const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', {
model: 'nvidia/nemotron-3-nano-30b-a3b:free', // Free model on OpenRouter
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
max_tokens: 2000
}, {
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://creabook.app',
'X-Title': 'CreaBook'
}
});
const content = response.data.choices[0].message.content;
console.log('=== Outline AI Response ===');
console.log('Raw content:', content);
// Try to parse JSON from response
let outline;
const jsonMatch = content.match(/\{[\s\S]*\}/);
if (jsonMatch) {
try {
outline = JSON.parse(jsonMatch[0]);
console.log('Parsed outline:', outline);
}
catch (parseError) {
console.error('JSON parse error:', parseError);
outline = { raw: content, error: 'Failed to parse JSON' };
}
}
else {
console.error('No JSON object found in response');
outline = { raw: content, error: 'No JSON found' };
}
res.json({ outline });
}
catch (error) {
console.error('Outline generation error:', error);
res.status(500).json({
error: 'Failed to generate outline',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Generate a chapter based on outline
exports.bookRoutes.post('/chapter', async (req, res) => {
try {
const { genre, chapterTitle, chapterSummary, previousContent } = req.body;
if (!genre || !chapterTitle || !chapterSummary) {
return res.status(400).json({ error: 'genre, chapterTitle, and chapterSummary are required' });
}
const template = genreTemplates_js_1.genreTemplates[genre];
let prompt = `${template.prompts.chapter}
Chapter: ${chapterTitle}
Summary: ${chapterSummary}
Tone: ${template.defaults.tone}
POV: ${template.defaults.pov}
`;
if (previousContent) {
prompt += `\n\nPrevious content for context:\n${previousContent.substring(0, 2000)}...`;
}
const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', {
model: 'nvidia/nemotron-3-nano-30b-a3b:free',
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
max_tokens: 3000
}, {
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://creabook.app',
'X-Title': 'CreaBook'
}
});
const content = response.data.choices[0].message.content;
res.json({
content,
chapterTitle
});
}
catch (error) {
console.error('Chapter generation error:', error);
res.status(500).json({
error: 'Failed to generate chapter',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Expand or refine text
exports.bookRoutes.post('/expand', async (req, res) => {
try {
const { text, instruction = 'Expand and improve this text' } = req.body;
if (!text) {
return res.status(400).json({ error: 'text is required' });
}
const prompt = `${instruction}:
Original text:
${text}
Provide an expanded and improved version:`;
const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', {
model: 'nvidia/nemotron-3-nano-30b-a3b:free',
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
max_tokens: 2000
}, {
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://creabook.app',
'X-Title': 'CreaBook'
}
});
const content = response.data.choices[0].message.content;
res.json({ expanded: content });
}
catch (error) {
console.error('Expand error:', error);
res.status(500).json({
error: 'Failed to expand text',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Generate character suggestions
exports.bookRoutes.post('/characters', async (req, res) => {
try {
const { genre, storyIdea } = req.body;
if (!genre || !storyIdea) {
return res.status(400).json({ error: 'genre and storyIdea are required' });
}
const template = genreTemplates_js_1.genreTemplates[genre];
const prompt = `Based on this ${genre} story idea, suggest 3-5 main characters.
Story Idea: ${storyIdea}
Genre conventions: ${template.description}
Return ONLY a valid JSON array with this exact format:
[
{
"name": "Character Name",
"role": "protagonist|antagonist|supporting",
"traits": ["trait1", "trait2"],
"motivation": "What drives them",
"backstory": "Brief backstory"
}
]
Do not include any text outside the JSON array.`;
console.log('=== Character Generation Prompt ===');
console.log(prompt);
const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', {
model: 'nvidia/nemotron-3-nano-30b-a3b:free',
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
max_tokens: 1500
}, {
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://creabook.app',
'X-Title': 'CreaBook'
}
});
const content = response.data.choices[0].message.content;
console.log('=== Character AI Response ===');
console.log('Raw content:', content);
let characters;
const jsonMatch = content.match(/\[[\s\S]*\]/);
if (jsonMatch) {
try {
characters = JSON.parse(jsonMatch[0]);
console.log('Parsed characters:', characters);
}
catch (parseError) {
console.error('JSON parse error:', parseError);
characters = { raw: content, error: 'Failed to parse JSON' };
}
}
else {
console.error('No JSON array found in response');
characters = { raw: content, error: 'No JSON array found' };
}
res.json({ characters });
}
catch (error) {
console.error('Character generation error:', error);
res.status(500).json({
error: 'Failed to generate characters',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Generate plot suggestions
exports.bookRoutes.post('/plot', async (req, res) => {
try {
const { genre, currentPlot, issue } = req.body;
if (!genre || !currentPlot) {
return res.status(400).json({ error: 'genre and currentPlot are required' });
}
const template = genreTemplates_js_1.genreTemplates[genre];
const prompt = `Help develop the plot for this ${genre} story:
Current Plot:
${currentPlot}
${issue ? `Specific Issue: ${issue}` : 'Suggest plot developments and twists.'}
Consider genre conventions: ${template.description}
Structure: ${template.structure.join(' → ')}
Provide specific, actionable plot suggestions:`;
const response = await axios_1.default.post('https://openrouter.ai/api/v1/chat/completions', {
model: 'nvidia/nemotron-3-nano-30b-a3b:free',
messages: [{ role: 'user', content: prompt }],
temperature: 0.7,
max_tokens: 1500
}, {
headers: {
'Authorization': `Bearer ${process.env.OPENROUTER_API_KEY}`,
'Content-Type': 'application/json',
'HTTP-Referer': 'https://creabook.app',
'X-Title': 'CreaBook'
}
});
const content = response.data.choices[0].message.content;
res.json({ suggestions: content });
}
catch (error) {
console.error('Plot suggestion error:', error);
res.status(500).json({
error: 'Failed to generate plot suggestions',
details: error instanceof Error ? error.message : String(error)
});
}
});
//# sourceMappingURL=books.js.map
+1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
export declare const coverRoutes: import("express-serve-static-core").Router;
//# sourceMappingURL=covers.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"covers.d.ts","sourceRoot":"","sources":["../../src/api/covers.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,WAAW,4CAAW,CAAC"}
+203
View File
@@ -0,0 +1,203 @@
"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 } = req.body;
if (!prompt) {
return res.status(400).json({ error: 'prompt is required' });
}
const 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
+1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -0,0 +1,2 @@
export declare const ollamaRoutes: import("express-serve-static-core").Router;
//# sourceMappingURL=ollama.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/api/ollama.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,4CAAW,CAAC"}
+105
View File
@@ -0,0 +1,105 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ollamaRoutes = void 0;
const express_1 = require("express");
const ollama_1 = __importDefault(require("ollama"));
exports.ollamaRoutes = (0, express_1.Router)();
// Generate text with Ollama
exports.ollamaRoutes.post('/generate', async (req, res) => {
try {
const { model, prompt, stream = false } = req.body;
if (!model || !prompt) {
return res.status(400).json({ error: 'model and prompt are required' });
}
const response = await ollama_1.default.chat({
model: model || 'llama3',
messages: [{ role: 'user', content: prompt }],
stream: false,
});
res.json({
content: response.message.content,
model: response.model
});
}
catch (error) {
console.error('Ollama generate error:', error);
res.status(500).json({
error: 'Failed to generate content',
details: error instanceof Error ? error.message : String(error)
});
}
});
// Stream text generation (SSE)
exports.ollamaRoutes.post('/stream', async (req, res) => {
try {
const { model, prompt } = req.body;
if (!model || !prompt) {
return res.status(400).json({ error: 'model and prompt are required' });
}
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const stream = await ollama_1.default.chat({
model: model || 'llama3',
messages: [{ role: 'user', content: prompt }],
stream: true,
});
for await (const chunk of stream) {
const content = chunk.message?.content || '';
if (content) {
res.write(`data: ${JSON.stringify({ content })}\n\n`);
}
}
res.write('data: [DONE]\n\n');
res.end();
}
catch (error) {
console.error('Ollama stream error:', error);
res.write(`data: ${JSON.stringify({ error: 'Streaming failed' })}\n\n`);
res.end();
}
});
// Generate image (if using a model that supports it)
exports.ollamaRoutes.post('/image', async (req, res) => {
try {
const { prompt, model = 'llama3.2-vision' } = req.body;
if (!prompt) {
return res.status(400).json({ error: 'prompt is required' });
}
// Note: Ollama's image generation depends on the model
// Available models: llama3.2-vision
const response = await ollama_1.default.generate({
model,
prompt,
});
res.json({
image: response.image,
model
});
}
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)
});
}
});
// List available models
exports.ollamaRoutes.get('/models', async (req, res) => {
try {
const response = await ollama_1.default.list();
res.json({ models: response.models });
}
catch (error) {
console.error('Model list error:', error);
res.status(500).json({
error: 'Failed to list models',
details: error instanceof Error ? error.message : String(error)
});
}
});
//# sourceMappingURL=ollama.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/api/ollama.ts"],"names":[],"mappings":";;;;;;AAAA,qCAAiC;AACjC,oDAA4B;AAEf,QAAA,YAAY,GAAG,IAAA,gBAAM,GAAE,CAAC;AAErC,4BAA4B;AAC5B,oBAAY,CAAC,IAAI,CAAC,WAAW,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAChD,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;QAEnD,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YACtB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,gBAAM,CAAC,IAAI,CAAC;YACjC,KAAK,EAAE,KAAK,IAAI,QAAQ;YACxB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC;YACP,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,OAAO;YACjC,KAAK,EAAE,QAAQ,CAAC,KAAK;SACtB,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC/C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,4BAA4B;YACnC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAChE,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,+BAA+B;AAC/B,oBAAY,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC9C,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;QAEnC,IAAI,CAAC,KAAK,IAAI,CAAC,MAAM,EAAE,CAAC;YACtB,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,+BAA+B,EAAE,CAAC,CAAC;QAC1E,CAAC;QAED,GAAG,CAAC,SAAS,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;QACnD,GAAG,CAAC,SAAS,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;QAC3C,GAAG,CAAC,SAAS,CAAC,YAAY,EAAE,YAAY,CAAC,CAAC;QAE1C,MAAM,MAAM,GAAG,MAAM,gBAAM,CAAC,IAAI,CAAC;YAC/B,KAAK,EAAE,KAAK,IAAI,QAAQ;YACxB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,MAAM,EAAE,IAAI;SACb,CAAC,CAAC;QAEH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YACjC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,OAAO,IAAI,EAAE,CAAC;YAC7C,IAAI,OAAO,EAAE,CAAC;gBACZ,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;QAED,GAAG,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;QAC9B,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,KAAK,CAAC,CAAC;QAC7C,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC,MAAM,CAAC,CAAC;QACxE,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,qDAAqD;AACrD,oBAAY,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC7C,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,KAAK,GAAG,iBAAiB,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;QAEvD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC,CAAC;QAC/D,CAAC;QAED,uDAAuD;QACvD,oCAAoC;QACpC,MAAM,QAAQ,GAAQ,MAAM,gBAAM,CAAC,QAAQ,CAAC;YAC1C,KAAK;YACL,MAAM;SACP,CAAC,CAAC;QAEH,GAAG,CAAC,IAAI,CAAC;YACP,KAAK,EAAG,QAAgB,CAAC,KAAK;YAC9B,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,yBAAyB,EAAE,KAAK,CAAC,CAAC;QAChD,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,0BAA0B;YACjC,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAChE,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,oBAAY,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;IAC7C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,gBAAM,CAAC,IAAI,EAAE,CAAC;QACrC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IACxC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;QAC1C,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;YACnB,KAAK,EAAE,uBAAuB;YAC9B,OAAO,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;SAChE,CAAC,CAAC;IACL,CAAC;AACH,CAAC,CAAC,CAAC"}