Files
EMI-Backend/routes/profile.js
Adolfo Reyna 0b36db9b33 docs: Update Swagger documentation for Profile endpoints
Updated the Swagger documentation for various Profile endpoints to accurately
reflect their return types, including arrays of Profile objects and detailed
schemas for specific responses.
2025-07-17 10:32:22 -04:00

916 lines
24 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
* components:
* schemas:
* Profile:
* type: object
* properties:
* userid:
* type: string
* description: The ID of the user associated with the profile.
* profile:
* type: object
* properties:
* firstName:
* type: string
* lastName:
* type: string
* photo:
* type: string
* location:
* type: string
* language:
* type: string
* status:
* type: string
* description:
* type: string
* data:
* type: object
* description: Additional custom data for the profile.
* username:
* type: string
* following:
* type: array
* items:
* type: string
* description: List of profile IDs this profile is following.
* lastUpdate:
* type: string
* format: date-time
* newsFeedCache:
* type: array
* items:
* type: object
* notifications:
* type: array
* items:
* type: object
* isGroup:
* type: boolean
* isCourse:
* type: boolean
* isPrivate:
* type: boolean
* isChat:
* type: boolean
* subscribed:
* type: object
* description: Users subscribed to this group (if isGroup is true).
* pending:
* type: object
* description: Pending subscription requests for private groups.
*/
/**
* @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:
* $ref: '#/components/schemas/Profile'
*/
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
* $ref: '#/components/schemas/Profile'
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Profile'
*/
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:
* $ref: '#/components/schemas/Profile'
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* invitation:
* $ref: '#/components/schemas/Profile'
*
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* groups:
* type: array
* items:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* groups:
* type: array
* items:
* $ref: '#/components/schemas/Profile'
*/
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:
* $ref: '#/components/schemas/Profile'
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* groups:
* type: array
* items:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* groups:
* type: array
* items:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* groups:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
* profiles:
* type: array
* items:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
*/
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:
* $ref: '#/components/schemas/Profile'
* data:
* type: object
* responses:
* 200:
* description: OK
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
*/
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
* content:
* application/json:
* schema:
* $ref: '#/components/schemas/Profile'
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
*/
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
* content:
* application/json:
* schema:
* type: object
* properties:
* status:
* type: string
*/
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