fix(backend): escape regex search

This commit is contained in:
Aleksander Wilczyński 2024-11-17 02:04:59 +01:00
parent 10a5fa7556
commit 14be22c46f
Signed by untrusted user: alekswilc
GPG Key ID: D4464A248E5F27FE
7 changed files with 35 additions and 121 deletions

View File

@ -1,104 +0,0 @@
/*
* Copyright (C) 2024 Aleksander <alekswilc> Wilczyński (aleks@alekswilc.dev)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* See LICENSE for more.
*/
import { connect } from 'mongoose';
import { Model, model, Schema } from 'mongoose';
(async () => {
await connect(process.env.MONGO_URI);
// #region model
const schema = new Schema({
id: {
type: String,
required: true
},
steam: {
type: String,
required: true
},
steamName: {
type: String,
required: true
},
trainStats: {
type: Object,
required: false,
default: {}
},
dispatcherStats: {
type: Object,
required: false,
default: {}
},
trainTime: {
type: Number,
required: false,
default: 0
},
trainPoints: {
type: Number,
required: false,
default: 0
},
trainDistance: {
type: Number,
required: false,
default: 0
},
dispatcherTime: {
type: Number,
required: false,
default: 0
},
});
const MProfile = model('profile', schema);
// #endregion
const _ = await MProfile.find({});
for (const x of _) {
/*
dispatcherTime: number;
trainTime: number
trainPoints: number
trainDistance: number*/
x.trainPoints = Object.values(x.trainStats).length === 1 ?
Object.values(x.trainStats)[0].score :
Object.values(x.trainStats).reduce((acc, curr) => acc + curr.score, 0)
x.trainDistance = Object.values(x.trainStats).length === 1 ?
Object.values(x.trainStats)[0].distance :
Object.values(x.trainStats).reduce((acc, curr) => acc + curr.distance, 0)
x.trainTime = Object.values(x.trainStats).length === 1 ?
Object.values(x.trainStats)[0].trainTime :
Object.values(x.trainStats).reduce((acc, curr) => acc + curr.trainTime, 0)
x.dispatcherTime = Object.values(x.dispatcherStats).length === 1 ?
Object.values(x.dispatcherStats)[0].time :
Object.values(x.dispatcherStats).reduce((acc, curr) => acc + curr.time, 0)
console.log('updated ' + x.steamName)
await MProfile.updateOne({ id: x.id }, { trainPoints: x.trainPoints, trainDistance: x.trainDistance, dispatcherTime: x.dispatcherTime, trainTime: x.trainTime })
};
console.log('done!')
})();

View File

