486 lines
16 KiB
JavaScript
486 lines
16 KiB
JavaScript
/**
|
|
* Demo server for SmartSolTech website
|
|
* Uses mock data instead of MongoDB for demonstration
|
|
*/
|
|
|
|
const express = require('express');
|
|
const path = require('path');
|
|
const cookieParser = require('cookie-parser');
|
|
const session = require('express-session');
|
|
const flash = require('connect-flash');
|
|
const helmet = require('helmet');
|
|
const compression = require('compression');
|
|
const rateLimit = require('express-rate-limit');
|
|
const i18n = require('i18n');
|
|
|
|
const app = express();
|
|
const PORT = process.env.PORT || 3000;
|
|
|
|
// Configure i18n
|
|
i18n.configure({
|
|
locales: ['en', 'ko', 'ru', 'kk'],
|
|
directory: path.join(__dirname, 'locales'),
|
|
defaultLocale: 'ko',
|
|
cookie: 'language',
|
|
queryParameter: 'lang',
|
|
autoReload: true,
|
|
syncFiles: true,
|
|
objectNotation: true
|
|
});
|
|
|
|
// Security middleware
|
|
app.use(helmet({
|
|
contentSecurityPolicy: {
|
|
directives: {
|
|
defaultSrc: ["'self'"],
|
|
styleSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://unpkg.com"],
|
|
scriptSrc: ["'self'", "'unsafe-inline'", "https://cdn.jsdelivr.net", "https://cdnjs.cloudflare.com", "https://unpkg.com"],
|
|
imgSrc: ["'self'", "data:", "https:", "blob:"],
|
|
fontSrc: ["'self'", "https://cdnjs.cloudflare.com"],
|
|
connectSrc: ["'self'", "https:"],
|
|
mediaSrc: ["'self'"],
|
|
frameSrc: ["'none'"]
|
|
}
|
|
}
|
|
}));
|
|
|
|
// Rate limiting
|
|
const limiter = rateLimit({
|
|
windowMs: 15 * 60 * 1000, // 15 minutes
|
|
max: 100, // limit each IP to 100 requests per windowMs
|
|
message: 'Too many requests from this IP, please try again later.'
|
|
});
|
|
app.use(limiter);
|
|
|
|
// Compression
|
|
app.use(compression());
|
|
|
|
// View engine setup
|
|
app.set('views', path.join(__dirname, 'views'));
|
|
app.set('view engine', 'ejs');
|
|
|
|
// Middleware
|
|
app.use(express.json({ limit: '10mb' }));
|
|
app.use(express.urlencoded({ extended: true, limit: '10mb' }));
|
|
app.use(cookieParser());
|
|
app.use(express.static(path.join(__dirname, 'public')));
|
|
|
|
// Initialize i18n
|
|
app.use(i18n.init);
|
|
|
|
// Session configuration
|
|
app.use(session({
|
|
secret: 'demo-secret-key',
|
|
resave: false,
|
|
saveUninitialized: false,
|
|
cookie: {
|
|
secure: false, // Set to true in production with HTTPS
|
|
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
|
}
|
|
}));
|
|
|
|
// Flash messages
|
|
app.use(flash());
|
|
|
|
// Global variables for templates
|
|
app.use((req, res, next) => {
|
|
res.locals.success_msg = req.flash('success');
|
|
res.locals.error_msg = req.flash('error');
|
|
res.locals.error = req.flash('error');
|
|
res.locals.currentYear = new Date().getFullYear();
|
|
next();
|
|
});
|
|
|
|
// Mock data
|
|
const mockPortfolio = [
|
|
{
|
|
_id: '1',
|
|
title: 'E-commerce 플랫폼',
|
|
description: '현대적인 온라인 쇼핑몰 플랫폼을 개발했습니다. React와 Node.js를 활용하여 높은 성능과 사용자 경험을 제공하며, 결제 시스템과 재고 관리 기능을 포함합니다.',
|
|
shortDescription: '반응형 온라인 쇼핑몰 플랫폼 개발',
|
|
category: 'web-development',
|
|
technologies: ['React', 'Node.js', 'MongoDB', 'Express', 'Stripe', 'AWS'],
|
|
images: [
|
|
{ url: '/images/portfolio/ecommerce-1.jpg', alt: 'E-commerce 메인페이지', isPrimary: true }
|
|
],
|
|
clientName: '패션 브랜드 ABC',
|
|
projectUrl: 'https://example-ecommerce.demo',
|
|
status: 'completed',
|
|
featured: true,
|
|
publishedAt: new Date('2024-01-15'),
|
|
completedAt: new Date('2024-01-10'),
|
|
isPublished: true,
|
|
viewCount: 150,
|
|
likes: 25
|
|
},
|
|
{
|
|
_id: '2',
|
|
title: '모바일 피트니스 앱',
|
|
description: 'React Native를 사용하여 크로스플랫폼 피트니스 애플리케이션을 개발했습니다. 운동 계획, 칼로리 추적, 소셜 기능을 포함하여 사용자들의 건강한 라이프스타일을 지원합니다.',
|
|
shortDescription: '건강 관리를 위한 크로스플랫폼 모바일 앱',
|
|
category: 'mobile-app',
|
|
technologies: ['React Native', 'Redux', 'Firebase', 'Node.js', 'PostgreSQL'],
|
|
images: [
|
|
{ url: '/images/portfolio/fitness-1.jpg', alt: '피트니스 앱 메인화면', isPrimary: true }
|
|
],
|
|
clientName: '헬스케어 스타트업 FIT',
|
|
status: 'completed',
|
|
featured: true,
|
|
publishedAt: new Date('2024-02-20'),
|
|
completedAt: new Date('2024-02-15'),
|
|
isPublished: true,
|
|
viewCount: 200,
|
|
likes: 35
|
|
},
|
|
{
|
|
_id: '3',
|
|
title: '기업 웹사이트 리뉴얼',
|
|
description: '기업의 브랜드 아이덴티티를 반영한 웹사이트 리뉴얼 프로젝트입니다. 사용자 경험을 개선하고 모던한 디자인을 적용하여 브랜드 가치를 높였습니다.',
|
|
shortDescription: '브랜드 아이덴티티를 반영한 기업 웹사이트 리뉴얼',
|
|
category: 'ui-ux-design',
|
|
technologies: ['Figma', 'React', 'Sass', 'Framer Motion', 'Contentful'],
|
|
images: [
|
|
{ url: '/images/portfolio/corporate-1.jpg', alt: '기업 웹사이트 메인페이지', isPrimary: true }
|
|
],
|
|
clientName: '기술 기업 TechCorp',
|
|
projectUrl: 'https://example-corp.demo',
|
|
status: 'completed',
|
|
featured: true,
|
|
publishedAt: new Date('2024-03-10'),
|
|
completedAt: new Date('2024-03-05'),
|
|
isPublished: true,
|
|
viewCount: 120,
|
|
likes: 18
|
|
}
|
|
];
|
|
|
|
const mockServices = [
|
|
{
|
|
_id: '1',
|
|
name: '웹 개발',
|
|
description: '현대적이고 반응형인 웹사이트와 웹 애플리케이션을 개발합니다. React, Node.js, MongoDB 등 최신 기술 스택을 활용하여 성능과 사용자 경험을 최적화합니다.',
|
|
shortDescription: '현대적이고 반응형 웹사이트 및 웹 애플리케이션 개발',
|
|
icon: 'fas fa-code',
|
|
category: 'development',
|
|
pricing: {
|
|
basePrice: 500000,
|
|
currency: 'KRW',
|
|
priceType: 'project',
|
|
priceRange: { min: 500000, max: 5000000 }
|
|
},
|
|
featured: true,
|
|
isActive: true
|
|
},
|
|
{
|
|
_id: '2',
|
|
name: '모바일 앱 개발',
|
|
description: 'iOS와 Android 플랫폼을 위한 네이티브 및 크로스플랫폼 모바일 애플리케이션을 개발합니다. React Native, Flutter 등을 활용하여 효율적인 개발을 진행합니다.',
|
|
shortDescription: 'iOS/Android 네이티브 및 크로스플랫폼 앱 개발',
|
|
icon: 'fas fa-mobile-alt',
|
|
category: 'development',
|
|
pricing: {
|
|
basePrice: 800000,
|
|
currency: 'KRW',
|
|
priceType: 'project',
|
|
priceRange: { min: 800000, max: 8000000 }
|
|
},
|
|
featured: true,
|
|
isActive: true
|
|
},
|
|
{
|
|
_id: '3',
|
|
name: 'UI/UX 디자인',
|
|
description: '사용자 중심의 직관적이고 아름다운 인터페이스를 디자인합니다. 사용자 경험 연구와 프로토타이핑을 통해 최적의 디자인 솔루션을 제공합니다.',
|
|
shortDescription: '사용자 중심의 직관적이고 아름다운 인터페이스 디자인',
|
|
icon: 'fas fa-palette',
|
|
category: 'design',
|
|
pricing: {
|
|
basePrice: 300000,
|
|
currency: 'KRW',
|
|
priceType: 'project',
|
|
priceRange: { min: 300000, max: 2000000 }
|
|
},
|
|
featured: true,
|
|
isActive: true
|
|
},
|
|
{
|
|
_id: '4',
|
|
name: '디지털 마케팅',
|
|
description: 'SEO, 소셜미디어 마케팅, 온라인 광고를 통해 디지털 마케팅 전략을 수립하고 실행합니다. 데이터 분석을 통한 지속적인 최적화를 제공합니다.',
|
|
shortDescription: 'SEO, 소셜미디어, 온라인 광고를 통한 디지털 마케팅',
|
|
icon: 'fas fa-chart-line',
|
|
category: 'marketing',
|
|
pricing: {
|
|
basePrice: 200000,
|
|
currency: 'KRW',
|
|
priceType: 'project',
|
|
priceRange: { min: 200000, max: 1500000 }
|
|
},
|
|
featured: true,
|
|
isActive: true
|
|
}
|
|
];
|
|
|
|
const mockSettings = {
|
|
siteName: 'SmartSolTech',
|
|
siteDescription: '혁신적인 기술 솔루션으로 비즈니스의 성장을 지원합니다',
|
|
contact: {
|
|
email: 'info@smartsoltech.kr',
|
|
phone: '+82-10-1234-5678',
|
|
address: 'Seoul, South Korea'
|
|
},
|
|
social: {
|
|
facebook: 'https://facebook.com/smartsoltech',
|
|
twitter: 'https://twitter.com/smartsoltech',
|
|
linkedin: 'https://linkedin.com/company/smartsoltech',
|
|
instagram: 'https://instagram.com/smartsoltech'
|
|
}
|
|
};
|
|
|
|
// Helper function for category names
|
|
function getCategoryName(category) {
|
|
const categoryNames = {
|
|
'web-development': '웹 개발',
|
|
'mobile-app': '모바일 앱',
|
|
'ui-ux-design': 'UI/UX 디자인',
|
|
'branding': '브랜딩',
|
|
'marketing': '디지털 마케팅'
|
|
};
|
|
return categoryNames[category] || category;
|
|
}
|
|
|
|
// Language switching route
|
|
app.get('/lang/:language', (req, res) => {
|
|
const language = req.params.language;
|
|
const supportedLanguages = ['en', 'ko', 'ru', 'kk'];
|
|
|
|
if (supportedLanguages.includes(language)) {
|
|
res.cookie('language', language, { maxAge: 365 * 24 * 60 * 60 * 1000 }); // 1 year
|
|
req.setLocale(language);
|
|
}
|
|
|
|
const redirectUrl = req.get('Referer') || '/';
|
|
res.redirect(redirectUrl);
|
|
});
|
|
|
|
// Middleware to set language and theme preferences
|
|
app.use((req, res, next) => {
|
|
// Set language
|
|
const language = req.cookies.language || req.query.lang || 'ko';
|
|
if (['en', 'ko', 'ru', 'kk'].includes(language)) {
|
|
req.setLocale(language);
|
|
}
|
|
|
|
// Set theme preference
|
|
const theme = req.cookies.theme || 'light';
|
|
res.locals.theme = theme;
|
|
res.locals.currentLanguage = req.getLocale();
|
|
res.locals.__ = res.__;
|
|
|
|
next();
|
|
});
|
|
|
|
// Theme switching route
|
|
app.get('/theme/:theme', (req, res) => {
|
|
const theme = req.params.theme;
|
|
if (['light', 'dark'].includes(theme)) {
|
|
res.cookie('theme', theme, { maxAge: 365 * 24 * 60 * 60 * 1000 }); // 1 year
|
|
}
|
|
|
|
const redirectUrl = req.get('Referer') || '/';
|
|
res.redirect(redirectUrl);
|
|
});
|
|
|
|
// Routes
|
|
app.get('/', (req, res) => {
|
|
res.render('index', {
|
|
title: res.__('navigation.home') + ' - SmartSolTech',
|
|
settings: mockSettings,
|
|
featuredPortfolio: mockPortfolio.filter(p => p.featured),
|
|
featuredServices: mockServices.filter(s => s.featured),
|
|
currentPage: 'home'
|
|
});
|
|
});
|
|
|
|
app.get('/portfolio', (req, res) => {
|
|
const category = req.query.category;
|
|
let filteredPortfolio = mockPortfolio;
|
|
|
|
if (category && category !== 'all') {
|
|
filteredPortfolio = mockPortfolio.filter(p => p.category === category);
|
|
}
|
|
|
|
res.render('portfolio', {
|
|
title: res.__('navigation.portfolio') + ' - SmartSolTech',
|
|
portfolioItems: filteredPortfolio,
|
|
currentPage: 'portfolio'
|
|
});
|
|
});
|
|
|
|
app.get('/portfolio/:id', (req, res) => {
|
|
const portfolio = mockPortfolio.find(p => p._id === req.params.id);
|
|
|
|
if (!portfolio) {
|
|
return res.status(404).render('error', {
|
|
title: '404 - ' + res.__('common.error'),
|
|
message: res.__('common.error'),
|
|
currentPage: 'error'
|
|
});
|
|
}
|
|
|
|
const relatedProjects = mockPortfolio.filter(p =>
|
|
p._id !== portfolio._id && p.category === portfolio.category
|
|
).slice(0, 3);
|
|
|
|
res.render('portfolio-detail', {
|
|
title: `${portfolio.title} - ${res.__('navigation.portfolio')}`,
|
|
portfolio,
|
|
relatedProjects,
|
|
currentPage: 'portfolio'
|
|
});
|
|
});
|
|
|
|
app.get('/services', (req, res) => {
|
|
res.render('services', {
|
|
title: res.__('navigation.services') + ' - SmartSolTech',
|
|
services: mockServices,
|
|
currentPage: 'services'
|
|
});
|
|
});
|
|
|
|
app.get('/about', (req, res) => {
|
|
res.render('about', {
|
|
title: res.__('navigation.about') + ' - SmartSolTech',
|
|
currentPage: 'about'
|
|
});
|
|
});
|
|
|
|
app.get('/contact', (req, res) => {
|
|
res.render('contact', {
|
|
title: res.__('navigation.contact') + ' - SmartSolTech',
|
|
settings: mockSettings,
|
|
currentPage: 'contact'
|
|
});
|
|
});
|
|
|
|
app.post('/contact', (req, res) => {
|
|
// Simulate contact form processing
|
|
console.log('Contact form submission:', req.body);
|
|
|
|
req.flash('success', res.__('common.success'));
|
|
res.redirect('/contact');
|
|
});
|
|
|
|
app.get('/calculator', (req, res) => {
|
|
res.render('calculator', {
|
|
title: res.__('navigation.calculator') + ' - SmartSolTech',
|
|
services: mockServices,
|
|
currentPage: 'calculator'
|
|
});
|
|
});
|
|
|
|
// API Routes for calculator
|
|
app.post('/api/calculator/estimate', (req, res) => {
|
|
const { service, projectType, timeline, features } = req.body;
|
|
|
|
// Simple calculation logic
|
|
let basePrice = 500000;
|
|
const selectedService = mockServices.find(s => s._id === service);
|
|
|
|
if (selectedService) {
|
|
basePrice = selectedService.pricing.basePrice;
|
|
}
|
|
|
|
// Apply multipliers based on project complexity
|
|
const typeMultipliers = {
|
|
'simple': 1,
|
|
'medium': 1.5,
|
|
'complex': 2.5,
|
|
'enterprise': 4
|
|
};
|
|
|
|
const timelineMultipliers = {
|
|
'urgent': 1.5,
|
|
'normal': 1,
|
|
'flexible': 0.9
|
|
};
|
|
|
|
const typeMultiplier = typeMultipliers[projectType] || 1;
|
|
const timelineMultiplier = timelineMultipliers[timeline] || 1;
|
|
const featuresMultiplier = 1 + (features?.length || 0) * 0.2;
|
|
|
|
const estimatedPrice = Math.round(basePrice * typeMultiplier * timelineMultiplier * featuresMultiplier);
|
|
|
|
res.json({
|
|
success: true,
|
|
estimate: {
|
|
basePrice,
|
|
estimatedPrice,
|
|
breakdown: {
|
|
basePrice,
|
|
projectType: projectType,
|
|
typeMultiplier,
|
|
timeline,
|
|
timelineMultiplier,
|
|
features: features || [],
|
|
featuresMultiplier
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// API Routes for portfolio interactions
|
|
app.post('/api/portfolio/:id/like', (req, res) => {
|
|
const portfolio = mockPortfolio.find(p => p._id === req.params.id);
|
|
if (portfolio) {
|
|
portfolio.likes = (portfolio.likes || 0) + 1;
|
|
res.json({ success: true, likes: portfolio.likes });
|
|
} else {
|
|
res.status(404).json({ success: false, message: 'Portfolio not found' });
|
|
}
|
|
});
|
|
|
|
app.post('/api/portfolio/:id/view', (req, res) => {
|
|
const portfolio = mockPortfolio.find(p => p._id === req.params.id);
|
|
if (portfolio) {
|
|
portfolio.viewCount = (portfolio.viewCount || 0) + 1;
|
|
res.json({ success: true, viewCount: portfolio.viewCount });
|
|
} else {
|
|
res.status(404).json({ success: false, message: 'Portfolio not found' });
|
|
}
|
|
});
|
|
|
|
// Error handling
|
|
app.use((req, res) => {
|
|
res.status(404).render('error', {
|
|
title: '404 - 페이지를 찾을 수 없습니다',
|
|
message: '요청하신 페이지를 찾을 수 없습니다.',
|
|
currentPage: '404'
|
|
});
|
|
});
|
|
|
|
app.use((err, req, res, next) => {
|
|
console.error(err.stack);
|
|
res.status(500).render('error', {
|
|
title: '500 - 서버 오류',
|
|
message: '서버에서 오류가 발생했습니다.',
|
|
currentPage: 'error'
|
|
});
|
|
});
|
|
|
|
// Start server
|
|
app.listen(PORT, () => {
|
|
console.log(`🚀 SmartSolTech Demo Server running on http://localhost:${PORT}`);
|
|
console.log('📋 Available pages:');
|
|
console.log(' • Home: http://localhost:3000/');
|
|
console.log(' • About: http://localhost:3000/about');
|
|
console.log(' • Portfolio: http://localhost:3000/portfolio');
|
|
console.log(' • Services: http://localhost:3000/services');
|
|
console.log(' • Contact: http://localhost:3000/contact');
|
|
console.log(' • Calculator: http://localhost:3000/calculator');
|
|
console.log('');
|
|
console.log('💡 This is a demo version using mock data');
|
|
console.log('💾 To use with MongoDB, run: npm start');
|
|
});
|
|
|
|
module.exports = app; |