Skip to content

Add page to see followers & who people are following (Backend) #29

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions api/v1/db/UserManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,8 @@ class UserManager {
lastGuidelinesRead: current_time,
privateProfile: false,
allowFollowingView: false,
profileHideFollowing: false,
profileHideFollowers: false,
is_studio,
});

Expand Down Expand Up @@ -2059,7 +2061,8 @@ class UserManager {
},
{
$match: {
"follower.banned": false
"follower.banned": false,
"follower.profileHideFollowing": { $ne: true }
}
},
{
Expand Down Expand Up @@ -2147,10 +2150,21 @@ class UserManager {
*/
async getFollowerCount(username) {
const result = await this.users.findOne({username: username});

return result.followers;
}

/**
* Get the amount of people a user is following
* @param {string} username Username of the user
* @returns {Promise<number>} Amount of people the user is following
*/
async getFollowingCount(username) {
const result = await this.users.findOne({ username: username });

return result.following;
}

/**
* Send a message
* @param {string} receiver ID of the person receiving the message
Expand Down Expand Up @@ -3511,6 +3525,22 @@ class UserManager {
await this.users.updateOne({ username: username }, { $set: { allowFollowingView: toggle } });
}

async setProfileHideFollowing(username, toggle) {
await this.users.updateOne({ username: username }, { $set: { profileHideFollowing: toggle } });
}
async setProfileHideFollowers(username, toggle) {
await this.users.updateOne({ username: username }, { $set: { profileHideFollowers: toggle } });
}

async getProfileHideFollowing(username) {
const result = await this.users.findOne({ username: username });
return result.profileHideFollowing || false;
}
async getProfileHideFollowers(username) {
const result = await this.users.findOne({ username: username });
return result.profileHideFollowers || false;
}

/**
* Temporarily ban a user
* @param {string} username Username of the user
Expand Down
49 changes: 44 additions & 5 deletions api/v1/routes/users/meta/following/getFollowers.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,59 @@ module.exports = (app, utils) => {
app.get("/api/v1/users/meta/getfollowers", async function (req, res) {
const packet = req.query;

const username = (String(packet.username)).toLowerCase();
// meant for getting the data
const target = (String(packet.target)).toLowerCase();
const page = utils.handle_page(packet.page);

if (!username) {
if (!target) {
utils.error(res, 400, "Missing username");
return;
}

if (!await utils.UserManager.existsByUsername(username)) {
if (!await utils.UserManager.existsByUsername(target)) {
utils.error(res, 404, "User not found");
return;
}

const followers = await utils.UserManager.getFollowers(username, page, Number(utils.env.PageSize));
// incase the user hides followers, check if we are the user/a mod
const token = packet.token || "";

const login = await utils.UserManager.loginWithToken(token);
let username = login.username;
let loggedIn = login.success;

const target_data = await utils.UserManager.getUserData(target);
const user_data = await utils.UserManager.getUserData(username);
const isMod = loggedIn ? (user_data.moderator || user_data.admin) : false;

// if not logged in, or we arent a mod and the current acc is not the target
if (!loggedIn || (!isMod && (username !== target))) {
if (target_data.privateProfile) {
// if its a private profile, error if we arent logged in, the account doesnt let following view their profile, or we arent being followed by them
if (!loggedIn || !target_data.allowFollowingView) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}

const usernameID = await utils.UserManager.getIDByUsername(username);
const isFollowing = await utils.UserManager.isFollowing(target_data.id, usernameID);
if (!isFollowing) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}
}
if (await utils.UserManager.getProfileHideFollowers(target)) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "Hidden" });
return;
}
}

const followers = await utils.UserManager.getFollowers(target, page, Number(utils.env.PageSize));

res.status(200);
res.header("Content-Type", 'application/json');
Expand Down
75 changes: 75 additions & 0 deletions api/v1/routes/users/meta/following/getFollowing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
const UserManager = require("../../../../db/UserManager");

/**
* @typedef {Object} Utils
* @property {UserManager} UserManager
*/

/**
*
* @param {any} app Express app
* @param {Utils} utils Utils
*/
module.exports = (app, utils) => {
app.get("/api/v1/users/meta/getfollowing", async function (req, res) {
const packet = req.query;

// meant for getting the data
const target = (String(packet.target)).toLowerCase();
const page = utils.handle_page(packet.page);

if (!target) {
utils.error(res, 400, "Missing username");
return;
}
if (!await utils.UserManager.existsByUsername(target)) {
utils.error(res, 404, "User not found");
return;
}

// incase the user hides following, check if we are the user/a mod
const token = packet.token || "";

const login = await utils.UserManager.loginWithToken(token);
let username = login.username;
let loggedIn = login.success;

const target_data = await utils.UserManager.getUserData(target);
const user_data = await utils.UserManager.getUserData(username);
const isMod = loggedIn ? (user_data.moderator || user_data.admin) : false;

// if not logged in, or we arent a mod and the current acc is not the target
if (!loggedIn || (!isMod && (username !== target))) {
if (target_data.privateProfile) {
// if its a private profile, error if we arent logged in, the account doesnt let following view their profile, or we arent being followed by them
if (!loggedIn || !target_data.allowFollowingView) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}

const usernameID = await utils.UserManager.getIDByUsername(username);
const isFollowing = await utils.UserManager.isFollowing(target_data.id, usernameID);
if (!isFollowing) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}
}
if (await utils.UserManager.getProfileHideFollowing(target)) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "Hidden" });
return;
}
}

