const express = require('express'); const { Pool } = require('pg'); const cors = require('cors'); const bcrypt = require('bcryptjs'); const jwt = require('jsonwebtoken'); require('dotenv').config(); const app = express(); const PORT = process.env.PORT || 80; // PostgreSQL connection const pool = new Pool({ connectionString: process.env.DATABASE_URL, ssl: process.env.DATABASE_URL?.includes('amazonaws.com') ? { rejectUnauthorized: false } : false }); // Middleware app.use(cors({ origin: true, // Allow all origins in development credentials: true, methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], allowedHeaders: ['Content-Type', 'Authorization'], exposedHeaders: ['Content-Length', 'Content-Type'], maxAge: 86400 })); app.use(express.json()); // Handle preflight requests app.options('*', cors()); // Health check route app.get('/', (req, res) => { res.json({ status: 'ok', service: 'Filamenteka API' }); }); // JWT middleware const authenticateToken = (req, res, next) => { const authHeader = req.headers['authorization']; const token = authHeader && authHeader.split(' ')[1]; if (!token) { return res.status(401).json({ error: 'Unauthorized' }); } jwt.verify(token, process.env.JWT_SECRET || 'your-secret-key', (err, user) => { if (err) { console.error('JWT verification error:', err); return res.status(403).json({ error: 'Invalid token' }); } req.user = user; next(); }); }; // Auth endpoints app.post('/api/login', async (req, res) => { const { username, password } = req.body; // For now, simple hardcoded admin check if (username === 'admin' && password === process.env.ADMIN_PASSWORD) { const token = jwt.sign({ username }, process.env.JWT_SECRET || 'your-secret-key', { expiresIn: '24h' }); res.json({ token }); } else { res.status(401).json({ error: 'Invalid credentials' }); } }); // Colors endpoints app.get('/api/colors', async (req, res) => { try { const result = await pool.query('SELECT * FROM colors ORDER BY name'); res.json(result.rows); } catch (error) { console.error('Error fetching colors:', error); res.status(500).json({ error: 'Failed to fetch colors' }); } }); app.post('/api/colors', authenticateToken, async (req, res) => { const { name, hex } = req.body; try { const result = await pool.query( 'INSERT INTO colors (name, hex) VALUES ($1, $2) RETURNING *', [name, hex] ); res.json(result.rows[0]); } catch (error) { console.error('Error creating color:', error); res.status(500).json({ error: 'Failed to create color' }); } }); app.put('/api/colors/:id', authenticateToken, async (req, res) => { const { id } = req.params; const { name, hex } = req.body; try { const result = await pool.query( 'UPDATE colors SET name = $1, hex = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $3 RETURNING *', [name, hex, id] ); res.json(result.rows[0]); } catch (error) { console.error('Error updating color:', error); res.status(500).json({ error: 'Failed to update color' }); } }); app.delete('/api/colors/:id', authenticateToken, async (req, res) => { const { id } = req.params; try { await pool.query('DELETE FROM colors WHERE id = $1', [id]); res.json({ success: true }); } catch (error) { console.error('Error deleting color:', error); res.status(500).json({ error: 'Failed to delete color' }); } }); // Filaments endpoints (PUBLIC - no auth required) app.get('/api/filaments', async (req, res) => { console.log('Filaments request headers:', req.headers); try { const result = await pool.query('SELECT * FROM filaments ORDER BY created_at DESC'); res.json(result.rows); } catch (error) { console.error('Error fetching filaments:', error); res.status(500).json({ error: 'Failed to fetch filaments' }); } }); app.post('/api/filaments', authenticateToken, async (req, res) => { const { brand, tip, finish, boja, boja_hex, refill, vakum, otvoreno, kolicina, cena } = req.body; try { const result = await pool.query( `INSERT INTO filaments (brand, tip, finish, boja, boja_hex, refill, vakum, otvoreno, kolicina, cena) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING *`, [brand, tip, finish, boja, boja_hex, refill, vakum, otvoreno, kolicina || 1, cena] ); res.json(result.rows[0]); } catch (error) { console.error('Error creating filament:', error); res.status(500).json({ error: 'Failed to create filament' }); } }); app.put('/api/filaments/:id', authenticateToken, async (req, res) => { const { id } = req.params; const { brand, tip, finish, boja, boja_hex, refill, vakum, otvoreno, kolicina, cena } = req.body; try { const result = await pool.query( `UPDATE filaments SET brand = $1, tip = $2, finish = $3, boja = $4, boja_hex = $5, refill = $6, vakum = $7, otvoreno = $8, kolicina = $9, cena = $10, updated_at = CURRENT_TIMESTAMP WHERE id = $11 RETURNING *`, [brand, tip, finish, boja, boja_hex, refill, vakum, otvoreno, kolicina || 1, cena, id] ); res.json(result.rows[0]); } catch (error) { console.error('Error updating filament:', error); res.status(500).json({ error: 'Failed to update filament' }); } }); app.delete('/api/filaments/:id', authenticateToken, async (req, res) => { const { id } = req.params; try { await pool.query('DELETE FROM filaments WHERE id = $1', [id]); res.json({ success: true }); } catch (error) { console.error('Error deleting filament:', error); res.status(500).json({ error: 'Failed to delete filament' }); } }); app.listen(PORT, () => { console.log(`Server running on port ${PORT}`); });