Files
EMI-Backend/index.js
2025-02-20 23:27:13 -05:00

164 lines
5.9 KiB
JavaScript

// This is the backend main file for the Fellowship social app.
// It uses mongo as DB, and the connection url should be defined on the .env file
// Most of the API endpoints require authentication, which is checked by sessionChecker function.
// Load enviroment keys for stripe, mongo, vimeo, email
require('dotenv').config();
const express = require('express');
const bodyParser = require('body-parser');
const cookieParser = require('cookie-parser');
const cors = require('cors');
// Web Push Notifications
const webPush = require('web-push');
const publicVapidKey = process.env.PUBLIC_VAPID_KEY;
const privateVapidKey = process.env.PRIVATE_VAPID_KEY;
const webPushEmail = process.env.WEB_PUSH_EMAIL;
webPush.setVapidDetails('mailto:' + webPushEmail, publicVapidKey, privateVapidKey);
const { cookiesOptions } = require('./config/cookiesOptions');
const { corsOptions } = require('./config/corsOptions');
const { client_logger } = require('./utils/analyticsLogger.js');
const { getSessionId, getUserId, getProfileId } = require('./utils/sessionUtils.js');
// Server Application
const app = express();
const port = process.env.PORT || 3000; // Setup to run on heroku
app.use(cors(corsOptions));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cookieParser());
const DB = require("./mongoDB.js");
// Routes
const profileRoute = require('./routes/profile.js');
const postRoute = require('./routes/post.js');
const songsRoute = require('./routes/songs.js');
const paymentsRoute = require('./routes/payments.js');
const subsplashRoute = require('./routes/subsplash.js');
const bibleRoute = require('./routes/bible.js');
const sessionChecker = require('./middleware/sessionChecker');
const { signup, login, logout, resetPassword } = require('./auth/authEmail.js');
app.route('/signup').get(signup).post(signup);
app.route('/login').get(login).post(login);
app.get('/logout', logout);
app.route('/resetPassword').post(resetPassword);
// Get
DB.getDB.then((DB) => {
console.log("Main logic: DB connected!");
// route for Home-Page
app.get('/', sessionChecker, async (req, res) => {
if (req.userInfo) return res.json({
status: "ok",
userInfo: req.userInfo,
profileInfo: req.profileInfo
});
res.json({ status: "ok" });
});
// Check for an invitation for an email
app.get("/invite/:email", async (req, res) => {
try {
const email = req.params.email.trim().toLowerCase();
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return res.status(400).json({ status: "error", message: "Provide a valid email" });
}
// Check for an invitation
const invitation = await DB.getInvitation(email);
if (!invitation) {
return res.status(404).json({ status: "error", message: "No invitation found for this email" });
}
// Check if the user is already registered
const existingUser = await DB.getUser(email);
if (existingUser?._id) {
return res.status(409).json({ status: "error", message: "This user is already registered" });
}
// Capture event in logging
client_logger.capture({
distinctId: 'app_level',
event: `server@${req.method}@${req.originalUrl}`,
});
// Respond with invitation details
return res.json({ status: "ok", invitation });
} catch (error) {
console.error("Error processing invite check:", error);
return res.status(500).json({ status: "error", message: "Internal server error" });
}
});
// Change the active profile for the user.
app.post('/changeProfile', sessionChecker, async (req, res) => {
const user_sid = getUserId(req);
let profile = await DB.getProfile(req.body.profileid);
if (!profile) {
return res.json({
status: "profile does not exists",
});
}
let isUserOwnerOfTheProfile = profile.userid != user_sid;
if (isUserOwnerOfTheProfile) {
return res.json({
status: "profile does not belong to the logged user",
});
}
// Change the coockie for active profile, this will inform all future requests
res.cookie('profile_id', profile._id, cookiesOptions);
return res.json({
status: "ok",
...profile
});
});
// This is the endpoint to refresh the push notification token
app.post('/token/', sessionChecker, async (req, res) => {
const profileid = getProfileId(req);
let token = req.body.token
DB.setProfileToken(profileid, token);
return res.json({
status: "ok"
});
});
// Used for webpush notifications
app.post('/subscribe', sessionChecker, async (req, res) => {
const subscription = req.body;
res.status(201).json({});
const profileid = getProfileId(req);
if (profileid) await DB.setWebSubscription(profileid, subscription);
});
//Private Routes
app.use('/user', sessionChecker, profileRoute);
app.use('/post', sessionChecker, postRoute);
app.use('/payments', sessionChecker, paymentsRoute);
app.use('/bible', sessionChecker, bibleRoute);
app.use('/songs', sessionChecker, songsRoute);
//Public Routes
app.use('/subsplash', subsplashRoute);
// route for handling 404 requests(unavailable routes)
app.use(function (req, res, next) {
res.status(404).send("Sorry can't find that!")
});
app.listen(port, () => {
console.log(`Fellowship app running at http://localhost:${port}`);
}).on('error', (err) => {
console.error('Server failed to start:', err);
});
}).catch((err) => {
console.error('Error connecting to MongoDB:', err);
throw err;
});