AdminLTE3
This commit is contained in:
1634
.history/routes/admin_20251026212605.js
Normal file
1634
.history/routes/admin_20251026212605.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212607.js
Normal file
1634
.history/routes/admin_20251026212607.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212617.js
Normal file
1634
.history/routes/admin_20251026212617.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212628.js
Normal file
1634
.history/routes/admin_20251026212628.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212638.js
Normal file
1634
.history/routes/admin_20251026212638.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212647.js
Normal file
1634
.history/routes/admin_20251026212647.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212658.js
Normal file
1634
.history/routes/admin_20251026212658.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212708.js
Normal file
1634
.history/routes/admin_20251026212708.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212718.js
Normal file
1634
.history/routes/admin_20251026212718.js
Normal file
File diff suppressed because it is too large
Load Diff
1634
.history/routes/admin_20251026212756.js
Normal file
1634
.history/routes/admin_20251026212756.js
Normal file
File diff suppressed because it is too large
Load Diff
1652
.history/routes/admin_20251026215040.js
Normal file
1652
.history/routes/admin_20251026215040.js
Normal file
File diff suppressed because it is too large
Load Diff
1652
.history/routes/admin_20251026215051.js
Normal file
1652
.history/routes/admin_20251026215051.js
Normal file
File diff suppressed because it is too large
Load Diff
1653
.history/routes/admin_20251026220319.js
Normal file
1653
.history/routes/admin_20251026220319.js
Normal file
File diff suppressed because it is too large
Load Diff
1654
.history/routes/admin_20251026220340.js
Normal file
1654
.history/routes/admin_20251026220340.js
Normal file
File diff suppressed because it is too large
Load Diff
1655
.history/routes/admin_20251026220351.js
Normal file
1655
.history/routes/admin_20251026220351.js
Normal file
File diff suppressed because it is too large
Load Diff
1656
.history/routes/admin_20251026220402.js
Normal file
1656
.history/routes/admin_20251026220402.js
Normal file
File diff suppressed because it is too large
Load Diff
1657
.history/routes/admin_20251026220414.js
Normal file
1657
.history/routes/admin_20251026220414.js
Normal file
File diff suppressed because it is too large
Load Diff
1658
.history/routes/admin_20251026220429.js
Normal file
1658
.history/routes/admin_20251026220429.js
Normal file
File diff suppressed because it is too large
Load Diff
1659
.history/routes/admin_20251026220439.js
Normal file
1659
.history/routes/admin_20251026220439.js
Normal file
File diff suppressed because it is too large
Load Diff
1659
.history/routes/admin_20251026220452.js
Normal file
1659
.history/routes/admin_20251026220452.js
Normal file
File diff suppressed because it is too large
Load Diff
496
.history/routes/api/admin_20251026221106.js
Normal file
496
.history/routes/api/admin_20251026221106.js
Normal file
@@ -0,0 +1,496 @@
|
||||
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;
|
||||
496
.history/routes/api/admin_20251026221118.js
Normal file
496
.history/routes/api/admin_20251026221118.js
Normal file
@@ -0,0 +1,496 @@
|
||||
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;
|
||||
77
.history/routes/demo-adminlte_20251026211505.js
Normal file
77
.history/routes/demo-adminlte_20251026211505.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// Демо-роут для AdminLTE админки
|
||||
router.get('/demo-adminlte', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-adminlte', {
|
||||
layout: 'admin/layout-adminlte',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AdminLTE Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
77
.history/routes/demo-adminlte_20251026211507.js
Normal file
77
.history/routes/demo-adminlte_20251026211507.js
Normal file
@@ -0,0 +1,77 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// Демо-роут для AdminLTE админки
|
||||
router.get('/demo-adminlte', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-adminlte', {
|
||||
layout: 'admin/layout-adminlte',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AdminLTE Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
150
.history/routes/demo-adminlte_20251026211708.js
Normal file
150
.history/routes/demo-adminlte_20251026211708.js
Normal file
@@ -0,0 +1,150 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// Демо-роут для AdminLTE админки
|
||||
router.get('/demo-adminlte', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-adminlte', {
|
||||
layout: 'admin/layout-adminlte',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AdminLTE Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
// Демо-роут для Tabler админки
|
||||
router.get('/demo-tabler', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-tabler', {
|
||||
layout: 'admin/layout-tabler',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Tabler Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
150
.history/routes/demo-adminlte_20251026211716.js
Normal file
150
.history/routes/demo-adminlte_20251026211716.js
Normal file
@@ -0,0 +1,150 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// Демо-роут для AdminLTE админки
|
||||
router.get('/demo-adminlte', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-adminlte', {
|
||||
layout: 'admin/layout-adminlte',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AdminLTE Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
// Демо-роут для Tabler админки
|
||||
router.get('/demo-tabler', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-tabler', {
|
||||
layout: 'admin/layout-tabler',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Tabler Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
162
.history/routes/demo-adminlte_20251026211846.js
Normal file
162
.history/routes/demo-adminlte_20251026211846.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// Главная страница сравнения admin bundles
|
||||
router.get('/admin-comparison', async (req, res) => {
|
||||
try {
|
||||
res.render('admin-bundle-comparison', {
|
||||
layout: false // Не используем layout для этой страницы
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Admin Comparison Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
// Демо-роут для AdminLTE админки
|
||||
router.get('/demo-adminlte', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-adminlte', {
|
||||
layout: 'admin/layout-adminlte',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AdminLTE Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
// Демо-роут для Tabler админки
|
||||
router.get('/demo-tabler', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-tabler', {
|
||||
layout: 'admin/layout-tabler',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Tabler Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
162
.history/routes/demo-adminlte_20251026211849.js
Normal file
162
.history/routes/demo-adminlte_20251026211849.js
Normal file
@@ -0,0 +1,162 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
|
||||
// Главная страница сравнения admin bundles
|
||||
router.get('/admin-comparison', async (req, res) => {
|
||||
try {
|
||||
res.render('admin-bundle-comparison', {
|
||||
layout: false // Не используем layout для этой страницы
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Admin Comparison Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
// Демо-роут для AdminLTE админки
|
||||
router.get('/demo-adminlte', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-adminlte', {
|
||||
layout: 'admin/layout-adminlte',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('AdminLTE Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
// Демо-роут для Tabler админки
|
||||
router.get('/demo-tabler', async (req, res) => {
|
||||
try {
|
||||
// Мок-данные для демонстрации
|
||||
const stats = {
|
||||
portfolioCount: 12,
|
||||
servicesCount: 6,
|
||||
contactsCount: 24,
|
||||
usersCount: 3
|
||||
};
|
||||
|
||||
const recentPortfolio = [
|
||||
{
|
||||
title: '삼성 모바일 앱 개발',
|
||||
category: 'mobile-app',
|
||||
status: 'completed',
|
||||
createdAt: new Date('2024-10-20')
|
||||
},
|
||||
{
|
||||
title: 'LG 웹사이트 리뉴얼',
|
||||
category: 'web-development',
|
||||
status: 'in-progress',
|
||||
createdAt: new Date('2024-10-18')
|
||||
},
|
||||
{
|
||||
title: '현대차 UI/UX 디자인',
|
||||
category: 'ui-ux-design',
|
||||
status: 'planning',
|
||||
createdAt: new Date('2024-10-15')
|
||||
}
|
||||
];
|
||||
|
||||
const recentContacts = [
|
||||
{
|
||||
name: '김철수',
|
||||
email: 'kim@example.com',
|
||||
status: 'pending',
|
||||
createdAt: new Date('2024-10-25')
|
||||
},
|
||||
{
|
||||
name: '이영희',
|
||||
email: 'lee@company.kr',
|
||||
status: 'replied',
|
||||
createdAt: new Date('2024-10-24')
|
||||
},
|
||||
{
|
||||
name: '박민수',
|
||||
email: 'park@startup.co.kr',
|
||||
status: 'new',
|
||||
createdAt: new Date('2024-10-23')
|
||||
}
|
||||
];
|
||||
|
||||
const user = {
|
||||
name: '관리자'
|
||||
};
|
||||
|
||||
res.render('admin/dashboard-tabler', {
|
||||
layout: 'admin/layout-tabler',
|
||||
title: '대시보드',
|
||||
currentPage: 'dashboard',
|
||||
currentLanguage: 'ko',
|
||||
stats,
|
||||
recentPortfolio,
|
||||
recentContacts,
|
||||
user
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Tabler Demo Error:', error);
|
||||
res.status(500).send('Server Error');
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user