const following = await utils.UserManager.getFollowing(target, page, Number(utils.env.PageSize));

res.status(200);
res.header("Content-Type", 'application/json');
res.send(following);
});
}
72 changes: 70 additions & 2 deletions api/v1/routes/users/meta/following/isFollowing.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ module.exports = (app, utils) => {
const username = (String(packet.username)).toLowerCase();
const target = (String(packet.target)).toLowerCase();

// check that these accs exist
if (!username || !target) {
utils.error(res, 400, "Missing username or target");
return;
Expand All @@ -26,17 +27,84 @@ module.exports = (app, utils) => {
utils.error(res, 404, "NotFound");
return;
}

if (!await utils.UserManager.existsByUsername(target)) {
utils.error(res, 404, "NotFound");
return;
}

// incase the users have some privacy setting, check if we are a mod
const token = packet.token || "";
const login = await utils.UserManager.loginWithToken(token);
let fetcherUsername = login.username;
let loggedIn = login.success;

// get all of the data for this req
const target_data = await utils.UserManager.getUserData(target);
const user_data = await utils.UserManager.getUserData(username);
const fetcher_data = await utils.UserManager.getUserData(fetcherUsername);
const fetcherIsMod = loggedIn ? (fetcher_data.moderator || fetcher_data.admin) : false;

const usernameID = await utils.UserManager.getIDByUsername(username);
const targetID = await utils.UserManager.getIDByUsername(target);
const fetcherID = loggedIn ? await utils.UserManager.getIDByUsername(fetcherUsername) : null;

const isFollowing = await utils.UserManager.isFollowing(usernameID, targetID);
// if not logged in, or we arent a mod
if (!loggedIn || !fetcherIsMod) {
// this is a bit complicated:
// we need to see if the user has a private profile/if we can see that profile. if we can, we need to be able to see who they are following.
// we need to see if the target has a private profile/if we can see that profile. if we can, we need to be able to see their followers.
// see if user is private
if (user_data.privateProfile) {
if (!loggedIn || !user_data.allowFollowingView) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}
// people the user follows can see the user's profile
const isFollowing = await utils.UserManager.isFollowing(usernameID, fetcherID);
if (!isFollowing) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}
}
// see if user hides their following
if (await utils.UserManager.getProfileHideFollowing(username)) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "Hidden" });
return;
}
// see if target is private
if (target_data.privateProfile) {
if (!loggedIn || !target_data.allowFollowingView) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}
// people the target follows can see the target's profile
const isFollowing = await utils.UserManager.isFollowing(targetID, fetcherID);
if (!isFollowing) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "PrivateProfile" });
return;
}
}
// see if target hides their followers
if (await utils.UserManager.getProfileHideFollowers(target)) {
res.status(403);
res.header("Content-Type", "application/json");
res.json({ "error": "Hidden" });
return;
}
}

