1
0
mirror of https://github.com/bpatrik/pigallery2.git synced 2025-11-29 22:48:28 +02:00

Fix special character searches. fixes #757, fixes #890

This commit is contained in:
Patrik J. Braun
2025-09-13 12:05:33 +02:00
parent bc5311c8c1
commit f3e0fbf44b
8 changed files with 76 additions and 36 deletions

View File

@@ -17,6 +17,45 @@ import {ServerTime} from './ServerTimingMWs';
import {SortByTypes} from '../../common/entities/SortingMethods';
export class GalleryMWs {
/**
* Middleware to safely parse searchQueryDTO from URL parameters
* Handles URL decoding and JSON parsing with proper error handling
*/
public static parseSearchQuery(
req: Request,
res: Response,
next: NextFunction
): void {
try {
if (!req.params['searchQueryDTO']) {
return next();
}
let rawQueryParam = req.params['searchQueryDTO'] as string;
let query: SearchQueryDTO;
try {
query = JSON.parse(rawQueryParam);
} catch (parseError) {
return next(
new ErrorDTO(
ErrorCodes.INPUT_ERROR,
'Invalid search query JSON: ' + parseError.message,
parseError
)
);
}
// Store the parsed query for use by subsequent middlewares
req.resultPipe = query;
return next();
} catch (err) {
return next(
new ErrorDTO(ErrorCodes.GENERAL_ERROR, 'Error parsing search query', err)
);
}
}
@ServerTime('1.db', 'List Directory')
public static async listDirectory(
req: Request,
@@ -78,13 +117,13 @@ export class GalleryMWs {
return next();
}
if (Config.Search.enabled === false || !req.params['searchQueryDTO']) {
if (Config.Search.enabled === false || !req.resultPipe) {
return next();
}
// Handle search-query-based zip
try {
const query: SearchQueryDTO = JSON.parse(req.params['searchQueryDTO'] as string);
const query: SearchQueryDTO = req.resultPipe as any;
// Get all media items from search
const searchResult = await ObjectManagers.getInstance().SearchManager.search(
@@ -251,15 +290,12 @@ export class GalleryMWs {
try {
if (
Config.Search.enabled === false ||
!req.params['searchQueryDTO']
!req.resultPipe
) {
return next();
}
const query: SearchQueryDTO = JSON.parse(
req.params['searchQueryDTO'] as string
);
const query: SearchQueryDTO = req.resultPipe as any;
const result = await ObjectManagers.getInstance().SearchManager.search(
req.session.context,
query
@@ -326,14 +362,12 @@ export class GalleryMWs {
try {
if (
Config.RandomPhoto.enabled === false ||
!req.params['searchQueryDTO']
!req.resultPipe
) {
return next();
}
const query: SearchQueryDTO = JSON.parse(
req.params['searchQueryDTO'] as string
);
const query: SearchQueryDTO = req.resultPipe as any;
const photos =
await ObjectManagers.getInstance().SearchManager.getNMedia(

View File

@@ -274,9 +274,10 @@ export class SharingMWs {
if (Config.Sharing.enabled === false) {
return next();
}
const query: SearchQueryDTO = JSON.parse(
req.params['searchQueryDTO'] as string
);
if (!req.resultPipe) {
return next();
}
const query: SearchQueryDTO = req.resultPipe as any;
if (req.session.context?.user.role >= UserRoles.Admin) {
req.resultPipe =
await ObjectManagers.getInstance().SharingManager.listAllForQuery(query);

View File

@@ -6,11 +6,13 @@ import {SessionContext} from '../../model/SessionContext';
declare global {
namespace Express {
interface Request {
// sending data between middlewares. Next middleware will expect the output of the previous one in this field.
resultPipe?: unknown;
body?: {
loginCredential?: LoginCredential;
};
locale?: string;
// Stored in the session cookie. Travels to the client side
session: {
context?: SessionContext;
rememberMe?: boolean;

View File

@@ -56,6 +56,7 @@ export class GalleryRouter {
AuthenticationMWs.authorise(UserRoles.LimitedGuest),
// specific part
GalleryMWs.parseSearchQuery,
ServerTimingMWs.addServerTiming,
GalleryMWs.zipDirectory
);
@@ -167,6 +168,7 @@ export class GalleryRouter {
VersionMWs.injectGalleryVersion,
// specific part
GalleryMWs.parseSearchQuery,
GalleryMWs.getRandomImage,
GalleryMWs.loadFile,
ServerTimingMWs.addServerTiming,
@@ -260,6 +262,7 @@ export class GalleryRouter {
VersionMWs.injectGalleryVersion,
// specific part
GalleryMWs.parseSearchQuery,
GalleryMWs.search,
ThumbnailGeneratorMWs.addThumbnailInformation,
GalleryMWs.cleanUpGalleryResults,

View File

@@ -2,6 +2,7 @@ import {AuthenticationMWs} from '../middlewares/user/AuthenticationMWs';
import {UserRoles} from '../../common/entities/UserDTO';
import {RenderingMWs} from '../middlewares/RenderingMWs';
import {SharingMWs} from '../middlewares/SharingMWs';
import {GalleryMWs} from '../middlewares/GalleryMWs';
import * as express from 'express';
import {QueryParams} from '../../common/QueryParams';
import {ServerTimingMWs} from '../middlewares/ServerTimingMWs';
@@ -104,6 +105,7 @@ export class SharingRouter {
[Config.Server.apiPath + '/share/list/:searchQueryDTO'],
AuthenticationMWs.authenticate,
AuthenticationMWs.authorise(UserRoles.User),
GalleryMWs.parseSearchQuery,
SharingMWs.listSharingForQuery,
ServerTimingMWs.addServerTiming,
RenderingMWs.renderSharingList

View File

@@ -108,7 +108,7 @@ export class ContentLoaderService {
let cw = this.galleryCacheService.getSearch(JSON.parse(query));
if (!cw || cw.searchResult == null) {
try {
cw = await this.networkService.getJson<ContentWrapperWithError>('/search/' + query);
cw = await this.networkService.getJson<ContentWrapperWithError>('/search/' + encodeURIComponent(query));
this.galleryCacheService.setSearch(cw);
} catch (e) {
cw = cw || {

View File

@@ -139,7 +139,7 @@ export class AutoCompleteService {
acParams[QueryParams.gallery.search.type] = type;
}
this.networkService
.getJson<IAutoCompleteItem[]>('/autocomplete/' + text, acParams)
.getJson<IAutoCompleteItem[]>('/autocomplete/' + encodeURIComponent(text), acParams)
.then((ret) => {
this.galleryCacheService.setAutoComplete(text, type, ret);
items.next(

View File

@@ -8,7 +8,7 @@ import {QueryParams} from '../../../../common/QueryParams';
import {UserDTO, UserRoles} from '../../../../common/entities/UserDTO';
import {Utils} from '../../../../common/Utils';
import {Config} from '../../../../common/config/public/Config';
import {SearchQueryDTO, SearchQueryTypes, TextSearch, TextSearchQueryMatchTypes} from '../../../../common/entities/SearchQueryDTO';
import {SearchQueryDTO} from '../../../../common/entities/SearchQueryDTO';
@Injectable()
@@ -169,6 +169,23 @@ export class ShareService {
return this.sharingKey != null;
}
public async getSharingListForQuery(
query: SearchQueryDTO
): Promise<SharingDTO[]> {
return this.networkService.getJson('/share/list/' + encodeURIComponent(JSON.stringify(query)));
}
public getSharingList(): Promise<SharingDTO[]> {
if (!Config.Sharing.enabled) {
return Promise.resolve([]);
}
return this.networkService.getJson('/share/listAll');
}
public deleteSharing(sharing: SharingDTO): Promise<void> {
return this.networkService.deleteJson('/share/' + encodeURIComponent(sharing.sharingKey));
}
private async getSharing(): Promise<void> {
try {
this.sharingSubject.next(null);
@@ -194,23 +211,4 @@ export class ShareService {
console.error(e);
}
}
public async getSharingListForQuery(
query: SearchQueryDTO
): Promise<SharingDTO[]> {
return this.networkService.getJson('/share/list/' + JSON.stringify(query));
}
public getSharingList(): Promise<SharingDTO[]> {
if (!Config.Sharing.enabled) {
return Promise.resolve([]);
}
return this.networkService.getJson('/share/listAll');
}
public deleteSharing(sharing: SharingDTO): Promise<void> {
return this.networkService.deleteJson('/share/' + sharing.sharingKey);
}
}