const express = require('express'); const path = require('path'); const session = require('express-session'); const bcrypt = require('bcrypt'); const helmet = require('helmet'); const cors = require('cors'); require('dotenv').config(); // Инициализация базы данных const { db, initDatabase } = require('./database/init'); const app = express(); const port = process.env.PORT || 3000; // Middleware app.use(helmet({ contentSecurityPolicy: false, // Отключаем для удобства разработки })); app.use(cors()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); // Сессии app.use(session({ secret: process.env.SESSION_SECRET || 'your-secret-key-change-in-production', resave: false, saveUninitialized: false, cookie: { secure: process.env.NODE_ENV === 'production', maxAge: 24 * 60 * 60 * 1000 // 24 часа } })); // Middleware для проверки аутентификации function requireAuth(req, res, next) { if (req.session.userId) { next(); } else { res.status(401).json({ error: 'Authentication required' }); } } // Основная страница - перенаправление на SaaS платформу app.get('/', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'dashboard.html')); }); // Старая функциональность счетчика кликов (доступна по /counter) app.get('/counter', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'index.html')); }); // SaaS платформа app.get('/dashboard', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'dashboard.html')); }); // Health check app.get('/health', (req, res) => { res.json({ status: 'ok', timestamp: new Date().toISOString() }); }); // API Routes app.post('/api/auth/login', (req, res) => { const { email, password } = req.body; if (!email || !password) { return res.status(400).json({ error: 'Email and password are required' }); } db.get('SELECT * FROM users WHERE email = ?', [email], (err, user) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (!user) { return res.status(401).json({ error: 'Invalid credentials' }); } bcrypt.compare(password, user.password_hash, (err, isValid) => { if (err || !isValid) { return res.status(401).json({ error: 'Invalid credentials' }); } req.session.userId = user.id; req.session.username = user.username; res.json({ user: { id: user.id, username: user.username, email: user.email, subscription_type: user.subscription_type } }); }); }); }); app.post('/api/auth/register', (req, res) => { const { username, email, password } = req.body; if (!username || !email || !password) { return res.status(400).json({ error: 'Username, email and password are required' }); } if (password.length < 6) { return res.status(400).json({ error: 'Password must be at least 6 characters' }); } bcrypt.hash(password, 10, (err, hash) => { if (err) { return res.status(500).json({ error: 'Error hashing password' }); } db.run('INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)', [username, email, hash], function(err) { if (err) { if (err.code === 'SQLITE_CONSTRAINT') { return res.status(400).json({ error: 'Username or email already exists' }); } return res.status(500).json({ error: 'Database error' }); } req.session.userId = this.lastID; req.session.username = username; res.status(201).json({ user: { id: this.lastID, username, email, subscription_type: 'free' } }); }); }); }); app.post('/api/auth/logout', (req, res) => { req.session.destroy((err) => { if (err) { return res.status(500).json({ error: 'Error logging out' }); } res.json({ message: 'Logged out successfully' }); }); }); // Получение информации о пользователе app.get('/api/user/profile', requireAuth, (req, res) => { db.get('SELECT id, username, email, subscription_type, created_at FROM users WHERE id = ?', [req.session.userId], (err, user) => { if (err) { return res.status(500).json({ error: 'Database error' }); } res.json({ user }); }); }); // Управление credentials app.get('/api/user/credentials', requireAuth, (req, res) => { db.all(`SELECT id, service_name, credential_type, description, created_at FROM user_credentials WHERE user_id = ? AND is_active = 1`, [req.session.userId], (err, credentials) => { if (err) { return res.status(500).json({ error: 'Database error' }); } res.json({ credentials }); }); }); app.post('/api/user/credentials', requireAuth, (req, res) => { const { service_name, credential_type, value, description } = req.body; if (!service_name || !credential_type || !value) { return res.status(400).json({ error: 'Missing required fields' }); } // Простое "шифрование" - в продакшене используйте настоящее шифрование const encrypted_value = Buffer.from(value).toString('base64'); db.run(`INSERT OR REPLACE INTO user_credentials (user_id, service_name, credential_type, encrypted_value, description) VALUES (?, ?, ?, ?, ?)`, [req.session.userId, service_name, credential_type, encrypted_value, description], function(err) { if (err) { return res.status(500).json({ error: 'Database error' }); } res.status(201).json({ id: this.lastID, message: 'Credential saved successfully' }); }); }); app.delete('/api/user/credentials/:id', requireAuth, (req, res) => { const credentialId = req.params.id; db.run('DELETE FROM user_credentials WHERE id = ? AND user_id = ?', [credentialId, req.session.userId], function(err) { if (err) { return res.status(500).json({ error: 'Database error' }); } if (this.changes === 0) { return res.status(404).json({ error: 'Credential not found' }); } res.json({ message: 'Credential deleted successfully' }); }); }); // Шаблоны рабочих процессов app.get('/api/templates', (req, res) => { const { category, featured } = req.query; let query = 'SELECT * FROM workflow_templates WHERE is_active = 1'; const params = []; if (category) { query += ' AND category = ?'; params.push(category); } if (featured === 'true') { query += ' AND is_featured = 1'; } query += ' ORDER BY is_featured DESC, created_at DESC'; db.all(query, params, (err, templates) => { if (err) { return res.status(500).json({ error: 'Database error' }); } // Парсим JSON поля const processedTemplates = templates.map(template => ({ ...template, template_data: JSON.parse(template.template_data), required_credentials: JSON.parse(template.required_credentials) })); res.json({ templates: processedTemplates }); }); }); app.get('/api/templates/:id', (req, res) => { const templateId = req.params.id; db.get('SELECT * FROM workflow_templates WHERE id = ? AND is_active = 1', [templateId], (err, template) => { if (err) { return res.status(500).json({ error: 'Database error' }); } if (!template) { return res.status(404).json({ error: 'Template not found' }); } // Парсим JSON поля template.template_data = JSON.parse(template.template_data); template.required_credentials = JSON.parse(template.required_credentials); res.json({ template }); }); }); // Пользовательские рабочие процессы app.get('/api/user/workflows', requireAuth, (req, res) => { db.all(`SELECT uw.*, wt.name as template_name FROM user_workflows uw LEFT JOIN workflow_templates wt ON uw.template_id = wt.id WHERE uw.user_id = ? ORDER BY uw.created_at DESC`, [req.session.userId], (err, workflows) => { if (err) { return res.status(500).json({ error: 'Database error' }); } res.json({ workflows }); }); }); app.post('/api/user/workflows', requireAuth, (req, res) => { const { template_id, workflow_name, configuration } = req.body; if (!workflow_name) { return res.status(400).json({ error: 'Workflow name is required' }); } db.run(`INSERT INTO user_workflows (user_id, template_id, workflow_name, configuration) VALUES (?, ?, ?, ?)`, [req.session.userId, template_id || null, workflow_name, JSON.stringify(configuration || {})], function(err) { if (err) { return res.status(500).json({ error: 'Database error' }); } res.status(201).json({ id: this.lastID, message: 'Workflow created successfully' }); }); }); // Статистика использования app.get('/api/user/stats', requireAuth, (req, res) => { const queries = [ 'SELECT COUNT(*) as total_workflows FROM user_workflows WHERE user_id = ?', 'SELECT COUNT(*) as total_credentials FROM user_credentials WHERE user_id = ? AND is_active = 1', 'SELECT COUNT(*) as total_executions FROM usage_stats WHERE user_id = ?' ]; Promise.all(queries.map(query => new Promise((resolve, reject) => { db.get(query, [req.session.userId], (err, result) => { if (err) reject(err); else resolve(result); }); }))).then(results => { res.json({ stats: { total_workflows: results[0].total_workflows, total_credentials: results[1].total_credentials, total_executions: results[2].total_executions } }); }).catch(() => { res.status(500).json({ error: 'Database error' }); }); }); // Static файлы (после пользовательских маршрутов) app.use(express.static(path.join(__dirname, 'public'))); // Инициализация приложения async function startApp() { try { await initDatabase(); app.listen(port, () => { console.log(`🚀 SaaS Automation Platform running on http://localhost:${port}`); console.log(`📊 Health check available at: http://localhost:${port}/health`); console.log(`🔑 Demo credentials: demo@example.com / demo123`); }); } catch (error) { console.error('❌ Failed to initialize application:', error); process.exit(1); } } startApp();