Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
98ac26bf70 | ||
|
|
7e235637c4 | ||
|
|
0e8d5f93c9 | ||
|
|
a0ccce04a3 |
@@ -7,6 +7,16 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [1.9.5] - 2026-08-27
|
||||
|
||||
### Fixed
|
||||
|
||||
- The public event-alias route (`/:redirectUrl`, used for short links like `/camp-2025`) is a catch-all matching any unmatched top-level path, so every bot/scanner probe for a nonexistent page (`/wp-login.php`, `/.env`, etc.) was hitting the backend and firing a live database query. Both the frontend route and the `GET /api/events/by-alias/:redirectUrl` endpoint now reject anything that isn't a plausible alias (letters/numbers/hyphens/underscores) before touching the database, instead of forwarding scanner noise straight through — this traffic pattern could exhaust the database connection pool and take the server down under load.
|
||||
|
||||
### Added
|
||||
|
||||
- PM2 (`ecosystem.config.js`) now restarts either process if it exceeds 500MB of memory, instead of letting an unbounded leak run until the OS OOM-kills it.
|
||||
|
||||
## [1.9.4] - 2026-08-26
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "event-management-backend",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.5",
|
||||
"description": "Event Management System Backend",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1696,9 +1696,20 @@ const scheduleWhatsappEventAttendees = async (req, res) => {
|
||||
* @route GET /api/events/by-alias/:redirectUrl
|
||||
* @access Public
|
||||
*/
|
||||
// Aliases are admin-set slugs (e.g. "camp-2025") — see the event wizard's "URL
|
||||
// Alias" field. This endpoint is public and also the target of the frontend's
|
||||
// catch-all [redirectUrl] route, so it's what every bot/scanner probe hitting
|
||||
// an unmatched top-level path (/wp-login.php, /.env, etc.) ends up calling.
|
||||
// Rejecting non-slug-shaped values here skips a DB round-trip for that traffic.
|
||||
const VALID_ALIAS = /^[a-zA-Z0-9_-]{1,100}$/;
|
||||
|
||||
const getEventByAlias = async (req, res) => {
|
||||
const { redirectUrl } = req.params;
|
||||
|
||||
if (!VALID_ALIAS.test(redirectUrl)) {
|
||||
return res.status(404).json({ message: 'Event not found' });
|
||||
}
|
||||
|
||||
try {
|
||||
const event = await prisma.event.findFirst({
|
||||
where: {
|
||||
|
||||
+3
-1
@@ -5,13 +5,15 @@ module.exports = {
|
||||
cwd: __dirname + '/backend',
|
||||
script: 'src/index.js',
|
||||
env: { NODE_ENV: 'production' },
|
||||
max_memory_restart: '500M',
|
||||
},
|
||||
{
|
||||
name: 'hope-events-frontend',
|
||||
cwd: __dirname + '/frontend',
|
||||
script: 'npm',
|
||||
args: 'start -- -p 3001',
|
||||
args: 'start -- -p 3000',
|
||||
env: { NODE_ENV: 'production' },
|
||||
max_memory_restart: '500M',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hope-events-frontend",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.5",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev --turbopack",
|
||||
|
||||
@@ -3,15 +3,25 @@ import { apiFetch } from "@/lib/api";
|
||||
|
||||
export const revalidate = 60;
|
||||
|
||||
// Event aliases are admin-set slugs (e.g. "camp-2025", "movie-night") — see the
|
||||
// "URL Alias" field in the event wizard. This catch-all route matches *any*
|
||||
// unmatched top-level path, so it's also what every bot/scanner probe hits
|
||||
// (/wp-login.php, /.env, /xmlrpc.php, etc.). Rejecting anything that isn't a
|
||||
// plausible slug here skips a live DB query for that background noise instead
|
||||
// of forwarding it straight to the backend.
|
||||
const VALID_ALIAS = /^[a-zA-Z0-9_-]{1,100}$/;
|
||||
|
||||
export default async function EventRedirectPage({ params }: { params: Promise<{ redirectUrl: string }> }) {
|
||||
const { redirectUrl } = await params;
|
||||
|
||||
let event: any = null;
|
||||
|
||||
try {
|
||||
event = await apiFetch<any>(`/api/events/by-alias/${redirectUrl}`);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch event:", error);
|
||||
if (VALID_ALIAS.test(redirectUrl)) {
|
||||
try {
|
||||
event = await apiFetch<any>(`/api/events/by-alias/${redirectUrl}`);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch event:", error);
|
||||
}
|
||||
}
|
||||
|
||||
if (!event || event.message?.toLowerCase().includes("not found")) {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "hope-events",
|
||||
"version": "1.9.4",
|
||||
"version": "1.9.5",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev:backend": "cd backend && npm run dev",
|
||||
|
||||
Reference in New Issue
Block a user