// user doesnt hide who they follow, and target doesnt hide their followers
const isFollowing = await utils.UserManager.isFollowing(usernameID, targetID);
res.status(200);
res.header("Content-Type", 'application/json');
res.send({ following: isFollowing });
Expand Down
29 changes: 29 additions & 0 deletions api/v1/routes/users/meta/privateFollowSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
module.exports = (app, utils) => {
app.post('/api/v1/users/privateFollowSettings', utils.cors(), async function (req, res) {
const packet = req.body;

const username = String(packet.username).toLowerCase();
const token = packet.token;

const login = await utils.UserManager.loginWithToken(token);
if (!login.success) {
utils.error(res, 401, "Reauthenticate");
return;
}

const profileHideFollowing = packet.profileHideFollowing;
const profileHideFollowers = false; // NOTE: Actually, hiding your follower list doesnt really make sense...

if (typeof profileHideFollowing !== "boolean" || typeof profileHideFollowers !== "boolean") {
utils.error(res, 400, "InvalidBody")
return;
}

await utils.UserManager.setProfileHideFollowing(username, profileHideFollowing);
await utils.UserManager.setProfileHideFollowers(username, profileHideFollowers);

res.status(200);
res.header("Content-Type", "application/json");
res.send({ success: true });
});
}
25 changes: 18 additions & 7 deletions api/v1/routes/users/profile.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = (app, utils) => {

const privateProfile = target_data.privateProfile;
const canFollowingSeeProfile = target_data.allowFollowingView;
const canSeeFollowing = !target_data.profileHideFollowing;

let user = {
success: false,
Expand All @@ -54,9 +55,11 @@ module.exports = (app, utils) => {
myFeaturedProject: "",
myFeaturedProjectTitle: "",
followers: 0,
following: 0,
canrankup: false,
privateProfile,
canFollowingSeeProfile,
canSeeFollowing,
isFollowing: false,
};

Expand All @@ -74,13 +77,17 @@ module.exports = (app, utils) => {
return;
}

if (username !== target && (
!(user.isFollowing && canFollowingSeeProfile) && !isMod
)) {
res.status(200);
res.header("Content-Type", "application/json");
res.send(user);
return;
if (username !== target) {
const usernameID = await utils.UserManager.getIDByUsername(username);
const isTargetFollowingUser = await utils.UserManager.isFollowing(targetID, usernameID);

// if we arent a mod, then error if followers cant see the profile or if they can but you arent a follower
if (!isMod && (!canFollowingSeeProfile || (canFollowingSeeProfile && !isTargetFollowingUser))) {
res.status(200);
res.header("Content-Type", "application/json");
res.send(user);
return;
}
}
}

Expand All @@ -98,6 +105,8 @@ module.exports = (app, utils) => {
|| (badges.length > 0); // or we have a badge

const followers = await utils.UserManager.getFollowerCount(target);
const following = await utils.UserManager.getFollowingCount(target);
const canActuallySeeFollowing = canSeeFollowing || (!canSeeFollowing && ((loggedIn && username === target) || isMod))

const myFeaturedProject = await utils.UserManager.getFeaturedProject(target);
const myFeaturedProjectTitle = await utils.UserManager.getFeaturedProjectTitle(target);
Expand All @@ -116,9 +125,11 @@ module.exports = (app, utils) => {
myFeaturedProject,
myFeaturedProjectTitle,
followers: followers,
following: canActuallySeeFollowing ? following : 0,
canrankup: canRequestRankUp && rank !== 1,
privateProfile,
canFollowingSeeProfile,
canSeeFollowing,
isFollowing: user.isFollowing,
};

Expand Down