0
0
mirror of https://github.com/sp-tarkov/server.git synced 2025-02-12 15:50:42 -05:00

Add MongoID validation to databaseService

- Validate that quests, traders, items and customizations all have MongoID IDs
- If any validation fails, output an error and stop server startup
This commit is contained in:
DrakiaXYZ 2024-11-22 23:01:11 -08:00
parent 8e8b97ac26
commit 9a8cf9a8a9
5 changed files with 73 additions and 3 deletions

View File

@ -66,6 +66,7 @@
"customisation-unable_to_find_suit_with_id": "Unable to find suit with offer id: %s",
"customisation-unable_to_get_trader_suits": "Unable to get suits from trader: %s",
"database-data_at_path_missing": "The database was unable to retrieve data from: [%s] Please ensure your configs are valid and the data at the location exists",
"database-invalid_data": "Invalid data detected in database, please remove any outdated mods. See previous error for invalid data. Server stopped.",
"database-no_location_found_with_id": "No location found with an Id of: %s in database",
"database-no_trader_found_with_id": "Unable to find trader: %s in database",
"dialog-chatbot_id_already_exists": "Chat bot: %s being registered already exists, unable to register bot",

View File

@ -6,11 +6,11 @@ import { ConfigTypes } from "@spt/models/enums/ConfigTypes";
import { IHttpConfig } from "@spt/models/spt/config/IHttpConfig";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { ConfigServer } from "@spt/servers/ConfigServer";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { WebSocketServer } from "@spt/servers/WebSocketServer";
import { IHttpListener } from "@spt/servers/http/IHttpListener";
import { LocalisationService } from "@spt/services/LocalisationService";
import { inject, injectAll, injectable } from "tsyringe";
import { DatabaseService } from "@spt/services/DatabaseService";
@injectable()
export class HttpServer {
@ -19,7 +19,7 @@ export class HttpServer {
constructor(
@inject("PrimaryLogger") protected logger: ILogger,
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("DatabaseService") protected databaseService: DatabaseService,
@inject("HttpServerHelper") protected httpServerHelper: HttpServerHelper,
@inject("LocalisationService") protected localisationService: LocalisationService,
@injectAll("HttpListener") protected httpListeners: IHttpListener[],
@ -36,6 +36,12 @@ export class HttpServer {
public load(): void {
this.started = false;
// If the database couldn't be validated, don't start the server
if (!this.databaseService.isDatabaseValid())
{
return;
}
/* create server */
const httpServer: Server = http.createServer();

View File

@ -21,16 +21,19 @@ import { ITemplates } from "@spt/models/spt/templates/ITemplates";
import { ILogger } from "@spt/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt/servers/DatabaseServer";
import { LocalisationService } from "@spt/services/LocalisationService";
import { HashUtil } from "@spt/utils/HashUtil";
import { inject, injectable } from "tsyringe";
@injectable()
export class DatabaseService {
protected locationConfig: ILocationConfig;
protected isDataValid: boolean;
constructor(
@inject("PrimaryLogger") protected logger: ILogger,
@inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("LocalisationService") protected localisationService: LocalisationService,
@inject("HashUtil") protected hashUtil: HashUtil,
) {}
/**
@ -322,4 +325,53 @@ export class DatabaseService {
return this.databaseServer.getTables().templates.locationServices;
}
/**
* Validates that the database doesn't contain invalid ID data
*/
public validateDatabase(): void {
const start = performance.now();
this.isDataValid =
this.validateTable(this.getQuests(), 'quest') &&
this.validateTable(this.getTraders(), 'trader') &&
this.validateTable(this.getItems(), 'item') &&
this.validateTable(this.getCustomization(), 'customization');
if (!this.isDataValid)
{
this.logger.error(this.localisationService.getText("database-invalid_data"));
}
const validateTime = performance.now() - start
this.logger.debug(`ID validation took: ${validateTime.toFixed(2)}ms`);
}
/**
* Validate that the given table only contains valid MongoIDs
* @param table Table to validate for MongoIDs
* @param tableType The type of table, used in output message
* @returns True if the table only contains valid data
*/
private validateTable(table: Record<string, any>, tableType: string): boolean
{
for (const tableId in table)
{
if (!this.hashUtil.isValidMongoId(tableId))
{
this.logger.error(`Invalid ${tableType} ID: '${tableId}'`);
return false;
}
}
return true;
}
/**
* Check if the database is valid
* @returns True if the database contains valid data, false otherwise
*/
public isDatabaseValid(): boolean {
return this.isDataValid;
}
}

View File

@ -57,6 +57,15 @@ export class PostDbLoadService {
// add items gets left out,causing warnings
this.itemBaseClassService.hydrateItemBaseClassCache();
// Validate that only mongoIds exist in items, quests, and traders
// Kill the startup if not.
// TODO: We can probably remove this in a couple versions
this.databaseService.validateDatabase();
if (!this.databaseService.isDatabaseValid())
{
return;
}
this.addCustomLooseLootPositions();
this.adjustMinReserveRaiderSpawnChance();

View File

@ -10,6 +10,7 @@ import { LocalisationService } from "@spt/services/LocalisationService";
import { EncodingUtil } from "@spt/utils/EncodingUtil";
import { TimeUtil } from "@spt/utils/TimeUtil";
import { inject, injectAll, injectable } from "tsyringe";
import { DatabaseService } from "@spt/services/DatabaseService";
@injectable()
export class App {
@ -23,6 +24,7 @@ export class App {
@inject("ConfigServer") protected configServer: ConfigServer,
@inject("EncodingUtil") protected encodingUtil: EncodingUtil,
@inject("HttpServer") protected httpServer: HttpServer,
@inject("DatabaseService") protected databaseService: DatabaseService,
@injectAll("OnLoad") protected onLoadComponents: OnLoad[],
@injectAll("OnUpdate") protected onUpdateComponents: OnUpdate[],
) {
@ -58,7 +60,7 @@ export class App {
protected async update(onUpdateComponents: OnUpdate[]): Promise<void> {
// If the server has failed to start, skip any update calls
if (!this.httpServer.isStarted()) {
if (!this.httpServer.isStarted() || !this.databaseService.isDatabaseValid()) {
return;
}