feat: Deprecation DEPPS11: Replace PublicAPIRouter with PagesRouter (#9974)

BREAKING CHANGE: This release replaces `PublicAPIRouter` with `PagesRouter` (Deprecation DEPPS11).
This commit is contained in:
Manuel
2025-12-12 20:55:39 +01:00
committed by GitHub
parent 1d31406233
commit 8f877d42c0
16 changed files with 84 additions and 895 deletions

View File

@@ -828,10 +828,8 @@ export class Config {
return this.masterKey;
}
// TODO: Remove this function once PagesRouter replaces the PublicAPIRouter;
// the (default) endpoint has to be defined in PagesRouter only.
get pagesEndpoint() {
return this.pages && this.pages.enableRouter && this.pages.pagesEndpoint
return this.pages && this.pages.pagesEndpoint
? this.pages.pagesEndpoint
: 'apps';
}

View File

@@ -27,7 +27,6 @@ import { InstallationsRouter } from './Routers/InstallationsRouter';
import { LogsRouter } from './Routers/LogsRouter';
import { ParseLiveQueryServer } from './LiveQuery/ParseLiveQueryServer';
import { PagesRouter } from './Routers/PagesRouter';
import { PublicAPIRouter } from './Routers/PublicAPIRouter';
import { PushRouter } from './Routers/PushRouter';
import { CloudCodeRouter } from './Routers/CloudCodeRouter';
import { RolesRouter } from './Routers/RolesRouter';
@@ -330,9 +329,7 @@ class ParseServer {
api.use(
'/',
express.urlencoded({ extended: false }),
pages.enableRouter
? new PagesRouter(pages).expressRouter()
: new PublicAPIRouter().expressRouter()
new PagesRouter(pages).expressRouter()
);
api.use(express.json({ type: '*/*', limit: maxUploadSize }));

View File

@@ -1,332 +0,0 @@
import PromiseRouter from '../PromiseRouter';
import Config from '../Config';
import express from 'express';
import path from 'path';
import fs from 'fs';
import qs from 'querystring';
import { Parse } from 'parse/node';
import Deprecator from '../Deprecator/Deprecator';
const public_html = path.resolve(__dirname, '../../public_html');
const views = path.resolve(__dirname, '../../views');
export class PublicAPIRouter extends PromiseRouter {
constructor() {
super();
Deprecator.logRuntimeDeprecation({
usage: 'PublicAPIRouter',
solution: 'pages.enableRouter'
});
}
verifyEmail(req) {
const { token: rawToken } = req.query;
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
const appId = req.params.appId;
const config = Config.get(appId);
if (!config) {
this.invalidRequest();
}
if (!config.publicServerURL) {
return this.missingPublicServerURL();
}
if (!token) {
return this.invalidLink(req);
}
const userController = config.userController;
return userController.verifyEmail(token).then(
() => {
return Promise.resolve({
status: 302,
location: `${config.verifyEmailSuccessURL}`,
});
},
() => {
return this.invalidVerificationLink(req, token);
}
);
}
resendVerificationEmail(req) {
const username = req.body?.username;
const appId = req.params.appId;
const config = Config.get(appId);
if (!config) {
this.invalidRequest();
}
if (!config.publicServerURL) {
return this.missingPublicServerURL();
}
const token = req.body.token;
if (!username && !token) {
return this.invalidLink(req);
}
const userController = config.userController;
return userController.resendVerificationEmail(username, req, token).then(
() => {
return Promise.resolve({
status: 302,
location: `${config.linkSendSuccessURL}`,
});
},
() => {
return Promise.resolve({
status: 302,
location: `${config.linkSendFailURL}`,
});
}
);
}
changePassword(req) {
return new Promise((resolve, reject) => {
const config = Config.get(req.query.id);
if (!config) {
this.invalidRequest();
}
if (!config.publicServerURL) {
return resolve({
status: 404,
text: 'Not found.',
});
}
// Should we keep the file in memory or leave like that?
fs.readFile(path.resolve(views, 'choose_password'), 'utf-8', (err, data) => {
if (err) {
return reject(err);
}
data = data.replace('PARSE_SERVER_URL', `'${config.publicServerURL}'`);
resolve({
text: data,
});
});
});
}
requestResetPassword(req) {
const config = req.config;
if (!config) {
this.invalidRequest();
}
if (!config.publicServerURL) {
return this.missingPublicServerURL();
}
const { token: rawToken } = req.query;
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
if (!token) {
return this.invalidLink(req);
}
return config.userController.checkResetTokenValidity(token).then(
() => {
const params = qs.stringify({
token,
id: config.applicationId,
app: config.appName,
});
return Promise.resolve({
status: 302,
location: `${config.choosePasswordURL}?${params}`,
});
},
() => {
return this.invalidLink(req);
}
);
}
resetPassword(req) {
const config = req.config;
if (!config) {
this.invalidRequest();
}
if (!config.publicServerURL) {
return this.missingPublicServerURL();
}
const { new_password, token: rawToken } = req.body || {};
const token = rawToken && typeof rawToken !== 'string' ? rawToken.toString() : rawToken;
if ((!token || !new_password) && req.xhr === false) {
return this.invalidLink(req);
}
if (!token) {
throw new Parse.Error(Parse.Error.OTHER_CAUSE, 'Missing token');
}
if (!new_password) {
throw new Parse.Error(Parse.Error.PASSWORD_MISSING, 'Missing password');
}
return config.userController
.updatePassword(token, new_password)
.then(
() => {
return Promise.resolve({
success: true,
});
},
err => {
return Promise.resolve({
success: false,
err,
});
}
)
.then(result => {
const queryString = {
token: token,
id: config.applicationId,
error: result.err,
app: config.appName,
};
if (result?.err === 'The password reset link has expired') {
delete queryString.token;
queryString.token = token;
}
const params = qs.stringify(queryString);
if (req.xhr) {
if (result.success) {
return Promise.resolve({
status: 200,
response: 'Password successfully reset',
});
}
if (result.err) {
throw new Parse.Error(Parse.Error.OTHER_CAUSE, `${result.err}`);
}
}
const location = result.success
? `${config.passwordResetSuccessURL}`
: `${config.choosePasswordURL}?${params}`;
return Promise.resolve({
status: 302,
location,
});
});
}
invalidLink(req) {
return Promise.resolve({
status: 302,
location: req.config.invalidLinkURL,
});
}
invalidVerificationLink(req, token) {
const config = req.config;
if (req.params.appId) {
const params = qs.stringify({
appId: req.params.appId,
token,
});
return Promise.resolve({
status: 302,
location: `${config.invalidVerificationLinkURL}?${params}`,
});
} else {
return this.invalidLink(req);
}
}
missingPublicServerURL() {
return Promise.resolve({
text: 'Not found.',
status: 404,
});
}
invalidRequest() {
const error = new Error();
error.status = 403;
error.message = 'unauthorized';
throw error;
}
setConfig(req) {
req.config = Config.get(req.params.appId);
return Promise.resolve();
}
mountRoutes() {
this.route(
'GET',
'/apps/:appId/verify_email',
req => {
this.setConfig(req);
},
req => {
return this.verifyEmail(req);
}
);
this.route(
'POST',
'/apps/:appId/resend_verification_email',
req => {
this.setConfig(req);
},
req => {
return this.resendVerificationEmail(req);
}
);
this.route('GET', '/apps/choose_password', req => {
return this.changePassword(req);
});
this.route(
'POST',
'/apps/:appId/request_password_reset',
req => {
this.setConfig(req);
},
req => {
return this.resetPassword(req);
}
);
this.route(
'GET',
'/apps/:appId/request_password_reset',
req => {
this.setConfig(req);
},
req => {
return this.requestResetPassword(req);
}
);
}
expressRouter() {
const router = express.Router();
router.use('/apps', express.static(public_html));
router.use('/', super.expressRouter());
return router;
}
}
export default PublicAPIRouter;