Support more filters and sorting options on the backend.

This commit is contained in:
Fabio Manganiello 2025-02-22 20:51:46 +01:00
parent d95e44f8d2
commit 3cb0a371a6
4 changed files with 87 additions and 20 deletions

3
src/helpers/logging.ts Normal file
View file

@ -0,0 +1,3 @@
export function logRequest(req: any) {
console.log(`Request: ${req.method} ${req.url}`);
}

View file

@ -5,11 +5,13 @@ import dotenv from 'dotenv';
import { Db } from './db/Db'; import { Db } from './db/Db';
import { LocationRequest } from './models/LocationRequest'; import { LocationRequest } from './models/LocationRequest';
import { LocationRepository } from './repo/LocationRepository'; import { LocationRepository } from './repo/LocationRepository';
import { logRequest } from './helpers/logging';
dotenv.config(); dotenv.config();
const app = express(); const app = express();
const db = Db.fromEnv(); const db = Db.fromEnv();
const locationRepo = new LocationRepository(db);
const address = process.env.BACKEND_ADDRESS || '127.0.0.1'; const address = process.env.BACKEND_ADDRESS || '127.0.0.1';
const port = process.env.BACKEND_PORT || 3000; const port = process.env.BACKEND_PORT || 3000;
@ -19,7 +21,7 @@ app.use(express.static('frontend/dist'));
// API route // API route
app.get('/api/gpsdata', async (req, res) => { app.get('/api/gpsdata', async (req, res) => {
console.log(`[${req.ip}] GET ${req.originalUrl}`); logRequest(req);
let query: any = {}; let query: any = {};
try { try {
@ -32,7 +34,7 @@ app.get('/api/gpsdata', async (req, res) => {
} }
try { try {
const gpsData = await new LocationRepository(db).getHistory(query); const gpsData = await locationRepo.getHistory(query);
res.json(gpsData); res.json(gpsData);
} catch (error) { } catch (error) {
const e = `Error fetching data: ${error}`; const e = `Error fetching data: ${error}`;

View file

@ -1,38 +1,100 @@
import { Nullable } from './Types'; import { Nullable } from './Types';
import { Db } from '../db/Db';
import { Op } from 'sequelize';
class LocationRequest { class LocationRequest {
limit: Nullable<number> = 10; limit: Nullable<number> = 250;
offset: Nullable<number> = null; offset: Nullable<number> = null;
startDate: Nullable<Date> = null;
endDate: Nullable<Date> = null;
minId: Nullable<number> = null;
maxId: Nullable<number> = null;
country: Nullable<string> = null;
locality: Nullable<string> = null;
postalCode: Nullable<string> = null;
orderBy: string = 'timestamp';
order: string = 'DESC';
constructor(req: any) { constructor(req: any) {
if (req.limit != null) { this.initNumber('limit', req);
this.limit = parseInt(req.limit); this.initNumber('offset', req);
if (isNaN(this.limit)) { this.initDate('startDate', req);
throw new TypeError('Invalid limit'); this.initDate('endDate', req);
} this.initNumber('minId', req);
} this.initNumber('maxId', req);
this.country = req.country;
this.locality = req.locality;
this.postalCode = req.postalCode;
this.orderBy = req.orderBy || 'timestamp';
}
if (req.offset != null) { private initNumber(key: string, req: any): void {
this.offset = parseInt(req.offset); if (req[key] != null) {
if (isNaN(this.offset)) { const numValue = (this as any)[key] = parseInt(req[key]);
throw new TypeError('Invalid offset'); if (isNaN(numValue)) {
throw new TypeError(`Invalid value for ${key}: ${req[key]}`);
} }
} }
} }
public toMap(): any { private initDate(key: string, req: any): void {
let map: any = {}; if (req[key] != null) {
const numValue = (this as any)[key] = parseInt(req[key]);
const dateValue = (this as any)[key] = new Date(isNaN(numValue) ? req[key] : numValue);
if (isNaN(dateValue.getTime())) {
throw new TypeError(`Invalid value for ${key}: ${req[key]}`);
}
}
}
public toMap(db: Db): any {
let queryMap: any = {};
const where: any = {};
if (this.limit != null) { if (this.limit != null) {
map.limit = this.limit; queryMap.limit = this.limit;
} }
if (this.offset != null) { if (this.offset != null) {
map.offset = this.offset; queryMap.offset = this.offset;
} }
map.order = [['created_at', 'DESC']]; const colMapping: any = db.locationTableColumns
return map; if (this.startDate != null || this.endDate != null) {
const start = this.startDate == null ? 0 : this.startDate.getTime();
const end = this.endDate == null ? new Date().getTime() : this.endDate.getTime();
const column = colMapping.timestamp || 'timestamp';
const where_t: any = where[column] = {};
where_t[Op.between] = [start, end];
}
if (this.minId != null || this.maxId != null) {
const column = colMapping.id || 'id';
const where_id: any = where[column] = {};
if (this.minId == null && this.maxId != null) {
where_id[Op.lte] = this.maxId;
} else if (this.minId != null && this.maxId == null) {
where_id[Op.gte] = this.minId;
} else {
where_id[Op.between] = [this.minId, this.maxId];
}
}
if (this.country != null) {
where[colMapping.country || 'country'] = this.country;
}
if (this.locality != null) {
where[colMapping.locality || 'locality'] = this.locality;
}
if (this.postalCode != null) {
where[colMapping.postal_code || 'postal_code'] = this.postalCode;
}
queryMap.where = where;
queryMap.order = [[colMapping[this.orderBy], this.order]];
return queryMap;
} }
} }

View file

@ -13,7 +13,7 @@ export class LocationRepository {
let apiResponse: any[] = []; let apiResponse: any[] = [];
try { try {
apiResponse = await this.db.GpsData().findAll(query.toMap()); apiResponse = await this.db.GpsData().findAll(query.toMap(this.db));
} catch (error) { } catch (error) {
throw new Error(`Error fetching data: ${error}`); throw new Error(`Error fetching data: ${error}`);
} }