some fixes
This commit is contained in:
921
.history/routes/admin_20251022194741.js
Normal file
921
.history/routes/admin_20251022194741.js
Normal file
@@ -0,0 +1,921 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { body, validationResult } = require('express-validator');
|
||||
const { User, Portfolio, Service, Contact, SiteSettings } = require('../models');
|
||||
|
||||
// Authentication middleware
|
||||
const requireAuth = (req, res, next) => {
|
||||
if (!req.session.user) {
|
||||
return res.redirect('/admin/login');
|
||||
}
|
||||
next();
|
||||
};
|
||||
|
||||
// Admin login page
|
||||
router.get('/login', (req, res) => {
|
||||
if (req.session.user) {
|
||||
return res.redirect('/admin/dashboard');
|
||||
}
|
||||
|
||||
res.render('admin/login', {
|
||||
title: 'Admin Login',
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
// Admin login POST
|
||||
router.post('/login', async (req, res) => {
|
||||
try {
|
||||
const { email, password } = req.body;
|
||||
|
||||
const user = await User.findOne({
|
||||
where: {
|
||||
email: email,
|
||||
isActive: true
|
||||
}
|
||||
});
|
||||
if (!user || !(await user.comparePassword(password))) {
|
||||
return res.render('admin/login', {
|
||||
title: 'Admin Login',
|
||||
error: 'Invalid credentials'
|
||||
});
|
||||
}
|
||||
|
||||
await user.updateLastLogin();
|
||||
|
||||
req.session.user = {
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role
|
||||
};
|
||||
|
||||
res.redirect('/admin/dashboard');
|
||||
} catch (error) {
|
||||
console.error('Admin login error:', error);
|
||||
res.render('admin/login', {
|
||||
title: 'Admin Login',
|
||||
error: 'Server error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Admin logout
|
||||
router.post('/logout', (req, res) => {
|
||||
req.session.destroy(err => {
|
||||
if (err) {
|
||||
console.error('Logout error:', err);
|
||||
}
|
||||
res.redirect('/admin/login');
|
||||
});
|
||||
});
|
||||
|
||||
// Dashboard (default route)
|
||||
router.get('/', requireAuth, async (req, res) => {
|
||||
res.redirect('/admin/dashboard');
|
||||
});
|
||||
|
||||
// Dashboard
|
||||
router.get('/dashboard', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const [
|
||||
portfolioCount,
|
||||
servicesCount,
|
||||
contactsCount,
|
||||
recentContacts,
|
||||
recentPortfolio
|
||||
] = await Promise.all([
|
||||
Portfolio.count({ where: { isPublished: true } }),
|
||||
Service.count({ where: { isActive: true } }),
|
||||
Contact.count(),
|
||||
Contact.findAll({
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 5
|
||||
}),
|
||||
Portfolio.findAll({
|
||||
where: { isPublished: true },
|
||||
order: [['createdAt', 'DESC']],
|
||||
limit: 5
|
||||
})
|
||||
]);
|
||||
|
||||
const stats = {
|
||||
portfolioCount: portfolioCount,
|
||||
servicesCount: servicesCount,
|
||||
contactsCount: contactsCount,
|
||||
usersCount: await User.count()
|
||||
};
|
||||
|
||||
res.render('admin/dashboard', {
|
||||
title: 'Admin Dashboard',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
stats,
|
||||
recentContacts,
|
||||
recentPortfolio
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Dashboard error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading dashboard'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Banner Editor
|
||||
router.get('/banner-editor', requireAuth, async (req, res) => {
|
||||
try {
|
||||
res.render('admin/banner-editor', {
|
||||
title: 'Редактор Баннеров - Admin Panel',
|
||||
layout: false, // Отключаем layout для этой страницы
|
||||
user: req.session.user,
|
||||
locale: req.getLocale() || 'ko',
|
||||
theme: req.session.theme || 'light'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Banner editor error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading banner editor'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Portfolio management
|
||||
router.get('/portfolio', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = 20;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [portfolio, total] = await Promise.all([
|
||||
Portfolio.findAll({
|
||||
order: [['createdAt', 'DESC']],
|
||||
offset: skip,
|
||||
limit: limit
|
||||
}),
|
||||
Portfolio.count()
|
||||
]);
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
res.render('admin/portfolio/list', {
|
||||
title: 'Portfolio Management - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
portfolio,
|
||||
pagination: {
|
||||
current: page,
|
||||
total: totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Portfolio list error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading portfolio'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add portfolio item
|
||||
router.get('/portfolio/add', requireAuth, (req, res) => {
|
||||
res.render('admin/portfolio/add', {
|
||||
title: 'Add Portfolio Item - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
categories: [
|
||||
'web-development',
|
||||
'mobile-app',
|
||||
'ui-ux-design',
|
||||
'e-commerce',
|
||||
'enterprise',
|
||||
'other'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
// Create portfolio item
|
||||
router.post('/portfolio/add', requireAuth, [
|
||||
body('title').notEmpty().withMessage('제목을 입력해주세요'),
|
||||
body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'),
|
||||
body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'),
|
||||
body('category').notEmpty().withMessage('카테고리를 선택해주세요'),
|
||||
body('technologies').isArray({ min: 1 }).withMessage('최소 한 개의 기술을 입력해주세요'),
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '입력 데이터를 확인해주세요',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
technologies,
|
||||
demoUrl,
|
||||
githubUrl,
|
||||
clientName,
|
||||
duration,
|
||||
isPublished = false,
|
||||
featured = false
|
||||
} = req.body;
|
||||
|
||||
const portfolio = await Portfolio.create({
|
||||
title,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()),
|
||||
demoUrl: demoUrl || null,
|
||||
githubUrl: githubUrl || null,
|
||||
clientName: clientName || null,
|
||||
duration: duration ? parseInt(duration) : null,
|
||||
isPublished: Boolean(isPublished),
|
||||
featured: Boolean(featured),
|
||||
publishedAt: Boolean(isPublished) ? new Date() : null,
|
||||
status: Boolean(isPublished) ? 'published' : 'draft'
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '포트폴리오가 성공적으로 생성되었습니다',
|
||||
portfolio: {
|
||||
id: portfolio.id,
|
||||
title: portfolio.title,
|
||||
category: portfolio.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Portfolio creation error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '포트폴리오 생성 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Edit portfolio item
|
||||
router.get('/portfolio/edit/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const portfolio = await Portfolio.findByPk(req.params.id);
|
||||
|
||||
if (!portfolio) {
|
||||
return res.status(404).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Portfolio item not found'
|
||||
});
|
||||
}
|
||||
|
||||
res.render('admin/portfolio/edit', {
|
||||
title: 'Edit Portfolio Item - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
portfolio,
|
||||
categories: [
|
||||
'web-development',
|
||||
'mobile-app',
|
||||
'ui-ux-design',
|
||||
'e-commerce',
|
||||
'enterprise',
|
||||
'other'
|
||||
]
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Portfolio edit error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading portfolio item'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update portfolio item
|
||||
router.put('/portfolio/:id', requireAuth, [
|
||||
body('title').notEmpty().withMessage('제목을 입력해주세요'),
|
||||
body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'),
|
||||
body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'),
|
||||
body('category').notEmpty().withMessage('카테고리를 선택해주세요')
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '입력 데이터를 확인해주세요',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
|
||||
const portfolio = await Portfolio.findByPk(req.params.id);
|
||||
if (!portfolio) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '포트폴리오를 찾을 수 없습니다'
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
title,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
technologies,
|
||||
demoUrl,
|
||||
githubUrl,
|
||||
clientName,
|
||||
duration,
|
||||
isPublished,
|
||||
featured
|
||||
} = req.body;
|
||||
|
||||
// Update portfolio
|
||||
await portfolio.update({
|
||||
title,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
technologies: Array.isArray(technologies) ? technologies : technologies.split(',').map(t => t.trim()),
|
||||
demoUrl: demoUrl || null,
|
||||
githubUrl: githubUrl || null,
|
||||
clientName: clientName || null,
|
||||
duration: duration ? parseInt(duration) : null,
|
||||
isPublished: Boolean(isPublished),
|
||||
featured: Boolean(featured),
|
||||
publishedAt: Boolean(isPublished) && !portfolio.publishedAt ? new Date() : portfolio.publishedAt,
|
||||
status: Boolean(isPublished) ? 'published' : 'draft'
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '포트폴리오가 성공적으로 업데이트되었습니다',
|
||||
portfolio: {
|
||||
id: portfolio.id,
|
||||
title: portfolio.title,
|
||||
category: portfolio.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Portfolio update error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '포트폴리오 업데이트 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete portfolio item
|
||||
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: '포트폴리오를 찾을 수 없습니다'
|
||||
});
|
||||
}
|
||||
|
||||
await portfolio.destroy();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '포트폴리오가 성공적으로 삭제되었습니다'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Portfolio deletion error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '포트폴리오 삭제 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle portfolio publish status
|
||||
router.patch('/portfolio/:id/toggle-publish', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const portfolio = await Portfolio.findByPk(req.params.id);
|
||||
|
||||
if (!portfolio) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '포트폴리오를 찾을 수 없습니다'
|
||||
});
|
||||
}
|
||||
|
||||
const newStatus = !portfolio.isPublished;
|
||||
await portfolio.update({
|
||||
isPublished: newStatus,
|
||||
publishedAt: newStatus && !portfolio.publishedAt ? new Date() : portfolio.publishedAt,
|
||||
status: newStatus ? 'published' : 'draft'
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `포트폴리오가 ${newStatus ? '게시' : '비공개'}되었습니다`,
|
||||
isPublished: newStatus
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Portfolio toggle publish error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '상태 변경 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Services management
|
||||
router.get('/services', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = 20;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
const [services, total] = await Promise.all([
|
||||
Service.findAll({
|
||||
order: [['createdAt', 'DESC']],
|
||||
offset: skip,
|
||||
limit: limit
|
||||
}),
|
||||
Service.count()
|
||||
]);
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
res.render('admin/services/list', {
|
||||
title: 'Services Management - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
services,
|
||||
pagination: {
|
||||
current: page,
|
||||
total: totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Services list error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading services'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add service
|
||||
router.get('/services/add', requireAuth, (req, res) => {
|
||||
res.render('admin/services/add', {
|
||||
title: 'Add Service - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
serviceTypes: [
|
||||
'web-development',
|
||||
'mobile-app',
|
||||
'ui-ux-design',
|
||||
'e-commerce',
|
||||
'seo',
|
||||
'maintenance',
|
||||
'consultation',
|
||||
'other'
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
// Create service
|
||||
router.post('/services/add', requireAuth, [
|
||||
body('name').notEmpty().withMessage('서비스명을 입력해주세요'),
|
||||
body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'),
|
||||
body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'),
|
||||
body('category').notEmpty().withMessage('카테고리를 선택해주세요'),
|
||||
body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요')
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '입력 데이터를 확인해주세요',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
basePrice,
|
||||
features,
|
||||
duration,
|
||||
isActive = true,
|
||||
featured = false
|
||||
} = req.body;
|
||||
|
||||
const service = await Service.create({
|
||||
name,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
basePrice: parseFloat(basePrice),
|
||||
features: features || [],
|
||||
duration: duration ? parseInt(duration) : null,
|
||||
isActive: Boolean(isActive),
|
||||
featured: Boolean(featured)
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '서비스가 성공적으로 생성되었습니다',
|
||||
service: {
|
||||
id: service.id,
|
||||
name: service.name,
|
||||
category: service.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Service creation error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '서비스 생성 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Edit service
|
||||
router.get('/services/edit/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const service = await Service.findByPk(req.params.id);
|
||||
|
||||
if (!service) {
|
||||
return res.status(404).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Service not found'
|
||||
});
|
||||
}
|
||||
|
||||
const availablePortfolio = await Portfolio.findAll({
|
||||
where: { isPublished: true },
|
||||
attributes: ['id', 'title', 'category']
|
||||
});
|
||||
|
||||
res.render('admin/services/edit', {
|
||||
title: 'Edit Service - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
service,
|
||||
availablePortfolio,
|
||||
serviceTypes: [
|
||||
'web-development',
|
||||
'mobile-app',
|
||||
'ui-ux-design',
|
||||
'e-commerce',
|
||||
'seo',
|
||||
'maintenance',
|
||||
'consultation',
|
||||
'other'
|
||||
]
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Service edit error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading service'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Update service
|
||||
router.put('/services/:id', requireAuth, [
|
||||
body('name').notEmpty().withMessage('서비스명을 입력해주세요'),
|
||||
body('shortDescription').notEmpty().withMessage('간단한 설명을 입력해주세요'),
|
||||
body('description').notEmpty().withMessage('자세한 설명을 입력해주세요'),
|
||||
body('category').notEmpty().withMessage('카테고리를 선택해주세요'),
|
||||
body('basePrice').isNumeric().withMessage('기본 가격을 입력해주세요')
|
||||
], async (req, res) => {
|
||||
try {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: '입력 데이터를 확인해주세요',
|
||||
errors: errors.array()
|
||||
});
|
||||
}
|
||||
|
||||
const service = await Service.findByPk(req.params.id);
|
||||
if (!service) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '서비스를 찾을 수 없습니다'
|
||||
});
|
||||
}
|
||||
|
||||
const {
|
||||
name,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
basePrice,
|
||||
features,
|
||||
duration,
|
||||
isActive,
|
||||
featured
|
||||
} = req.body;
|
||||
|
||||
await service.update({
|
||||
name,
|
||||
shortDescription,
|
||||
description,
|
||||
category,
|
||||
basePrice: parseFloat(basePrice),
|
||||
features: features || [],
|
||||
duration: duration ? parseInt(duration) : null,
|
||||
isActive: Boolean(isActive),
|
||||
featured: Boolean(featured)
|
||||
});
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '서비스가 성공적으로 업데이트되었습니다',
|
||||
service: {
|
||||
id: service.id,
|
||||
name: service.name,
|
||||
category: service.category
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Service update error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '서비스 업데이트 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Delete service
|
||||
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: '서비스를 찾을 수 없습니다'
|
||||
});
|
||||
}
|
||||
|
||||
await service.destroy();
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: '서비스가 성공적으로 삭제되었습니다'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Service deletion error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '서비스 삭제 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle service active status
|
||||
router.patch('/services/:id/toggle-active', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const service = await Service.findByPk(req.params.id);
|
||||
|
||||
if (!service) {
|
||||
return res.status(404).json({
|
||||
success: false,
|
||||
message: '서비스를 찾을 수 없습니다'
|
||||
});
|
||||
}
|
||||
|
||||
const newStatus = !service.isActive;
|
||||
await service.update({ isActive: newStatus });
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: `서비스가 ${newStatus ? '활성화' : '비활성화'}되었습니다`,
|
||||
isActive: newStatus
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Service toggle active error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: '상태 변경 중 오류가 발생했습니다'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Contacts management
|
||||
router.get('/contacts', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const page = parseInt(req.query.page) || 1;
|
||||
const limit = 20;
|
||||
const skip = (page - 1) * limit;
|
||||
const status = req.query.status;
|
||||
|
||||
let whereClause = {};
|
||||
if (status && status !== 'all') {
|
||||
whereClause.status = status;
|
||||
}
|
||||
|
||||
const [contacts, total] = await Promise.all([
|
||||
Contact.findAll({
|
||||
where: whereClause,
|
||||
order: [['createdAt', 'DESC']],
|
||||
offset: skip,
|
||||
limit: limit
|
||||
}),
|
||||
Contact.count({ where: whereClause })
|
||||
]);
|
||||
|
||||
const totalPages = Math.ceil(total / limit);
|
||||
|
||||
res.render('admin/contacts/list', {
|
||||
title: 'Contacts Management - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
contacts,
|
||||
currentStatus: status || 'all',
|
||||
pagination: {
|
||||
current: page,
|
||||
total: totalPages,
|
||||
hasNext: page < totalPages,
|
||||
hasPrev: page > 1
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Contacts list error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading contacts'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// View contact details
|
||||
router.get('/contacts/:id', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const contact = await Contact.findByPk(req.params.id);
|
||||
|
||||
if (!contact) {
|
||||
return res.status(404).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Contact not found'
|
||||
});
|
||||
}
|
||||
|
||||
// Mark as read
|
||||
if (!contact.isRead) {
|
||||
contact.isRead = true;
|
||||
await contact.save();
|
||||
}
|
||||
|
||||
res.render('admin/contacts/view', {
|
||||
title: 'Contact Details - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
contact
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Contact view error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading contact'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Settings
|
||||
router.get('/settings', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const settings = await SiteSettings.findOne() || await SiteSettings.create({});
|
||||
|
||||
res.render('admin/settings', {
|
||||
title: 'Site Settings - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
settings
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Settings error:', error);
|
||||
res.status(500).render('admin/error', {
|
||||
title: 'Error - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
message: 'Error loading settings'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Media gallery
|
||||
router.get('/media', requireAuth, (req, res) => {
|
||||
res.render('admin/media', {
|
||||
title: 'Media Gallery - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user
|
||||
});
|
||||
});
|
||||
|
||||
// Telegram bot configuration and testing
|
||||
router.get('/telegram', requireAuth, (req, res) => {
|
||||
const telegramService = require('../services/telegram');
|
||||
res.render('admin/telegram', {
|
||||
title: 'Telegram Bot - Admin Panel',
|
||||
layout: 'admin/layout',
|
||||
user: req.session.user,
|
||||
botConfigured: telegramService.isEnabled
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/telegram/test', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const telegramService = require('../services/telegram');
|
||||
const result = await telegramService.testConnection();
|
||||
|
||||
if (result.success) {
|
||||
const testMessage = `🤖 <b>Тест Telegram бота</b>\n\n` +
|
||||
`✅ Соединение успешно установлено!\n` +
|
||||
`⏰ Время тестирования: ${new Date().toLocaleString('ru-RU')}\n` +
|
||||
`🌐 Сайт: ${process.env.BASE_URL || 'http://localhost:3000'}\n\n` +
|
||||
`Бот готов к отправке уведомлений! 🚀`;
|
||||
|
||||
await telegramService.sendMessage(testMessage);
|
||||
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Test message sent successfully!',
|
||||
botInfo: result.bot
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: result.message || 'Failed to connect to Telegram bot'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Telegram test error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Error testing Telegram bot'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/telegram/send', requireAuth, async (req, res) => {
|
||||
try {
|
||||
const { message } = req.body;
|
||||
|
||||
if (!message || message.trim().length === 0) {
|
||||
return res.status(400).json({
|
||||
success: false,
|
||||
message: 'Message is required'
|
||||
});
|
||||
}
|
||||
|
||||
const telegramService = require('../services/telegram');
|
||||
const result = await telegramService.sendMessage(message);
|
||||
|
||||
if (result.success) {
|
||||
res.json({
|
||||
success: true,
|
||||
message: 'Message sent successfully!'
|
||||
});
|
||||
} else {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
message: result.error || 'Failed to send message'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Send Telegram message error:', error);
|
||||
res.status(500).json({
|
||||
success: false,
|
||||
message: 'Error sending message'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
Reference in New Issue
Block a user