@ -18,7 +18,7 @@ import { Router } from "express";
import { PipelineStage } from "mongoose";
import { IProfile, MProfile, raw_schema } from "../../mongo/profile.js";
import { SuccessResponseBuilder } from "../responseBuilder.js";
import { removeProperties } from "../../util/functions.js";
import { escapeRegexString, removeProperties } from "../../util/functions.js";
const generateSearch = (regex: RegExp) => [
{
@ -37,7 +37,7 @@ export class LeaderboardRoute
app.get("/train", async (req, res) =>
{
const s = req.query.q?.toString().split(",").map(x => new RegExp(x, "i"));
const s = req.query.q?.toString().split(",").map(x => new RegExp(escapeRegexString(x), "i"));
const filter: PipelineStage[] = [];
@ -64,7 +64,7 @@ export class LeaderboardRoute
app.get("/station", async (req, res) =>
{
const s = req.query.q?.toString().split(",").map(x => new RegExp(x, "i"));
const s = req.query.q?.toString().split(",").map(x => new RegExp(escapeRegexString(x), "i"));
const filter: PipelineStage[] = [];
s && filter.push({

View File

@ -22,7 +22,7 @@ import { PipelineStage } from "mongoose";
import { MBlacklist } from "../../mongo/blacklist.js";
import { SteamUtil } from "../../util/SteamUtil.js";
import { GitUtil } from "../../util/git.js";
import { removeProperties } from "../../util/functions.js";
import { escapeRegexString, removeProperties } from "../../util/functions.js";
import { SuccessResponseBuilder } from "../responseBuilder.js";
import { MProfile } from "../../mongo/profile.js";
@ -52,7 +52,7 @@ export class StationsRoute
app.get("/", async (req, res) =>
{
const s = req.query.q?.toString().split(",").map(x => new RegExp(x, "i"));
const s = req.query.q?.toString().split(",").map(x => new RegExp(escapeRegexString(x), "i"));
const profiles = await MProfile.find({ verified: true });
const filter: PipelineStage[] = [];

View File

@ -23,7 +23,7 @@ import { MBlacklist } from "../../mongo/blacklist.js";
import { SteamUtil } from "../../util/SteamUtil.js";
import { GitUtil } from "../../util/git.js";
import { SuccessResponseBuilder } from "../responseBuilder.js";
import { removeProperties } from "../../util/functions.js";
import { escapeRegexString, removeProperties } from "../../util/functions.js";
import { MProfile } from "../../mongo/profile.js";
const generateSearch = (regex: RegExp) => [
@ -49,7 +49,7 @@ export class TrainsRoute
app.get("/", async (req, res) =>
{
const s = req.query.q?.toString().split(",").map(x => new RegExp(x, "i"));
const s = req.query.q?.toString().split(",").map(x => new RegExp(escapeRegexString(x), "i"));
const profiles = await MProfile.find({ verified: true });
const filter: PipelineStage[] = [];

View File

@ -101,12 +101,19 @@ export const trainsList = [
],
},
{
train: "EN96",
train: "EN76",
pattern: [
"Elf/EN76-*",
],
},
{
train: 'EN96',
pattern: [
'Elf/EN96-*',
]
},
{
train: "EP07",
pattern: [

View File

@ -22,4 +22,10 @@ export const removeProperties = <T>(data: any, names: string[]) =>
delete data[ name ];
}
return data as T;
};
};
// https://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex
export const escapeRegexString = (str: string) => {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

View File

@ -19,13 +19,14 @@ import { TProfileData } from "../../../types/profile.ts";
import { useTranslation } from "react-i18next";
import { ArrowIcon } from "../../mini/icons/ArrowIcon.tsx";
import { formatTime } from "../../../util/time.ts";
import { FaCheck } from 'react-icons/fa6';
import { FaCheck } from "react-icons/fa6";
export const ProfileCard = ({ data }: { data: TProfileData }) =>
{
const [ showTrains, setShowTrains ] = useState(false);
const [ showStations, setShowStations ] = useState(false);
const [ sortTrainsBy, setSortTrainsBy ] = useState<"time" | "score" | "distance">("score");
const { t } = useTranslation();
return <div
@ -39,7 +40,8 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
</div>
<div className="mt-4">
<h3 className="mb-1.5 text-2xl font-semibold text-black dark:text-white">
{ data.steam.personname } { data.player.verified && <FaCheck className={ "inline text-meta-3 ml-1" }/> }
{ data.steam.personname } { data.player.verified &&
<FaCheck className={ "inline text-meta-3 ml-1" }/> }
</h3>
<div
@ -76,24 +78,27 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
{ t("profile.trains.train") }
</h5>
</div>
<div className="p-2.5 text-center xl:p-5">
<div className="p-2.5 text-center xl:p-5 cursor-pointer"
onClick={ () => setSortTrainsBy("distance") }>
<h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.distance") }
</h5>
</div>
<div className="hidden sm:block p-2.5 text-center xl:p-5">
<div className="hidden sm:block p-2.5 text-center xl:p-5 cursor-pointer"
onClick={ () => setSortTrainsBy("score") }>
<h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.points") }
</h5>
</div>
<div className="p-2.5 text-center xl:p-5">
<div className="p-2.5 text-center xl:p-5 cursor-pointer"
onClick={ () => setSortTrainsBy("time") }>
<h5 className="text-sm font-medium uppercase xsm:text-base">
{ t("profile.trains.time") }
</h5>
</div>
</div>
{ Object.keys(data.player.trainStats).map(trainName =>
{ Object.keys(data.player.trainStats).sort((a, b) => data.player.trainStats[ b ][ sortTrainsBy ] - data.player.trainStats[ a ][ sortTrainsBy ]).map(trainName =>
{
const train = data.player.trainStats[ trainName ];
@ -146,7 +151,7 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
</h5>
</div>
</div>
{ Object.keys(data.player.dispatcherStats).map(stationName =>
{ Object.keys(data.player.dispatcherStats).sort((a, b) => data.player.dispatcherStats[ b ].time - data.player.dispatcherStats[ a ].time).map(stationName =>
{
const station = data.player.dispatcherStats[ stationName ];
return <div
@ -160,7 +165,7 @@ export const ProfileCard = ({ data }: { data: TProfileData }) =>
</div>
<div className="flex items-center justify-center p-2.5 lg:p-5">
<p className="text-meta-3">{formatTime(station.time) }</p>
<p className="text-meta-3">{ formatTime(station.time) }</p>
</div>
</div>;
}) }