Files
sst_site/.history/routes/api/admin_20251026221118.js
2025-10-26 22:14:47 +09:00

496 lines
13 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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;