const express = require('express'); const router = express.Router(); const multer = require('multer'); const path = require('path'); const { Portfolio, Service, Contact, User } = require('../../models'); // Multer configuration for file uploads const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, 'public/uploads/'); }, filename: (req, file, cb) => { const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); cb(null, file.fieldname + '-' + uniqueSuffix + path.extname(file.originalname)); } }); const upload = multer({ storage: storage, fileFilter: (req, file, cb) => { if (file.mimetype.startsWith('image/')) { cb(null, true); } else { cb(new Error('Only image files are allowed!'), false); } }, limits: { fileSize: 10 * 1024 * 1024 // 10MB } }); // Authentication middleware const requireAuth = (req, res, next) => { if (!req.session.user) { return res.status(401).json({ success: false, message: 'Authentication required' }); } next(); }; // Portfolio API Routes router.post('/portfolio', requireAuth, upload.array('images', 10), async (req, res) => { try { const { title, shortDescription, description, category, clientName, projectUrl, githubUrl, technologies, featured, isPublished } = req.body; // Process uploaded images const images = req.files ? req.files.map((file, index) => ({ url: `/uploads/${file.filename}`, alt: `${title} image ${index + 1}`, isPrimary: index === 0 })) : []; // Parse technologies let techArray = []; if (technologies) { try { techArray = JSON.parse(technologies); } catch (e) { techArray = technologies.split(',').map(t => t.trim()); } } const portfolio = await Portfolio.create({ title, shortDescription, description, category, clientName, projectUrl: projectUrl || null, githubUrl: githubUrl || null, technologies: techArray, images, featured: featured === 'on', isPublished: isPublished === 'on', status: 'completed', publishedAt: isPublished === 'on' ? new Date() : null }); res.json({ success: true, portfolio }); } catch (error) { console.error('Portfolio creation error:', error); res.status(500).json({ success: false, message: error.message }); } }); router.patch('/portfolio/:id', requireAuth, upload.array('images', 10), async (req, res) => { try { const portfolio = await Portfolio.findByPk(req.params.id); if (!portfolio) { return res.status(404).json({ success: false, message: 'Portfolio not found' }); } const updates = { ...req.body }; // Handle checkboxes updates.featured = updates.featured === 'on'; updates.isPublished = updates.isPublished === 'on'; // Process technologies if (updates.technologies) { try { updates.technologies = JSON.parse(updates.technologies); } catch (e) { updates.technologies = updates.technologies.split(',').map(t => t.trim()); } } // Process new images if (req.files && req.files.length > 0) { const newImages = req.files.map((file, index) => ({ url: `/uploads/${file.filename}`, alt: `${updates.title || portfolio.title} image ${index + 1}`, isPrimary: index === 0 && (!portfolio.images || portfolio.images.length === 0) })); updates.images = [...(portfolio.images || []), ...newImages]; } await portfolio.update(updates); res.json({ success: true, portfolio }); } catch (error) { console.error('Portfolio update error:', error); res.status(500).json({ success: false, message: error.message }); } }); router.delete('/portfolio/:id', requireAuth, async (req, res) => { try { const portfolio = await Portfolio.findByPk(req.params.id); if (!portfolio) { return res.status(404).json({ success: false, message: 'Portfolio not found' }); } await portfolio.destroy(); res.json({ success: true }); } catch (error) { console.error('Portfolio deletion error:', error); res.status(500).json({ success: false, message: error.message }); } }); // Services API Routes router.post('/services', requireAuth, async (req, res) => { try { const { name, description, shortDescription, icon, category, features, pricing, estimatedTime, isActive, featured, tags } = req.body; // Parse arrays let featuresArray = []; let tagsArray = []; let pricingObj = {}; if (features) { try { featuresArray = JSON.parse(features); } catch (e) { featuresArray = features.split(',').map(f => f.trim()); } } if (tags) { try { tagsArray = JSON.parse(tags); } catch (e) { tagsArray = tags.split(',').map(t => t.trim()); } } if (pricing) { try { pricingObj = JSON.parse(pricing); } catch (e) { pricingObj = { basePrice: pricing }; } } const service = await Service.create({ name, description, shortDescription, icon, category, features: featuresArray, pricing: pricingObj, estimatedTime, isActive: isActive === 'on', featured: featured === 'on', tags: tagsArray }); res.json({ success: true, service }); } catch (error) { console.error('Service creation error:', error); res.status(500).json({ success: false, message: error.message }); } }); router.put('/services/:id', requireAuth, async (req, res) => { try { const service = await Service.findByPk(req.params.id); if (!service) { return res.status(404).json({ success: false, message: 'Service not found' }); } const { name, description, shortDescription, icon, category, features, pricing, estimatedTime, isActive, featured, tags, order, seo } = req.body; // Parse arrays and objects let featuresArray = []; let tagsArray = []; let pricingObj = {}; let seoObj = {}; if (features) { if (Array.isArray(features)) { featuresArray = features; } else { try { featuresArray = JSON.parse(features); } catch (e) { featuresArray = features.split(',').map(f => f.trim()); } } } if (tags) { if (Array.isArray(tags)) { tagsArray = tags; } else { try { tagsArray = JSON.parse(tags); } catch (e) { tagsArray = tags.split(',').map(t => t.trim()); } } } if (pricing) { if (typeof pricing === 'object') { pricingObj = pricing; } else { try { pricingObj = JSON.parse(pricing); } catch (e) { pricingObj = { basePrice: pricing }; } } } if (seo) { if (typeof seo === 'object') { seoObj = seo; } else { try { seoObj = JSON.parse(seo); } catch (e) { seoObj = {}; } } } const updateData = { name, description, shortDescription, icon, category, features: featuresArray, pricing: pricingObj, estimatedTime, isActive: Boolean(isActive), featured: Boolean(featured), tags: tagsArray, order: order ? parseInt(order) : 0, seo: seoObj }; // Remove undefined values Object.keys(updateData).forEach(key => { if (updateData[key] === undefined) { delete updateData[key]; } }); await service.update(updateData); res.json({ success: true, service }); } catch (error) { console.error('Service update error:', error); res.status(500).json({ success: false, message: error.message }); } }); router.delete('/services/:id', requireAuth, async (req, res) => { try { const service = await Service.findByPk(req.params.id); if (!service) { return res.status(404).json({ success: false, message: 'Service not found' }); } await service.destroy(); res.json({ success: true }); } catch (error) { console.error('Service deletion error:', error); res.status(500).json({ success: false, message: error.message }); } }); // Contacts API Routes router.patch('/contacts/:id', requireAuth, async (req, res) => { try { const contact = await Contact.findByPk(req.params.id); if (!contact) { return res.status(404).json({ success: false, message: 'Contact not found' }); } await contact.update(req.body); res.json({ success: true, contact }); } catch (error) { console.error('Contact update error:', error); res.status(500).json({ success: false, message: error.message }); } }); router.delete('/contacts/:id', requireAuth, async (req, res) => { try { const contact = await Contact.findByPk(req.params.id); if (!contact) { return res.status(404).json({ success: false, message: 'Contact not found' }); } await contact.destroy(); res.json({ success: true }); } catch (error) { console.error('Contact deletion error:', error); res.status(500).json({ success: false, message: error.message }); } }); // Telegram notification for contact router.post('/contacts/:id/telegram', requireAuth, async (req, res) => { try { const contact = await Contact.findByPk(req.params.id); if (!contact) { return res.status(404).json({ success: false, message: 'Contact not found' }); } // Send Telegram notification const telegramService = require('../../services/telegram'); const result = await telegramService.sendContactNotification(contact); if (result.success) { res.json({ success: true }); } else { res.status(500).json({ success: false, message: result.message || result.error }); } } catch (error) { console.error('Telegram notification error:', error); res.status(500).json({ success: false, message: error.message }); } }); // Test Telegram connection router.post('/telegram/test', requireAuth, async (req, res) => { try { const { botToken, chatId } = req.body; // Temporarily set up telegram service with provided credentials const axios = require('axios'); // Test bot info const botResponse = await axios.get(`https://api.telegram.org/bot${botToken}/getMe`); // Test sending a message const testMessage = '✅ Telegram bot подключен успешно!\n\nЭто тестовое сообщение от SmartSolTech Admin Panel.'; await axios.post(`https://api.telegram.org/bot${botToken}/sendMessage`, { chat_id: chatId, text: testMessage, parse_mode: 'Markdown' }); res.json({ success: true, bot: botResponse.data.result, message: 'Test message sent successfully' }); } catch (error) { console.error('Telegram test error:', error); let message = 'Connection failed'; if (error.response?.data?.description) { message = error.response.data.description; } else if (error.message) { message = error.message; } res.status(400).json({ success: false, message }); } }); // Settings API const { SiteSettings } = require('../../models'); router.get('/settings', requireAuth, async (req, res) => { try { const settings = await SiteSettings.findOne() || {}; res.json({ success: true, settings }); } catch (error) { console.error('Settings fetch error:', error); res.status(500).json({ success: false, message: error.message }); } }); router.post('/settings', requireAuth, upload.fields([ { name: 'logo', maxCount: 1 }, { name: 'favicon', maxCount: 1 } ]), async (req, res) => { try { let settings = await SiteSettings.findOne(); if (!settings) { settings = await SiteSettings.create({}); } const updates = {}; // Handle nested objects Object.keys(req.body).forEach(key => { if (key.includes('.')) { const [parent, child] = key.split('.'); if (!updates[parent]) updates[parent] = {}; updates[parent][child] = req.body[key]; } else { updates[key] = req.body[key]; } }); // Handle file uploads if (req.files.logo) { updates.logo = `/uploads/${req.files.logo[0].filename}`; } if (req.files.favicon) { updates.favicon = `/uploads/${req.files.favicon[0].filename}`; } // Update existing settings with new values Object.keys(updates).forEach(key => { if (typeof updates[key] === 'object' && updates[key] !== null) { settings[key] = { ...settings[key], ...updates[key] }; } else { settings[key] = updates[key]; } }); await settings.save(); res.json({ success: true, settings }); } catch (error) { console.error('Settings update error:', error); res.status(500).json({ success: false, message: error.message }); } }); module.exports = router;