This commit introduces Swagger API documentation for all endpoints in the application. - Installs and . - Configures Swagger in to generate and serve API documentation at . - Adds JSDoc-style Swagger annotations to all routes in and the directory (, , , , , ). - Defines a cookie-based security scheme for authenticated routes. This allows for interactive API documentation and testing via the endpoint.
729 lines
19 KiB
JavaScript
729 lines
19 KiB
JavaScript
var express = require('express')
|
|
var router = express.Router()
|
|
|
|
const DB = require("../mongoDB.js");
|
|
const Profile = require("../def/profile.js");
|
|
const Notifications = require("./../notifications.js");
|
|
const { getSessionId, getUserId, getProfileId } = require("./../utils/sessionUtils.js");
|
|
|
|
DB.getDB.then((DB) => {
|
|
|
|
const profileBelongsToUser = async (profileid, userid) => {
|
|
const profile = await DB.getProfileCache(profileid);
|
|
if (!profile) return false;
|
|
return profile.userid === String(userid);
|
|
}
|
|
|
|
/**
|
|
* @swagger
|
|
* tags:
|
|
* name: Profiles
|
|
* description: User profile management
|
|
*/
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/mine:
|
|
* get:
|
|
* summary: Get all profiles for the logged-in user
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* status:
|
|
* type: string
|
|
* profiles:
|
|
* type: array
|
|
* items:
|
|
* type: object
|
|
*/
|
|
router.get("/mine", async (req, res) => {
|
|
let userid = getUserId(req);
|
|
let profiles = await DB.getUserProfiles(userid);
|
|
return res.json({
|
|
status: "ok",
|
|
profiles
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/new:
|
|
* get:
|
|
* summary: (DEPRECATED) Create a new profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: query
|
|
* name: content
|
|
* required: true
|
|
* schema:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/new", async (req, res) => { //Deprecated please use route post("/")
|
|
let profile = {
|
|
userid: getUserId(req),
|
|
...req.query.content
|
|
};
|
|
let profileObj = new Profile(profile);
|
|
let r = await DB.newProfile(profileObj);
|
|
return res.json({
|
|
status: "ok",
|
|
...profileObj.toObj()
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user:
|
|
* post:
|
|
* summary: Create a new profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* content:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.post("/", async (req, res) => {
|
|
let profile = {
|
|
userid: getUserId(req),
|
|
...req.body.content
|
|
};
|
|
try {
|
|
let profileObj = new Profile(profile);
|
|
let r = await DB.newProfile(profileObj);
|
|
return res.json({
|
|
status: "ok",
|
|
...profileObj.toObj()
|
|
});
|
|
} catch (error) {
|
|
console.error("Error creating profile", error);
|
|
return res.json({
|
|
status: error,
|
|
});
|
|
}
|
|
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/invite:
|
|
* post:
|
|
* summary: Invite a new user by email
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* name:
|
|
* type: string
|
|
* email:
|
|
* type: string
|
|
* format: email
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
* 400:
|
|
* description: Bad request
|
|
*/
|
|
router.post("/invite", async (req, res) => {
|
|
try {
|
|
const userid = getUserId(req);
|
|
let { name, email } = req.body; // Destructuring for clarity
|
|
// Validate required fields
|
|
if (!name || !email) {
|
|
return res.status(400).json({ status: "Name and email are required" });
|
|
}
|
|
// Validate email format
|
|
email = email.trim().toLowerCase()
|
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
if (!emailRegex.test(email)) {
|
|
return res.status(400).json({ status: "Invalid email format" });
|
|
}
|
|
// Create new invitation, this returns a string if failed
|
|
let r = await DB.newInvitation(userid, name, email);
|
|
if (r instanceof String) {
|
|
// Handle failure response from DB.newInvitation
|
|
return res.status(400).json({
|
|
status: r,
|
|
message: `Failed to send invitation: ${r}`
|
|
});
|
|
}
|
|
// Handle response from DB.newInvitation
|
|
// Send email invitation
|
|
let senderProfile = await DB.getProfile(getProfileId(req));
|
|
Notifications.youHaveAnInvitation(name, email, senderProfile);
|
|
return res.status(200).json({
|
|
status: "ok",
|
|
message: `Invitation sent to ${name} (${email})`
|
|
});
|
|
} catch (error) {
|
|
console.error("Error during invitation process:", error);
|
|
return res.status(500).json({ status: "error", message: "Something went wrong, please try again later" });
|
|
}
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/invite/{email}:
|
|
* get:
|
|
* summary: Get invitation details for an email
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: email
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* format: email
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/invite/:email", async (req, res) => {
|
|
const userid = getUserId(req);
|
|
const email = req.params.email;
|
|
//validate email?
|
|
if (!email) return res.json({ status: "provide valid email" });
|
|
let r = await DB.getInvitation(email);
|
|
if (!r) return res.json({ status: "no invitation found with that email" });
|
|
let isUserAlreadyRegistered = await DB.getUser(email);
|
|
if (isUserAlreadyRegistered && isUserAlreadyRegistered._id) return res.json({ status: "This user is already registered" });
|
|
return res.json({ status: "ok", ...r });
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups:
|
|
* get:
|
|
* summary: Get a list of all groups
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/groups", async (req, res) => {
|
|
let groups = await DB.getGroups();
|
|
return res.json({
|
|
status: "ok",
|
|
groups
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/following:
|
|
* get:
|
|
* summary: Get a list of groups the current profile is following
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/groups/following", async (req, res) => {
|
|
const profileId = getProfileId(req);
|
|
let groups = await DB.getFollowingGroups(profileId);
|
|
return res.json({
|
|
status: "ok",
|
|
groups
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups:
|
|
* post:
|
|
* summary: Create a new group
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.post("/groups", async (req, res) => {
|
|
let profile = {
|
|
userid: getUserId(req),
|
|
isGroup: true,
|
|
...req.body
|
|
};
|
|
let profileObj = new Profile(profile);
|
|
DB.newProfile(profileObj)
|
|
return res.json({
|
|
status: "ok",
|
|
...profileObj.toObj()
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/courses:
|
|
* get:
|
|
* summary: Get a list of all courses
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/courses", async (req, res) => {
|
|
let groups = await DB.getCourses();
|
|
return res.json({
|
|
status: "ok",
|
|
groups
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/accept:
|
|
* post:
|
|
* summary: Accept a request to join a private group
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* groupid:
|
|
* type: string
|
|
* profileid:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.post("/groups/accept", async (req, res) => {
|
|
//This function should be called to accept the join request
|
|
//of an user that attempt to join a private group.
|
|
const groupid = getProfileId(req); //It needs to have this profile context
|
|
const groupidBody = req.body.groupid ? DB.ObjectID(req.body.groupid) : undefined;
|
|
if (groupidBody && groupid != groupidBody && !DB.isOwnerOfGroup(groupid, groupidBody)) {
|
|
return res.json({
|
|
status: "Only group owner can accept new subscribers"
|
|
});
|
|
}
|
|
const profileAcepted = DB.ObjectID(req.body.profileid);
|
|
DB.acceptGroupJoinReq(profileAcepted, groupidBody || groupid);
|
|
//Send Notification to accepted user
|
|
Notifications.yourGroupRequestAccepted(profileAcepted, groupidBody || groupid)
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/reject:
|
|
* post:
|
|
* summary: Reject a request to join a private group
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* groupid:
|
|
* type: string
|
|
* profileid:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.post("/groups/reject", async (req, res) => {
|
|
//This function should be called to reject the join request
|
|
//of an user that attempt to join a private group.
|
|
const groupid = getProfileId(req); //It needs to have this profile context
|
|
const groupidBody = req.body.groupid ? DB.ObjectID(req.body.groupid) : undefined;
|
|
if (groupidBody && groupid != groupidBody && !DB.isOwnerOfGroup(groupid, groupidBody)) {
|
|
return res.json({
|
|
status: "Only group owner can reject new subscribers"
|
|
});
|
|
}
|
|
const profileAcepted = DB.ObjectID(req.body.profileid);
|
|
DB.rejectGroupJoinReq(profileAcepted, groupidBody || groupid);
|
|
//Add Notification to rejected user
|
|
//Notifications.yourGroupHasARequest(profileAcepted, groupidBody || groupid)
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/search:
|
|
* get:
|
|
* summary: Search for groups
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: query
|
|
* name: query
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* - in: query
|
|
* name: courses
|
|
* schema:
|
|
* type: boolean
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/groups/search", async (req, res) => {
|
|
let query = req.query.query;
|
|
let coursesB = req.query.courses ? true : false;
|
|
let groups = await DB.searchGroups(query, coursesB);
|
|
return res.json({
|
|
status: "ok",
|
|
groups
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/{id}:
|
|
* get:
|
|
* summary: Get details for a specific group
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/groups/:id", async (req, res) => {
|
|
const groupid = req.params.id;
|
|
let groups = await DB.getGroup(groupid);
|
|
return res.json({
|
|
status: "ok",
|
|
groups
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/{id}/subscribe:
|
|
* get:
|
|
* summary: Subscribe to a group
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/groups/:id/subscribe", async (req, res) => {
|
|
const groupid = req.params.id;
|
|
const profileid = getProfileId(req);
|
|
const isPrivate = await DB.isGroupPrivate(groupid);
|
|
DB.subscribeToGroup(profileid, groupid, isPrivate);
|
|
//Add notification to group owner
|
|
if (isPrivate) Notifications.yourGroupHasARequest(profileid, groupid)
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/groups/{id}/unsubscribe:
|
|
* get:
|
|
* summary: Unsubscribe from a group
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/groups/:id/unsubscribe", async (req, res) => {
|
|
const groupid = req.params.id;
|
|
const profileid = getProfileId(req);
|
|
DB.unsubscribeToGroup(profileid, groupid);
|
|
//Add notification to group owner
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/search:
|
|
* get:
|
|
* summary: Search for profiles
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: query
|
|
* name: query
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/search", async (req, res) => {
|
|
let query = req.query.query;
|
|
let profiles = await DB.searchProfile(query);
|
|
return res.json({
|
|
status: "ok",
|
|
profiles
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/setData:
|
|
* post:
|
|
* summary: Set custom data for a profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* key:
|
|
* type: string
|
|
* value:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.post("/setData", (req, res) => {
|
|
const key = req.body.key;
|
|
const value = req.body.value;
|
|
const profileid = getProfileId(req);
|
|
DB.setData(profileid, key, value);
|
|
return res.json({
|
|
status: "ok",
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/myProfile:
|
|
* post:
|
|
* summary: Update the current user's profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* requestBody:
|
|
* required: true
|
|
* content:
|
|
* application/json:
|
|
* schema:
|
|
* type: object
|
|
* properties:
|
|
* profile:
|
|
* type: object
|
|
* data:
|
|
* type: object
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.post("/myProfile", async (req, res) => {
|
|
let profile = {
|
|
userid: getUserId(req),
|
|
profile: req.body.profile,
|
|
data: req.body.data
|
|
};
|
|
let profileObj = new Profile(profile); //validates profile
|
|
DB.updateProfile(getProfileId(req), profileObj);
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/{id}:
|
|
* get:
|
|
* summary: Get a specific profile by ID
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/:id", async (req, res) => {
|
|
let profileId = req.params.id;
|
|
let profile = await DB.getProfile(profileId);
|
|
return res.json({
|
|
status: "ok",
|
|
...profile
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/{id}:
|
|
* delete:
|
|
* summary: Delete a profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.delete("/:id", async (req, res) => {
|
|
const profileId = req.params.id;
|
|
const userid = getUserId(req);
|
|
if (!await profileBelongsToUser(profileId, userid))
|
|
return res.json({
|
|
status: "This profile is not yours."
|
|
});
|
|
await DB.removeProfile(profileId);
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/{id}/follow:
|
|
* get:
|
|
* summary: Follow a profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/:id/follow", async (req, res) => {
|
|
let followProfileId = req.params.id;
|
|
const profileid = getProfileId(req);
|
|
DB.followProfile(profileid, followProfileId);
|
|
//Add notification to user
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
/**
|
|
* @swagger
|
|
* /user/{id}/unfollow:
|
|
* get:
|
|
* summary: Unfollow a profile
|
|
* tags: [Profiles]
|
|
* security:
|
|
* - cookieAuth: []
|
|
* parameters:
|
|
* - in: path
|
|
* name: id
|
|
* required: true
|
|
* schema:
|
|
* type: string
|
|
* responses:
|
|
* 200:
|
|
* description: OK
|
|
*/
|
|
router.get("/:id/unfollow", async (req, res) => {
|
|
let followProfileId = req.params.id;
|
|
const profileid = getProfileId(req);
|
|
DB.unfollowProfile(profileid, followProfileId);
|
|
//Add notification to user
|
|
return res.json({
|
|
status: "ok"
|
|
});
|
|
});
|
|
|
|
});
|
|
|
|
module.exports = router |