fix(api): handle 4XX errors get-public-profile (#55205)

Co-authored-by: Mrugesh Mohapatra <1884376+raisedadead@users.noreply.github.com>
This commit is contained in:
Shaun Hamilton
2024-06-20 07:54:52 +02:00
committed by GitHub
parent a7e3e69da6
commit f1cd0cfae3
3 changed files with 122 additions and 116 deletions

View File

@@ -22,11 +22,6 @@ import {
setAccessTokenToResponse,
removeCookies
} from '../../server/utils/getSetAccessToken';
import {
normaliseUserFields,
getProgress,
publicUserProps
} from '../../server/utils/publicUserProps';
import { saveUser, observeMethod } from '../../server/utils/rx.js';
import { getEmailSender } from '../../server/utils/url-utils';
import {
@@ -744,116 +739,6 @@ export default function initializeUser(User) {
);
};
function prepUserForPublish(user, profileUI) {
const {
about,
calendar,
completedChallenges,
isDonating,
joinDate,
location,
name,
points,
portfolio,
username,
yearsTopContributor
} = user;
const {
isLocked = true,
showAbout = false,
showCerts = false,
showDonation = false,
showHeatMap = false,
showLocation = false,
showName = false,
showPoints = false,
showPortfolio = false,
showTimeLine = false
} = profileUI;
if (isLocked) {
return {
isLocked,
profileUI,
username
};
}
return {
...user,
about: showAbout ? about : '',
calendar: showHeatMap ? calendar : {},
completedChallenges: (function () {
if (showTimeLine) {
return showCerts
? completedChallenges
: completedChallenges.filter(
({ challengeType }) => challengeType !== 7
);
} else {
return [];
}
})(),
isDonating: showDonation ? isDonating : null,
joinDate: showAbout ? joinDate : '',
location: showLocation ? location : '',
name: showName ? name : '',
points: showPoints ? points : null,
portfolio: showPortfolio ? portfolio : [],
yearsTopContributor: yearsTopContributor
};
}
User.getPublicProfile = function getPublicProfile(username, cb) {
return User.findOne$({ where: { username } })
.flatMap(user => {
if (!user) {
return Observable.of({});
}
const { completedChallenges, progressTimestamps, profileUI } = user;
const allUser = {
..._.pick(user, publicUserProps),
points: progressTimestamps.length,
completedChallenges,
...getProgress(progressTimestamps),
...normaliseUserFields(user),
joinDate: user.id.getTimestamp()
};
const publicUser = prepUserForPublish(allUser, profileUI);
return Observable.of({
entities: {
user: {
[user.username]: {
...publicUser
}
}
},
result: user.username
});
})
.subscribe(user => cb(null, user), cb);
};
User.remoteMethod('getPublicProfile', {
accepts: {
arg: 'username',
type: 'string',
required: true
},
returns: [
{
arg: 'user',
type: 'object',
root: true
}
],
http: {
path: '/get-public-profile',
verb: 'GET'
}
});
User.giveBrowniePoints = function giveBrowniePoints(
receiver,
giver,

View File

@@ -1,5 +1,11 @@
import { pick } from 'lodash';
import { getRedirectParams } from '../utils/redirection';
import { deprecatedEndpoint } from '../utils/disabled-endpoints';
import {
getProgress,
normaliseUserFields,
publicUserProps
} from '../utils/publicUserProps';
module.exports = function (app) {
const router = app.loopback.Router();
@@ -10,6 +16,7 @@ module.exports = function (app) {
router.get('/unsubscribe/:email', unsubscribeDeprecated);
router.get('/ue/:unsubscribeId', unsubscribeById);
router.get('/resubscribe/:unsubscribeId', resubscribe);
router.get('/api/users/get-public-profile', blockUserAgent, getPublicProfile);
app.use(router);
@@ -105,4 +112,118 @@ module.exports = function (app) {
.catch(next);
});
}
const blockedUserAgentParts = ['python', 'google-apps-script', 'curl'];
function blockUserAgent(req, res, next) {
const userAgent = req.headers['user-agent'];
if (
!userAgent ||
blockedUserAgentParts.some(ua => userAgent.toLowerCase().includes(ua))
) {
return res
.status(400)
.send(
'This endpoint is no longer available outside of the freeCodeCamp ecosystem'
);
}
return next();
}
async function getPublicProfile(req, res) {
const { username } = req.query;
if (!username) {
return res.status(400).json({ error: 'No username provided' });
}
const user = await User.findOne({ where: { username } });
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
const { completedChallenges, progressTimestamps, profileUI } = user;
const allUser = {
...pick(user, publicUserProps),
points: progressTimestamps.length,
completedChallenges,
...getProgress(progressTimestamps),
...normaliseUserFields(user),
joinDate: user.id.getTimestamp()
};
const publicUser = prepUserForPublish(allUser, profileUI);
return res.json({
entities: {
user: {
[user.username]: {
...publicUser
}
}
},
result: user.username
});
}
function prepUserForPublish(user, profileUI) {
const {
about,
calendar,
completedChallenges,
isDonating,
joinDate,
location,
name,
points,
portfolio,
username,
yearsTopContributor
} = user;
const {
isLocked = true,
showAbout = false,
showCerts = false,
showDonation = false,
showHeatMap = false,
showLocation = false,
showName = false,
showPoints = false,
showPortfolio = false,
showTimeLine = false
} = profileUI;
if (isLocked) {
return {
isLocked,
profileUI,
username
};
}
return {
...user,
about: showAbout ? about : '',
calendar: showHeatMap ? calendar : {},
completedChallenges: (function () {
if (showTimeLine) {
return showCerts
? completedChallenges
: completedChallenges.filter(
({ challengeType }) => challengeType !== 7
);
} else {
return [];
}
})(),
isDonating: showDonation ? isDonating : null,
joinDate: showAbout ? joinDate : '',
location: showLocation ? location : '',
name: showName ? name : '',
points: showPoints ? points : null,
portfolio: showPortfolio ? portfolio : [],
yearsTopContributor: yearsTopContributor
};
}
};

View File

@@ -43,7 +43,6 @@ function bootUser(app) {
const deleteMsUsername = createDeleteMsUsername(app);
const postSubmitSurvey = createPostSubmitSurvey(app);
const deleteUserSurveys = createDeleteUserSurveys(app);
api.get('/account', sendNonUserToHome, deprecatedEndpoint);
api.get('/account/unlink/:social', sendNonUserToHome, getUnlinkSocial);
api.get('/user/get-session-user', getSessionUser);
@@ -532,4 +531,5 @@ function createPostReportUserProfile(app) {
);
};
}
export default bootUser;