Spaces:
Configuration error
Configuration error
| const express = require('express'); | |
| const mongoose = require('mongoose'); | |
| const cors = require('cors'); | |
| const bcrypt = require('bcryptjs'); | |
| const jwt = require('jsonwebtoken'); | |
| const multer = require('multer'); | |
| const path = require('path'); | |
| const fs = require('fs'); | |
| require('dotenv').config(); | |
| const app = express(); | |
| const PORT = process.env.PORT || 3001; | |
| const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-here'; | |
| // Middleware | |
| app.use(cors()); | |
| app.use(express.json({ limit: '50mb' })); | |
| app.use(express.urlencoded({ extended: true, limit: '50mb' })); | |
| // Serve static files | |
| app.use('/uploads', express.static(path.join(__dirname, 'uploads'))); | |
| // Ensure uploads directory exists | |
| const uploadsDir = path.join(__dirname, 'uploads'); | |
| if (!fs.existsSync(uploadsDir)) { | |
| fs.mkdirSync(uploadsDir, { recursive: true }); | |
| } | |
| // Multer configuration for file uploads | |
| const storage = multer.diskStorage({ | |
| destination: (req, file, cb) => { | |
| cb(null, 'uploads/'); | |
| }, | |
| filename: (req, file, cb) => { | |
| const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); | |
| cb(null, uniqueSuffix + path.extname(file.originalname)); | |
| } | |
| }); | |
| const upload = multer({ | |
| storage: storage, | |
| limits: { fileSize: 10 * 1024 * 1024 }, // 10MB limit | |
| fileFilter: (req, file, cb) => { | |
| if (file.mimetype.startsWith('image/')) { | |
| cb(null, true); | |
| } else { | |
| cb(new Error('Only image files are allowed')); | |
| } | |
| } | |
| }); | |
| // MongoDB Connection | |
| mongoose.connect(process.env.MONGODB_URI || 'mongodb://localhost:27017/pencere-hesaplama', { | |
| useNewUrlParser: true, | |
| useUnifiedTopology: true, | |
| }).then(() => { | |
| console.log('Connected to MongoDB'); | |
| }).catch(err => { | |
| console.error('MongoDB connection error:', err); | |
| }); | |
| // Schemas | |
| const userSchema = new mongoose.Schema({ | |
| username: { type: String, required: true, unique: true }, | |
| password: { type: String, required: true }, | |
| email: { type: String, required: true, unique: true }, | |
| role: { type: String, enum: ['admin', 'user'], default: 'user' }, | |
| createdAt: { type: Date, default: Date.now } | |
| }); | |
| const companySchema = new mongoose.Schema({ | |
| name: { type: String, required: true }, | |
| logo: { type: String }, | |
| address: { type: String }, | |
| phone: { type: String }, | |
| email: { type: String }, | |
| website: { type: String }, | |
| description: { type: String }, | |
| createdAt: { type: Date, default: Date.now } | |
| }); | |
| const systemSchema = new mongoose.Schema({ | |
| name: { type: String, required: true }, | |
| image: { type: String }, | |
| parts: [{ | |
| name: { type: String, required: true }, | |
| type: { type: String, enum: ['yatay', 'dikey', 'cam'], required: true }, | |
| quantity: { type: Number, required: true, default: 1 }, | |
| reduction: { type: Number, default: 0 }, | |
| description: { type: String }, | |
| image: { type: String }, | |
| advancedFormula: { | |
| formula: { type: String }, | |
| type: { type: String } | |
| }, | |
| glassFormulas: { | |
| horizontal: { type: String }, | |
| horizontalType: { type: String }, | |
| vertical: { type: String }, | |
| verticalType: { type: String } | |
| }, | |
| createdAt: { type: Date, default: Date.now } | |
| }], | |
| createdAt: { type: Date, default: Date.now }, | |
| updatedAt: { type: Date, default: Date.now } | |
| }); | |
| const customerSchema = new mongoose.Schema({ | |
| name: { type: String, required: true, unique: true }, | |
| phone: { type: String }, | |
| email: { type: String }, | |
| address: { type: String }, | |
| createdAt: { type: Date, default: Date.now }, | |
| updatedAt: { type: Date, default: Date.now } | |
| }); | |
| const positionSchema = new mongoose.Schema({ | |
| customerId: { type: mongoose.Schema.Types.ObjectId, ref: 'Customer', required: false }, | |
| projectId: { type: mongoose.Schema.Types.ObjectId, ref: 'Project' }, | |
| systemId: { type: mongoose.Schema.Types.ObjectId, ref: 'System', required: true }, | |
| projectName: { type: String }, | |
| width: { type: Number, required: true }, | |
| height: { type: Number, required: true }, | |
| quantity: { type: Number, required: true, default: 1 }, | |
| customerName: { type: String }, | |
| horizontalParts: [{ | |
| name: { type: String }, | |
| size: { type: Number }, | |
| quantity: { type: Number }, | |
| formulaUsed: { type: Boolean } | |
| }], | |
| verticalParts: [{ | |
| name: { type: String }, | |
| size: { type: Number }, | |
| quantity: { type: Number }, | |
| formulaUsed: { type: Boolean } | |
| }], | |
| glassParts: [{ | |
| name: { type: String }, | |
| quantity: { type: Number }, | |
| size: { type: String }, | |
| width: { type: Number }, | |
| height: { type: Number }, | |
| totalArea: { type: String } | |
| }], | |
| glassInfo: { | |
| width: { type: Number }, | |
| height: { type: Number }, | |
| totalArea: { type: String } | |
| }, | |
| pozNumber: { type: Number }, | |
| createdAt: { type: Date, default: Date.now }, | |
| updatedAt: { type: Date, default: Date.now } | |
| }); | |
| const projectSchema = new mongoose.Schema({ | |
| name: { type: String, required: true }, | |
| customerId: { type: mongoose.Schema.Types.ObjectId, ref: 'Customer' }, | |
| description: { type: String }, | |
| startDate: { type: Date }, | |
| endDate: { type: Date }, | |
| status: { type: String, enum: ['active', 'completed', 'cancelled'], default: 'active' }, | |
| createdAt: { type: Date, default: Date.now }, | |
| updatedAt: { type: Date, default: Date.now } | |
| }); | |
| const pdfSettingsSchema = new mongoose.Schema({ | |
| settings: { type: Object, required: true }, | |
| type: { type: String, enum: ['global', 'perpage'], required: true }, | |
| isActive: { type: Boolean, default: true }, | |
| createdAt: { type: Date, default: Date.now }, | |
| updatedAt: { type: Date, default: Date.now } | |
| }); | |
| // Models | |
| const User = mongoose.model('User', userSchema); | |
| const Company = mongoose.model('Company', companySchema); | |
| const System = mongoose.model('System', systemSchema); | |
| const Customer = mongoose.model('Customer', customerSchema); | |
| const Position = mongoose.model('Position', positionSchema); | |
| const Project = mongoose.model('Project', projectSchema); | |
| const PDFSettings = mongoose.model('PDFSettings', pdfSettingsSchema); | |
| // Authentication 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: 'Access token required' }); | |
| } | |
| jwt.verify(token, JWT_SECRET, (err, user) => { | |
| if (err) { | |
| return res.status(403).json({ error: 'Invalid token' }); | |
| } | |
| req.user = user; | |
| next(); | |
| }); | |
| }; | |
| // Helper Functions | |
| const generatePozNumber = async (customerId = null) => { | |
| const query = customerId ? { customerId } : { customerId: null }; | |
| const lastPosition = await Position.findOne(query).sort({ pozNumber: -1 }); | |
| return lastPosition ? lastPosition.pozNumber + 1 : 1; | |
| }; | |
| // Auth Routes | |
| app.post('/api/auth/register', async (req, res) => { | |
| try { | |
| const { username, email, password } = req.body; | |
| if (!username || !email || !password) { | |
| return res.status(400).json({ error: 'All fields are required' }); | |
| } | |
| const existingUser = await User.findOne({ | |
| $or: [{ username }, { email }] | |
| }); | |
| if (existingUser) { | |
| return res.status(400).json({ error: 'Username or email already exists' }); | |
| } | |
| const hashedPassword = await bcrypt.hash(password, 10); | |
| const user = new User({ username, email, password: hashedPassword }); | |
| await user.save(); | |
| const token = jwt.sign( | |
| { userId: user._id, username: user.username, role: user.role }, | |
| JWT_SECRET, | |
| { expiresIn: '24h' } | |
| ); | |
| res.status(201).json({ | |
| message: 'User created successfully', | |
| token, | |
| user: { id: user._id, username: user.username, email: user.email, role: user.role } | |
| }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.post('/api/auth/login', async (req, res) => { | |
| try { | |
| const { username, password } = req.body; | |
| if (!username || !password) { | |
| return res.status(400).json({ error: 'Username and password are required' }); | |
| } | |
| const user = await User.findOne({ username }); | |
| if (!user) { | |
| return res.status(401).json({ error: 'Invalid credentials' }); | |
| } | |
| const isMatch = await bcrypt.compare(password, user.password); | |
| if (!isMatch) { | |
| return res.status(401).json({ error: 'Invalid credentials' }); | |
| } | |
| const token = jwt.sign( | |
| { userId: user._id, username: user.username, role: user.role }, | |
| JWT_SECRET, | |
| { expiresIn: '24h' } | |
| ); | |
| res.json({ | |
| message: 'Login successful', | |
| token, | |
| user: { id: user._id, username: user.username, email: user.email, role: user.role } | |
| }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Company Routes | |
| app.get('/api/company', async (req, res) => { | |
| try { | |
| let company = await Company.findOne(); | |
| if (!company) { | |
| company = new Company({ | |
| name: 'Firma Adı', | |
| createdAt: new Date() | |
| }); | |
| await company.save(); | |
| } | |
| res.json(company); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.put('/api/company', upload.single('logo'), async (req, res) => { | |
| try { | |
| const updateData = { ...req.body }; | |
| if (req.file) { | |
| updateData.logo = `/uploads/${req.file.filename}`; | |
| } | |
| const company = await Company.findOneAndUpdate( | |
| {}, | |
| { $set: updateData, updatedAt: new Date() }, | |
| { new: true, upsert: true } | |
| ); | |
| res.json(company); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // System Routes | |
| app.get('/api/systems', async (req, res) => { | |
| try { | |
| const systems = await System.find().sort({ createdAt: -1 }); | |
| res.json(systems); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.post('/api/systems', upload.single('image'), async (req, res) => { | |
| try { | |
| const systemData = { ...req.body }; | |
| if (req.file) { | |
| systemData.image = `/uploads/${req.file.filename}`; | |
| } | |
| const system = new System(systemData); | |
| await system.save(); | |
| res.status(201).json(system); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.put('/api/systems/:id', upload.single('image'), async (req, res) => { | |
| try { | |
| const updateData = { ...req.body, updatedAt: new Date() }; | |
| if (req.file) { | |
| updateData.image = `/uploads/${req.file.filename}`; | |
| } | |
| const system = await System.findByIdAndUpdate( | |
| req.params.id, | |
| updateData, | |
| { new: true } | |
| ); | |
| if (!system) { | |
| return res.status(404).json({ error: 'System not found' }); | |
| } | |
| res.json(system); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.delete('/api/systems/:id', async (req, res) => { | |
| try { | |
| const system = await System.findByIdAndDelete(req.params.id); | |
| if (!system) { | |
| return res.status(404).json({ error: 'System not found' }); | |
| } | |
| // Delete associated image file | |
| if (system.image && fs.existsSync(path.join(__dirname, system.image))) { | |
| fs.unlinkSync(path.join(__dirname, system.image)); | |
| } | |
| res.json({ message: 'System deleted successfully' }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Customer Routes | |
| app.get('/api/customers', async (req, res) => { | |
| try { | |
| const customers = await Customer.find().sort({ name: 1 }); | |
| res.json(customers); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.post('/api/customers', async (req, res) => { | |
| try { | |
| const customer = new Customer(req.body); | |
| await customer.save(); | |
| res.status(201).json(customer); | |
| } catch (error) { | |
| if (error.code === 11000) { | |
| res.status(400).json({ error: 'Customer name already exists' }); | |
| } else { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| } | |
| }); | |
| app.put('/api/customers/:id', async (req, res) => { | |
| try { | |
| const customer = await Customer.findByIdAndUpdate( | |
| req.params.id, | |
| { $set: req.body, updatedAt: new Date() }, | |
| { new: true } | |
| ); | |
| if (!customer) { | |
| return res.status(404).json({ error: 'Customer not found' }); | |
| } | |
| res.json(customer); | |
| } catch (error) { | |
| if (error.code === 11000) { | |
| res.status(400).json({ error: 'Customer name already exists' }); | |
| } else { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| } | |
| }); | |
| app.delete('/api/customers/:id', async (req, res) => { | |
| try { | |
| const customer = await Customer.findByIdAndDelete(req.params.id); | |
| if (!customer) { | |
| return res.status(404).json({ error: 'Customer not found' }); | |
| } | |
| // Delete associated positions | |
| await Position.deleteMany({ customerId: req.params.id }); | |
| res.json({ message: 'Customer deleted successfully' }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Position Routes | |
| app.get('/api/positions', async (req, res) => { | |
| try { | |
| const { customerId } = req.query; | |
| const query = customerId ? { customerId } : { customerId: null }; | |
| const positions = await Position.find(query) | |
| .populate('systemId', 'name image parts') | |
| .sort({ createdAt: -1 }); | |
| res.json(positions); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.post('/api/positions', async (req, res) => { | |
| try { | |
| const positionData = { ...req.body }; | |
| if (!positionData.pozNumber) { | |
| positionData.pozNumber = await generatePozNumber(positionData.customerId); | |
| } | |
| const position = new Position(positionData); | |
| await position.save(); | |
| await position.populate('systemId', 'name image parts'); | |
| res.status(201).json(position); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.put('/api/positions/:id', async (req, res) => { | |
| try { | |
| const position = await Position.findByIdAndUpdate( | |
| req.params.id, | |
| { $set: req.body, updatedAt: new Date() }, | |
| { new: true } | |
| ).populate('systemId', 'name image parts'); | |
| if (!position) { | |
| return res.status(404).json({ error: 'Position not found' }); | |
| } | |
| res.json(position); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.delete('/api/positions/:id', async (req, res) => { | |
| try { | |
| const position = await Position.findByIdAndDelete(req.params.id); | |
| if (!position) { | |
| return res.status(404).json({ error: 'Position not found' }); | |
| } | |
| res.json({ message: 'Position deleted successfully' }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // PDF Settings Routes | |
| app.get('/api/pdf-settings', async (req, res) => { | |
| try { | |
| const { type } = req.query; | |
| const query = type ? { type, isActive: true } : { isActive: true }; | |
| const settings = await PDFSettings.findOne(query).sort({ createdAt: -1 }); | |
| res.json(settings || { settings: {} }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.put('/api/pdf-settings', async (req, res) => { | |
| try { | |
| const { type = 'global', settings } = req.body; | |
| // Deactivate other settings of the same type | |
| await PDFSettings.updateMany({ type }, { isActive: false }); | |
| const pdfSettings = new PDFSettings({ type, settings, isActive: true }); | |
| await pdfSettings.save(); | |
| res.status(201).json(pdfSettings); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Backup Routes | |
| app.get('/api/backup', async (req, res) => { | |
| try { | |
| const backup = { | |
| timestamp: new Date().toISOString(), | |
| systems: await System.find(), | |
| customers: await Customer.find(), | |
| positions: await Position.find(), | |
| company: await Company.findOne(), | |
| pdfSettings: await PDFSettings.find(), | |
| projects: await Project.find() | |
| }; | |
| res.setHeader('Content-Type', 'application/json'); | |
| res.setHeader('Content-Disposition', `attachment; filename="backup-${new Date().toISOString().split('T')[0]}.json"`); | |
| res.json(backup); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| app.post('/api/restore', async (req, res) => { | |
| try { | |
| const backup = req.body; | |
| // Clear existing data | |
| await Promise.all([ | |
| System.deleteMany({}), | |
| Customer.deleteMany({}), | |
| Position.deleteMany({}), | |
| Company.deleteMany({}), | |
| PDFSettings.deleteMany({}), | |
| Project.deleteMany({}) | |
| ]); | |
| // Restore data | |
| if (backup.systems && backup.systems.length > 0) { | |
| await System.insertMany(backup.systems); | |
| } | |
| if (backup.customers && backup.customers.length > 0) { | |
| await Customer.insertMany(backup.customers); | |
| } | |
| if (backup.positions && backup.positions.length > 0) { | |
| await Position.insertMany(backup.positions); | |
| } | |
| if (backup.company) { | |
| await Company.create(backup.company); | |
| } | |
| if (backup.pdfSettings && backup.pdfSettings.length > 0) { | |
| await PDFSettings.insertMany(backup.pdfSettings); | |
| } | |
| if (backup.projects && backup.projects.length > 0) { | |
| await Project.insertMany(backup.projects); | |
| } | |
| res.json({ message: 'Data restored successfully' }); | |
| } catch (error) { | |
| res.status(500).json({ error: error.message }); | |
| } | |
| }); | |
| // Error handling middleware | |
| app.use((err, req, res, next) => { | |
| console.error(err.stack); | |
| res.status(500).json({ error: 'Something went wrong!' }); | |
| }); | |
| // Start server | |
| app.listen(PORT, () => { | |
| console.log(`Server is running on port ${PORT}`); | |
| console.log(`API documentation: http://localhost:${PORT}/api`); | |
| }); |