diff options
Diffstat (limited to 'backend/index.js')
-rw-r--r-- | backend/index.js | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/backend/index.js b/backend/index.js new file mode 100644 index 0000000..9f3047b --- /dev/null +++ b/backend/index.js @@ -0,0 +1,147 @@ +require('dotenv').config(); +const express = require('express'); +const rateLimit = require('express-rate-limit'); +const bodyParser = require('body-parser'); +const bcrypt = require('bcryptjs'); +const jwt = require('jsonwebtoken'); +const admin = require('firebase-admin'); +const cors = require('cors'); +const { body, validationResult } = require('express-validator'); + +// Initialize Express app +const app = express(); +app.use(bodyParser.json()); +app.use(cors()); // Enable CORS for all origins + +// Firebase Admin SDK setup +const serviceAccount = { + type: process.env.FIREBASE_TYPE, + project_id: process.env.FIREBASE_PROJECT_ID, + private_key_id: process.env.FIREBASE_PRIVATE_KEY_ID, + private_key: process.env.FIREBASE_PRIVATE_KEY.replace(/\\n/g, '\n'), + client_email: process.env.FIREBASE_CLIENT_EMAIL, + client_id: process.env.FIREBASE_CLIENT_ID, + auth_uri: process.env.FIREBASE_AUTH_URI, + token_uri: process.env.FIREBASE_TOKEN_URI, + auth_provider_x509_cert_url: process.env.FIREBASE_AUTH_PROVIDER_X509_CERT_URL, + client_x509_cert_url: process.env.FIREBASE_CLIENT_X509_CERT_URL +}; + +admin.initializeApp({ + credential: admin.credential.cert(serviceAccount), + storageBucket: process.env.FIREBASE_BUCKET, // Specify the bucket name here +}); + +const db = admin.firestore(); +const bucket = admin.storage().bucket(); // Access the bucket + +// Secret key for JWT +const JWT_SECRET = process.env.JWT_SECRET; + +// Rate Limiting middleware +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 after 15 minutes', +}); + +// Apply rate limiter to all requests +app.use(limiter); + +// Validation and sanitization middleware for registration +const registerValidationRules = () => [ + body('username') + .isLength({ min: 3 }).withMessage('Username must be at least 3 characters long') + .trim() + .escape(), + body('password') + .isLength({ min: 6 }).withMessage('Password must be at least 6 characters long') + .trim() +]; + +// Validation and sanitization middleware for login +const loginValidationRules = () => [ + body('username') + .isLength({ min: 3 }).withMessage('Username must be at least 3 characters long') + .trim() + .escape(), + body('password') + .isLength({ min: 6 }).withMessage('Password must be at least 6 characters long') + .trim() +]; + +// Middleware to check validation results +const validate = (req, res, next) => { + const errors = validationResult(req); + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }); + } + next(); +}; + +// Register route with validation and sanitization +app.post('/register', registerValidationRules(), validate, async (req, res, next) => { + const { username, password } = req.body; + + try { + const hashedPassword = await bcrypt.hash(password, 10); + await db.collection('users').doc(username).set({ + username, + password: hashedPassword + }); + res.status(201).send('User registered'); + } catch (error) { + next(error); // Pass the error to the error handling middleware + } +}); + +// Login route with validation and sanitization +app.post('/login', loginValidationRules(), validate, async (req, res, next) => { + const { username, password } = req.body; + + try { + const userDoc = await db.collection('users').doc(username).get(); + if (!userDoc.exists) { + return res.status(400).json({ error: 'Invalid username or password' }); + } + + const user = userDoc.data(); + const isPasswordValid = await bcrypt.compare(password, user.password); + if (!isPasswordValid) { + return res.status(400).json({ error: 'Invalid username or password' }); + } + + const token = jwt.sign({ username: user.username }, JWT_SECRET, { expiresIn: '1h' }); + res.json({ token }); + } catch (error) { + next(error); // Pass the error to the error handling middleware + } +}); + +// Fetch ads route +app.get('/ads', async (req, res, next) => { + try { + const adsSnapshot = await db.collection('ads').get(); + const ads = adsSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); + res.json(ads); + } catch (error) { + next(error); // Pass the error to the error handling middleware + } +}); + +// Centralized error handling middleware +app.use((err, req, res, next) => { + console.error('Server Error:', err); + res.status(err.status || 500).json({ + error: { + message: err.message || 'Internal Server Error', + stack: process.env.NODE_ENV === 'development' ? err.stack : {} + } + }); +}); + +// Start the server +const PORT = process.env.PORT || 5000; +app.listen(PORT, () => { + console.log(`Server running on http://localhost:${PORT}`); +}); |