Yggdrasil/errors/ValidationError.js
azures04 5cfadfd7ac Add validation schemas and improve texture handling
Introduces zod-based validation schemas for Minecraft and Mojang API endpoints. Refactors texture route to support hash-based file serving and removes the old static texture route. Updates database schema for player properties and adds an event to clean expired certificates. Improves ValidationError formatting, adjusts skin/cape URL construction, and adds SSRF protection for skin uploads.
2025-12-28 09:02:10 +01:00

119 lines
3.6 KiB
JavaScript

const DefaultError = require("./DefaultError")
const YggdrasilError = require("./YggdrasilError")
const SessionError = require("./SessionError")
const ServiceError = require("./ServiceError")
const logger = require("../modules/logger")
class ValidationError extends DefaultError {
constructor(result, config = {}, context = {}) {
let formattedErrors = []
if (result && result.error && Array.isArray(result.error.issues)) {
formattedErrors = result.error.issues.flatMap(issue => {
if (issue.code === "unrecognized_keys") {
return issue.keys.map(key => ({
field: [...issue.path, key].join("."),
message: "Field not allowed"
}))
}
if (issue.code === "invalid_union") {
return {
field: issue.path.join("."),
message: "Invalid input format (union mismatch)"
}
}
return {
field: issue.path.join("."),
message: issue.message
}
})
}
else if (result instanceof Error) {
formattedErrors = [{
field: "global",
message: result.message
}]
}
else if (typeof result === "string") {
formattedErrors = [{
field: "global",
message: result
}]
}
else {
formattedErrors = [{
field: "unknown",
message: "Unknown validation error"
}]
}
const message = config.message || "Validation failed"
const statusCode = config.code || 400
super(statusCode, message, { errors: formattedErrors })
this.config = config
this.formattedErrors = formattedErrors
this.context = context
this.logError()
}
logError() {
const { method, path, ip } = this.context
if (method && path) {
logger.warn(
`Validation failed for ${method} ${path} (${this.config.errorFormat || "Standard"}) ` +
`<IP:${ip || "Unknown"}>`,
["WEB", "yellow"]
)
}
}
serialize() {
if (this.config.errorFormat === "YggdrasilError") {
const err = new YggdrasilError(
this.code,
this.config.errorName || "IllegalArgumentException",
this.message,
JSON.stringify(this.formattedErrors)
)
return err.serialize()
}
if (this.config.errorFormat === "SessionError") {
const err = new SessionError(
this.code,
this.config.errorName || "Forbidden",
this.message,
this.context.path || ""
)
return err.serialize()
}
if (this.config.errorFormat === "ServiceError") {
const err = new ServiceError(
this.code,
this.context.path || "",
this.config.errorName || "ValidationException",
this.message,
this.formattedErrors
)
return err.serialize()
}
const response = {
code: this.code,
message: this.message,
errors: this.formattedErrors
}
if (this.cause) {
response.cause = this.cause
}
return response
}
}
module.exports = ValidationError