mirror of
https://gitlab.com/PronounsPage/PronounsPage.git
synced 2025-09-25 14:09:03 -04:00
feat(api): Filtering profile data
This commit is contained in:
parent
5097e6c7a1
commit
b1f9f39ca7
@ -67,7 +67,136 @@ const verifyLinks = (links, authenticators, username, linksMetadata) => {
|
||||
return verifiedLinks;
|
||||
}
|
||||
|
||||
const fetchProfiles = async (db, username, self) => {
|
||||
class ProfileOptions {
|
||||
default_props = {
|
||||
enabled: true,
|
||||
default_value: true
|
||||
};
|
||||
locale_specific_props = {
|
||||
enabled: true,
|
||||
default_value: true
|
||||
};
|
||||
|
||||
constructor(query_obj, locales) {
|
||||
const default_props = query_obj["props"];
|
||||
this.default_props = ProfileOptions._parse_props(default_props)
|
||||
|
||||
for (const key of Object.keys(locales)) {
|
||||
const lprops_obj = query_obj["lprops"];
|
||||
let props;
|
||||
if (typeof lprops_obj === "object") {
|
||||
props = lprops_obj[key];
|
||||
} else {
|
||||
props = query_obj[`lprops.${key.toLowerCase()}`];
|
||||
}
|
||||
if (props != null) {
|
||||
this.locale_specific_props[key] = ProfileOptions._parse_props(props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static _parse_props(raw_param) {
|
||||
if (raw_param) {
|
||||
/** @type {Array<string>} */
|
||||
let properties;
|
||||
let default_value = false;
|
||||
const obj = {
|
||||
default_value
|
||||
};
|
||||
if (Array.isArray(raw_param)) {
|
||||
properties = raw_param
|
||||
} else if (typeof raw_param == "object") {
|
||||
return {
|
||||
...raw_param,
|
||||
enabled: true,
|
||||
default_value: false,
|
||||
}
|
||||
} else if (typeof raw_param === 'string') {
|
||||
if (raw_param === "none") {
|
||||
// Don't show the locale at all
|
||||
return {
|
||||
enabled: false,
|
||||
default_value: false
|
||||
}
|
||||
} else if (raw_param === "empty") {
|
||||
// Include it as an object, but don't include any data
|
||||
return {
|
||||
enabled: true,
|
||||
default_value: false
|
||||
}
|
||||
} else if (raw_param === "all") {
|
||||
// All properties should be shown
|
||||
return {
|
||||
enabled: true,
|
||||
default_value: true
|
||||
}
|
||||
}
|
||||
properties = raw_param.split(',');
|
||||
}
|
||||
|
||||
for (const property of properties) {
|
||||
obj[property] = !default_value;
|
||||
}
|
||||
|
||||
return {
|
||||
...obj,
|
||||
enabled: true,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
enabled: true,
|
||||
default_value: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_data_for(locale) {
|
||||
return this.locale_specific_props[locale] ?? this.default_props;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} locale
|
||||
* @return {boolean}
|
||||
*/
|
||||
show_at_all(locale) {
|
||||
return this._data_for(locale).enabled ?? true
|
||||
}
|
||||
/**
|
||||
* @param {string} locale
|
||||
* @param {string} property
|
||||
* @return {boolean}
|
||||
*/
|
||||
prop(locale, property) {
|
||||
const data = this._data_for(locale);
|
||||
if (!(data.enabled ?? true)) return false;
|
||||
return data[property] ?? data.default_value ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {string} locale
|
||||
* @param {string} property
|
||||
* @param {() => T} value
|
||||
* @return {T | undefined}
|
||||
*/
|
||||
propv(locale, property, value) {
|
||||
if (this.prop(locale, property) === true) {
|
||||
return value();
|
||||
} else {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param db
|
||||
* @param {string} username
|
||||
* @param {boolean} self
|
||||
* @param {ProfileOptions} opts
|
||||
* @return {Promise<{}>}
|
||||
*/
|
||||
const fetchProfiles = async (db, username, self, opts) => {
|
||||
const user = await db.get(SQL`SELECT id FROM users WHERE usernameNorm = ${normalise(username)}`);
|
||||
if (!user) {
|
||||
return {};
|
||||
@ -90,6 +219,7 @@ const fetchProfiles = async (db, username, self) => {
|
||||
|
||||
const p = {}
|
||||
for (let profile of profiles) {
|
||||
if (!opts.show_at_all(profile)) continue;
|
||||
const links = JSON.parse(profile.links).map(l => normaliseUrl(l)).filter(l => !!l);
|
||||
const linksMetadata = {};
|
||||
for (let link of await db.all(SQL`SELECT * FROM links WHERE url IN (`.append(links.map(k => `'${k.replace(/'/g, "''")}'`).join(',')).append(SQL`)`))) {
|
||||
@ -99,39 +229,42 @@ const fetchProfiles = async (db, username, self) => {
|
||||
nodeinfo: JSON.parse(link.nodeinfo),
|
||||
};
|
||||
}
|
||||
const circle = await fetchCircles(db, profile.id, user.id);
|
||||
|
||||
p[profile.locale] = {
|
||||
opinions: JSON.parse(profile.opinions),
|
||||
names: JSON.parse(profile.names),
|
||||
pronouns: JSON.parse(profile.pronouns),
|
||||
description: profile.description,
|
||||
age: calcAge(profile.birthday),
|
||||
links,
|
||||
linksMetadata,
|
||||
verifiedLinks: verifyLinks(links, linkAuthenticators, username, linksMetadata),
|
||||
flags: JSON.parse(profile.flags),
|
||||
customFlags: JSON.parse(profile.customFlags),
|
||||
words: JSON.parse(profile.words),
|
||||
birthday: self ? profile.birthday : undefined,
|
||||
timezone: profile.timezone ? JSON.parse(profile.timezone) : null,
|
||||
teamName: profile.teamName,
|
||||
footerName: profile.footerName,
|
||||
footerAreas: profile.footerAreas ? profile.footerAreas.split(',') : [],
|
||||
credentials: profile.credentials ? profile.credentials.split('|') : [],
|
||||
credentialsLevel: profile.credentialsLevel,
|
||||
credentialsName: profile.credentialsName,
|
||||
card: profile.card,
|
||||
cardDark: profile.cardDark,
|
||||
circle,
|
||||
sensitive: JSON.parse(profile.sensitive),
|
||||
|
||||
const propv = (key, value) => opts.propv(profile, key, value);
|
||||
|
||||
const profile_obj = {
|
||||
opinions: propv("opinions", () => JSON.parse(profile.opinions)),
|
||||
names: propv("names", () => JSON.parse(profile.names)),
|
||||
pronouns: propv("pronouns", () => JSON.parse(profile.pronouns)),
|
||||
description: propv("description", () => profile.description),
|
||||
age: propv("age", () => calcAge(profile.birthday)),
|
||||
links: propv("links", () => links), // todo: calculate link data only if needed
|
||||
linksMetadata: propv("links", () => linksMetadata),
|
||||
verifiedLinks: propv("links", () => verifyLinks(links, linkAuthenticators, username, linksMetadata)),
|
||||
flags: propv("flags", () => JSON.parse(profile.flags)),
|
||||
customFlags: propv("flags", () => JSON.parse(profile.customFlags)),
|
||||
words: propv("words", () => JSON.parse(profile.words)),
|
||||
birthday: propv("age", () => self ? profile.birthday : undefined),
|
||||
timezone: propv("timezone", () => profile.timezone ? JSON.parse(profile.timezone) : null),
|
||||
teamName: propv("team", () => profile.teamName),
|
||||
footerName: propv("team", () => profile.footerName),
|
||||
footerAreas: propv("team", () => profile.footerAreas ? profile.footerAreas.split(',') : []),
|
||||
credentials: propv("credentials", () => profile.credentials ? profile.credentials.split('|') : []),
|
||||
credentialsLevel: propv("credentials", () => profile.credentialsLevel),
|
||||
credentialsName: propv("credentials", () => profile.credentialsName),
|
||||
card: propv("card_image", () => profile.card),
|
||||
cardDark: propv("card_image", () => profile.cardDark),
|
||||
circle: await propv("circle", () => fetchCircles(db, profile.id, user.id)),
|
||||
sensitive: propv("sensitive", () => JSON.parse(profile.sensitive)),
|
||||
};
|
||||
p[profile.locale] = profile_obj;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
|
||||
export const profilesSnapshot = async (db, username) => {
|
||||
return JSON.stringify(await fetchProfiles(db, username, true), null, 4);
|
||||
export const profilesSnapshot = async (db, username, opts) => {
|
||||
return JSON.stringify(await fetchProfiles(db, username, true, opts), null, 4);
|
||||
}
|
||||
|
||||
const susRegexes = fs.readFileSync(__dirname + '/../../moderation/sus.txt').toString('utf-8').split('\n').filter(x => !!x);
|
||||
@ -229,9 +362,15 @@ const fetchCircles = async(db, profileId, userId) => {
|
||||
|
||||
const router = Router();
|
||||
|
||||
/**
|
||||
* @param {import('express').Request} req
|
||||
* @param {import('express').Response} res
|
||||
* @param {any} user
|
||||
*/
|
||||
const fetchProfilesRoute = async (req, res, user) => {
|
||||
const isSelf = req.user && req.user.username === req.params.username;
|
||||
const isAdmin = req.isGranted('users');
|
||||
const opts = new ProfileOptions(req.query, req.locales);
|
||||
|
||||
if (!user || (user.bannedReason !== null && !isAdmin && !isSelf)) {
|
||||
return res.json({
|
||||
@ -245,7 +384,7 @@ const fetchProfilesRoute = async (req, res, user) => {
|
||||
|
||||
user.bannedTerms = user.bannedTerms ? user.bannedTerms.split(',') : [];
|
||||
|
||||
let profiles = await fetchProfiles(req.db, user.username, isSelf);
|
||||
let profiles = await fetchProfiles(req.db, user.username, isSelf, opts);
|
||||
if (req.query.version !== '2') {
|
||||
for (let [locale, profile] of Object.entries(profiles)) {
|
||||
profiles[locale] = downgradeToV1(profile);
|
||||
@ -569,7 +708,7 @@ router.post('/profile/delete/:locale', handleErrorAsync(async (req, res) => {
|
||||
|
||||
await req.db.get(SQL`DELETE FROM profiles WHERE userId = ${req.user.id} AND locale = ${req.params.locale}`);
|
||||
|
||||
return res.json(await fetchProfiles(req.db, req.user.username, true));
|
||||
return res.json(await fetchProfiles(req.db, req.user.username, true, new ProfileOptions({})));
|
||||
}));
|
||||
|
||||
router.post('/profile/report/:username', handleErrorAsync(async (req, res) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user