feat: Реализован полный CRUD для админ-панели и улучшена функциональность

- Portfolio CRUD: добавление, редактирование, удаление, переключение публикации
- Services CRUD: полное управление услугами с возможностью активации/деактивации
- Banner system: новая модель Banner с CRUD операциями и аналитикой кликов
- Telegram integration: расширенные настройки бота, обнаружение чатов, отправка сообщений
- Media management: улучшенная загрузка файлов с оптимизацией изображений и превью
- UI improvements: обновлённые админ-панели с rich-text редактором и drag&drop загрузкой
- Database: добавлена таблица banners с полями для баннеров и аналитики
This commit is contained in:
2025-10-22 20:32:16 +09:00
parent 150891b29d
commit 9477ff6de0
69 changed files with 11451 additions and 2321 deletions

View File

@@ -1,20 +1,23 @@
const express = require('express');
const router = express.Router();
const Portfolio = require('../models/Portfolio');
const Service = require('../models/Service');
const SiteSettings = require('../models/SiteSettings');
const { Portfolio, Service, SiteSettings } = require('../models');
const { Op } = require('sequelize');
// Home page
router.get('/', async (req, res) => {
try {
const [settings, featuredPortfolio, featuredServices] = await Promise.all([
SiteSettings.findOne() || {},
Portfolio.find({ featured: true, isPublished: true })
.sort({ order: 1, createdAt: -1 })
.limit(6),
Service.find({ featured: true, isActive: true })
.sort({ order: 1 })
.limit(4)
Portfolio.findAll({
where: { featured: true, isPublished: true },
order: [['order', 'ASC'], ['createdAt', 'DESC']],
limit: 6
}),
Service.findAll({
where: { featured: true, isActive: true },
order: [['order', 'ASC']],
limit: 4
})
]);
res.render('index', {
@@ -28,6 +31,7 @@ router.get('/', async (req, res) => {
console.error('Home page error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}
@@ -47,6 +51,7 @@ router.get('/about', async (req, res) => {
console.error('About page error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}
@@ -65,20 +70,28 @@ router.get('/portfolio', async (req, res) => {
query.category = category;
}
const [portfolio, total, categories] = await Promise.all([
Portfolio.find(query)
.sort({ featured: -1, publishedAt: -1 })
.skip(skip)
.limit(limit),
Portfolio.countDocuments(query),
Portfolio.distinct('category', { isPublished: true })
const [settings, portfolio, total, categories] = await Promise.all([
SiteSettings.findOne() || {},
Portfolio.findAll({
where: query,
order: [['featured', 'DESC'], ['publishedAt', 'DESC']],
offset: skip,
limit: limit
}),
Portfolio.count({ where: query }),
Portfolio.findAll({
where: { isPublished: true },
attributes: ['category'],
group: ['category']
}).then(results => results.map(r => r.category))
]);
const totalPages = Math.ceil(total / limit);
res.render('portfolio', {
title: 'Portfolio - SmartSolTech',
portfolio,
settings: settings || {},
portfolioItems: portfolio,
categories,
currentCategory: category || 'all',
pagination: {
@@ -93,6 +106,7 @@ router.get('/portfolio', async (req, res) => {
console.error('Portfolio page error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}
@@ -101,11 +115,15 @@ router.get('/portfolio', async (req, res) => {
// Portfolio detail page
router.get('/portfolio/:id', async (req, res) => {
try {
const portfolio = await Portfolio.findById(req.params.id);
const [settings, portfolio] = await Promise.all([
SiteSettings.findOne() || {},
Portfolio.findByPk(req.params.id)
]);
if (!portfolio || !portfolio.isPublished) {
return res.status(404).render('404', {
title: '404 - Project Not Found',
settings: settings || {},
message: 'The requested project was not found'
});
}
@@ -115,14 +133,19 @@ router.get('/portfolio/:id', async (req, res) => {
await portfolio.save();
// Get related projects
const relatedProjects = await Portfolio.find({
_id: { $ne: portfolio._id },
category: portfolio.category,
isPublished: true
}).limit(3);
const relatedProjects = await Portfolio.findAll({
where: {
id: { [Op.ne]: portfolio.id },
category: portfolio.category,
isPublished: true
},
order: [['publishedAt', 'DESC']],
limit: 3
});
res.render('portfolio-detail', {
title: `${portfolio.title} - Portfolio - SmartSolTech`,
settings: settings || {},
portfolio,
relatedProjects,
currentPage: 'portfolio'
@@ -131,6 +154,7 @@ router.get('/portfolio/:id', async (req, res) => {
console.error('Portfolio detail error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}
@@ -139,14 +163,22 @@ router.get('/portfolio/:id', async (req, res) => {
// Services page
router.get('/services', async (req, res) => {
try {
const services = await Service.find({ isActive: true })
.sort({ featured: -1, order: 1 })
.populate('portfolio', 'title images');
const categories = await Service.distinct('category', { isActive: true });
const [settings, services, categories] = await Promise.all([
SiteSettings.findOne() || {},
Service.findAll({
where: { isActive: true },
order: [['featured', 'DESC'], ['order', 'ASC']]
}),
Service.findAll({
where: { isActive: true },
attributes: ['category'],
group: ['category']
})
]);
res.render('services', {
title: 'Services - SmartSolTech',
settings: settings || {},
services,
categories,
currentPage: 'services'
@@ -155,6 +187,7 @@ router.get('/services', async (req, res) => {
console.error('Services page error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}
@@ -163,12 +196,18 @@ router.get('/services', async (req, res) => {
// Calculator page
router.get('/calculator', async (req, res) => {
try {
const services = await Service.find({ isActive: true })
.select('name pricing category')
.sort({ category: 1, name: 1 });
const [settings, services] = await Promise.all([
SiteSettings.findOne() || {},
Service.findAll({
where: { isActive: true },
attributes: ['id', 'name', 'pricing', 'category'],
order: [['category', 'ASC'], ['name', 'ASC']]
})
]);
res.render('calculator', {
title: 'Project Calculator - SmartSolTech',
settings: settings || {},
services,
currentPage: 'calculator'
});
@@ -176,6 +215,7 @@ router.get('/calculator', async (req, res) => {
console.error('Calculator page error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}
@@ -195,6 +235,7 @@ router.get('/contact', async (req, res) => {
console.error('Contact page error:', error);
res.status(500).render('error', {
title: 'Error',
settings: {},
message: 'Something went wrong'
});
}