aboutsummaryrefslogtreecommitdiffstats
path: root/backend/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'backend/index.js')
-rw-r--r--backend/index.js147
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}`);
+});