This commit is contained in:
2026-09-12 10:10:34 +02:00
parent 217f1be72d
commit 53055ea5c6
35 changed files with 2261 additions and 138 deletions
+22 -22
View File
@@ -10,7 +10,7 @@ import crypto from 'crypto';
export default router => {
router.group(/^\/api\/v2\/settings/, group => {
group.put(/\/setAvatar/, lib.loggedin, async (req, res) => {
group.put(/\/setAvatar/, lib.registeredUser, async (req, res) => {
if (!req.post.avatar) {
return res.json({
msg: 'no avatar provided',
@@ -46,7 +46,7 @@ export default router => {
});
// Switch to custom avatar (sets avatar ID to 0 so avatar_file is used)
group.put(/\/useCustomAvatar/, lib.loggedin, async (req, res) => {
group.put(/\/useCustomAvatar/, lib.registeredUser, async (req, res) => {
// Check if user has a custom avatar file
const userOpts = (await db`
select avatar_file from user_options where user_id = ${+req.session.id}
@@ -139,7 +139,7 @@ export default router => {
});
// Generic Token Generation (default type=discord if not specified, though frontend should specify)
group.post(/\/link\/token/, lib.loggedin, async (req, res) => {
group.post(/\/link\/token/, lib.registeredUser, async (req, res) => {
// 6-char alphanumeric code
const token = Math.random().toString(36).substring(2, 8).toUpperCase();
const type = req.post.type || 'discord'; // Default to discord for backward compatibility if needed
@@ -179,7 +179,7 @@ export default router => {
});
// Get linked accounts (Discord & Matrix)
group.get(/\/link\/accounts/, lib.loggedin, async (req, res) => {
group.get(/\/link\/accounts/, lib.registeredUser, async (req, res) => {
try {
const aliases = await db`
SELECT alias, type FROM user_alias
@@ -199,7 +199,7 @@ export default router => {
});
// Unlink account
group.delete(/\/link\/unlink\/(?<type>[a-z]+)\/(?<alias>.+)/, lib.loggedin, async (req, res) => {
group.delete(/\/link\/unlink\/(?<type>[a-z]+)\/(?<alias>.+)/, lib.registeredUser, async (req, res) => {
try {
const alias = decodeURIComponent(req.params.alias);
const type = req.params.type;
@@ -225,7 +225,7 @@ export default router => {
// Backward compatibility routes for Discord (Deprecated)
// Discord Token Generation (Redirect to generic)
group.post(/\/discord\/token/, lib.loggedin, async (req, res) => {
group.post(/\/discord\/token/, lib.registeredUser, async (req, res) => {
// Just call the logic inline
const token = Math.random().toString(36).substring(2, 8).toUpperCase();
try {
@@ -238,13 +238,13 @@ export default router => {
});
// Get linked Discord accounts (Legacy)
group.get(/\/discord\/linked/, lib.loggedin, async (req, res) => {
group.get(/\/discord\/linked/, lib.registeredUser, async (req, res) => {
const aliases = await db`SELECT alias FROM user_alias WHERE userid = ${req.session.id} AND type = 'discord'`;
return res.json({ success: true, aliases: aliases.map(a => ({ alias: a.alias })) }, 200);
});
// Unlink Discord account (Legacy)
group.delete(/\/discord\/unlink\/(?<alias>.+)/, lib.loggedin, async (req, res) => {
group.delete(/\/discord\/unlink\/(?<alias>.+)/, lib.registeredUser, async (req, res) => {
const alias = decodeURIComponent(req.params.alias);
await db`DELETE FROM user_alias WHERE lower(alias) = lower(${alias}) AND userid = ${req.session.id} AND type = 'discord'`;
return res.json({ success: true, msg: 'Account unlinked' }, 200);
@@ -365,7 +365,7 @@ export default router => {
});
// Update Default Upload Visibility preference
group.put(/\/default_upload_visibility/, lib.loggedin, async (req, res) => {
group.put(/\/default_upload_visibility/, lib.registeredUser, async (req, res) => {
if (cfg.allow_user_upload_visibility === false || cfg.websrv?.allow_user_upload_visibility === false) {
return res.json({ success: false, msg: 'Custom upload visibility is disabled by the administrator' }, 403);
}
@@ -389,7 +389,7 @@ export default router => {
});
// Update Username Color preference
group.put(/\/username_color/, lib.loggedin, async (req, res) => {
group.put(/\/username_color/, lib.registeredUser, async (req, res) => {
const { color } = req.post;
if (!color || !/^#([0-9A-F]{3}){1,2}$/i.test(color)) {
@@ -420,7 +420,7 @@ export default router => {
});
// Update password
group.put(/\/password/, lib.loggedin, async (req, res) => {
group.put(/\/password/, lib.registeredUser, async (req, res) => {
const { current_password, new_password, new_password_confirm } = req.post;
if (!new_password || !new_password_confirm) {
@@ -461,7 +461,7 @@ export default router => {
});
// Update email
group.put(/\/email/, lib.loggedin, async (req, res) => {
group.put(/\/email/, lib.registeredUser, async (req, res) => {
const { email } = req.post;
if (!email || !email.trim()) return res.json({ success: false, msg: 'Email is required' }, 400);
const cleanEmail = email.trim();
@@ -484,7 +484,7 @@ export default router => {
});
// Update Display Name
group.put(/\/display_name/, lib.loggedin, async (req, res) => {
group.put(/\/display_name/, lib.registeredUser, async (req, res) => {
const { display_name } = req.post;
if (display_name !== undefined && typeof display_name !== 'string') {
@@ -517,7 +517,7 @@ export default router => {
});
// Update Description
group.put(/\/description/, lib.loggedin, async (req, res) => {
group.put(/\/description/, lib.registeredUser, async (req, res) => {
if (!cfg.websrv.enable_profile_description) {
return res.json({ success: false, msg: 'Profile descriptions are disabled' }, 403);
}
@@ -553,7 +553,7 @@ export default router => {
});
// Update Font preference
group.put(/\/font/, lib.loggedin, async (req, res) => {
group.put(/\/font/, lib.registeredUser, async (req, res) => {
const { font } = req.post;
// F-023 Security: Validate font against actual files on disk
@@ -846,7 +846,7 @@ export default router => {
// GET /api/v2/settings/api-key
// Returns whether the user has an API key, when it was created, and the last 8 chars (masked preview).
group.get(/\/api-key$/, lib.loggedin, async (req, res) => {
group.get(/\/api-key$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_api_keys === false) {
return res.json({ success: false, msg: 'API keys are disabled' }, 403);
}
@@ -876,7 +876,7 @@ export default router => {
// POST /api/v2/settings/api-key/regenerate
// Generates a new key (or replaces an existing one). Returns the full key — only shown once.
group.post(/\/api-key\/regenerate$/, lib.loggedin, async (req, res) => {
group.post(/\/api-key\/regenerate$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_api_keys === false) {
return res.json({ success: false, msg: 'API keys are disabled' }, 403);
}
@@ -904,7 +904,7 @@ export default router => {
// DELETE /api/v2/settings/api-key
// Revokes (deletes) the user's API key.
group.delete(/\/api-key$/, lib.loggedin, async (req, res) => {
group.delete(/\/api-key$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_api_keys === false) {
return res.json({ success: false, msg: 'API keys are disabled' }, 403);
}
@@ -928,7 +928,7 @@ export default router => {
// GET /api/v2/settings/api-key/sharex-config
// Downloads a pre-filled ShareX custom uploader (.sxcu) for the requesting user.
group.get(/\/api-key\/sharex-config$/, lib.loggedin, async (req, res) => {
group.get(/\/api-key\/sharex-config$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_api_keys === false) {
return res.status(403).reply({ body: 'API keys are disabled' });
}
@@ -1005,7 +1005,7 @@ export default router => {
// GET /api/v2/settings/invites
// Returns eligibility, criteria breakdown, tokens created by this user, and slot usage.
group.get(/\/invites$/, lib.loggedin, async (req, res) => {
group.get(/\/invites$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_invites === false) {
return res.json({ success: false, msg: 'Invite system is disabled' }, 403);
}
@@ -1085,7 +1085,7 @@ export default router => {
// POST /api/v2/settings/invites/create
// Generates a new invite token if eligible and slots remain.
group.post(/\/invites\/create$/, lib.loggedin, async (req, res) => {
group.post(/\/invites\/create$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_invites === false) {
return res.json({ success: false, msg: 'Invite system is disabled' }, 403);
}
@@ -1151,7 +1151,7 @@ export default router => {
// POST /api/v2/settings/invites/delete
// Deletes an unused invite token owned by the calling user.
group.post(/\/invites\/delete$/, lib.loggedin, async (req, res) => {
group.post(/\/invites\/delete$/, lib.registeredUser, async (req, res) => {
if (cfg.websrv.enable_user_invites === false) {
return res.json({ success: false, msg: 'Invite system is disabled' }, 403);
}