🚀 Korea Tourism Agency - Complete implementation

 Features:
- Modern tourism website with responsive design
- AdminJS admin panel with image editor integration
- PostgreSQL database with comprehensive schema
- Docker containerization
- Image upload and gallery management

🛠 Tech Stack:
- Backend: Node.js + Express.js
- Database: PostgreSQL 13+
- Frontend: HTML/CSS/JS with responsive design
- Admin: AdminJS with custom components
- Deployment: Docker + Docker Compose
- Image Processing: Sharp with optimization

📱 Admin Features:
- Routes/Tours management (city, mountain, fishing)
- Guides profiles with specializations
- Articles and blog system
- Image editor with upload/gallery/URL options
- User management and authentication
- Responsive admin interface

🎨 Design:
- Korean tourism focused branding
- Mobile-first responsive design
- Custom CSS with modern aesthetics
- Image optimization and gallery
- SEO-friendly structure

🔒 Security:
- Helmet.js security headers
- bcrypt password hashing
- Input validation and sanitization
- CORS protection
- Environment variables
This commit is contained in:
2025-11-30 00:53:15 +09:00
parent ed871fc4d1
commit b4e513e996
36 changed files with 6894 additions and 239 deletions

View File

@@ -0,0 +1,275 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Система управления изображениями - Korea Tourism</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet">
<style>
.hero-compact {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 40px 0;
}
.feature-icon {
width: 60px;
height: 60px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
font-size: 24px;
}
.feature-card {
border: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
}
.code-block {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 4px;
padding: 15px;
font-family: monospace;
margin: 15px 0;
}
.api-endpoint {
background: #e3f2fd;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.step-number {
width: 30px;
height: 30px;
border-radius: 50%;
background: #667eea;
color: white;
display: inline-flex;
align-items: center;
justify-content: center;
margin-right: 15px;
}
</style>
</head>
<body>
<!-- Навигация -->
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="/">Korea Tourism</a>
<div class="navbar-nav ms-auto">
<a class="nav-link" href="/">Главная</a>
<a class="nav-link" href="/admin">Админ-панель</a>
<a class="nav-link" href="/test-editor">Тест редактора</a>
</div>
</div>
</nav>
<!-- Hero Section -->
<section class="hero-compact">
<div class="container text-center">
<h1 class="display-5 fw-bold mb-3">🖼️ Система управления изображениями</h1>
<p class="lead">Полнофункциональный редактор с возможностями обрезки, поворота и оптимизации</p>
</div>
</section>
<!-- Функции -->
<section class="py-5">
<div class="container">
<div class="row g-4">
<div class="col-md-4">
<div class="card feature-card h-100">
<div class="card-body text-center">
<div class="feature-icon bg-primary text-white">
<i class="fas fa-upload"></i>
</div>
<h5 class="card-title">Загрузка и обработка</h5>
<p class="card-text">Drag & Drop загрузка с автоматической оптимизацией и конвертацией в JPEG</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card feature-card h-100">
<div class="card-body text-center">
<div class="feature-icon bg-success text-white">
<i class="fas fa-crop"></i>
</div>
<h5 class="card-title">Редактирование</h5>
<p class="card-text">Обрезка, поворот на 90°, отражение горизонтально и вертикально</p>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card feature-card h-100">
<div class="card-body text-center">
<div class="feature-icon bg-info text-white">
<i class="fas fa-cogs"></i>
</div>
<h5 class="card-title">API интеграция</h5>
<p class="card-text">REST API для интеграции с любыми формами и компонентами</p>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Инструкции по использованию -->
<section class="py-5 bg-light">
<div class="container">
<h2 class="text-center mb-5">📋 Инструкции по использованию</h2>
<div class="row">
<div class="col-md-6">
<h4><i class="fas fa-user-cog me-2"></i>Через AdminJS</h4>
<div class="mb-3">
<span class="step-number">1</span>
Зайдите в <a href="/admin" target="_blank">админ-панель</a>
</div>
<div class="mb-3">
<span class="step-number">2</span>
Выберите "Туры" или "Гиды"
</div>
<div class="mb-3">
<span class="step-number">3</span>
В поле "Image URL" укажите путь к изображению
</div>
<div class="code-block">
Например: /uploads/routes/my-image.jpg
</div>
</div>
<div class="col-md-6">
<h4><i class="fas fa-code me-2"></i>Через JavaScript</h4>
<div class="mb-3">
<span class="step-number">1</span>
Подключите скрипт редактора
</div>
<div class="code-block">
&lt;script src="/js/image-editor.js"&gt;&lt;/script&gt;
</div>
<div class="mb-3">
<span class="step-number">2</span>
Откройте редактор
</div>
<div class="code-block">
window.openImageEditor({
targetFolder: 'routes', // routes, guides, articles
}).then(url => {
console.log('Сохранено:', url);
});
</div>
</div>
</div>
</div>
</section>
<!-- API Документация -->
<section class="py-5">
<div class="container">
<h2 class="text-center mb-5">🔌 API Эндпоинты</h2>
<div class="row g-4">
<div class="col-md-6">
<h5><i class="fas fa-upload me-2 text-primary"></i>Загрузка изображения</h5>
<div class="api-endpoint">
<strong>POST</strong> /api/images/upload-image
</div>
<p>Загружает изображение во временную папку</p>
<div class="code-block">
const formData = new FormData();
formData.append('image', file);
fetch('/api/images/upload-image', {
method: 'POST',
body: formData
}).then(r => r.json());
</div>
</div>
<div class="col-md-6">
<h5><i class="fas fa-magic me-2 text-success"></i>Обработка изображения</h5>
<div class="api-endpoint">
<strong>POST</strong> /api/images/process-image
</div>
<p>Применяет трансформации и сохраняет финальный файл</p>
<div class="code-block">
fetch('/api/images/process-image', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tempId: 'temp-123',
rotation: 90,
flipHorizontal: false,
cropData: { x: 0, y: 0, width: 300, height: 200 },
targetFolder: 'routes'
})
});
</div>
</div>
<div class="col-md-6">
<h5><i class="fas fa-list me-2 text-info"></i>Список изображений</h5>
<div class="api-endpoint">
<strong>GET</strong> /api/images/images/{folder}
</div>
<p>Возвращает список всех изображений в папке</p>
<div class="code-block">
// Получить изображения туров
fetch('/api/images/images/routes')
.then(r => r.json())
.then(data => console.log(data.images));
</div>
</div>
<div class="col-md-6">
<h5><i class="fas fa-trash me-2 text-danger"></i>Удаление изображения</h5>
<div class="api-endpoint">
<strong>DELETE</strong> /api/images/image
</div>
<p>Удаляет изображение с сервера</p>
<div class="code-block">
fetch('/api/images/image', {
method: 'DELETE',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
url: '/uploads/routes/image.jpg'
})
});
</div>
</div>
</div>
</div>
</section>
<!-- Тестирование -->
<section class="py-5 bg-primary text-white">
<div class="container text-center">
<h2 class="mb-4">🧪 Тестирование системы</h2>
<p class="lead mb-4">Попробуйте все возможности редактора изображений</p>
<div class="d-flex gap-3 justify-content-center">
<a href="/test-editor" class="btn btn-light btn-lg">
<i class="fas fa-play me-2"></i>Открыть тест-редактор
</a>
<a href="/admin" class="btn btn-outline-light btn-lg">
<i class="fas fa-cog me-2"></i>Админ-панель
</a>
</div>
</div>
</section>
<!-- Footer -->
<footer class="bg-dark text-white py-3">
<div class="container text-center">
<p class="mb-0">© 2025 Korea Tourism Agency - Система управления изображениями</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>