Initial project structure and core files
Add base project files including environment example, license, README, .gitignore, error classes, ESLint config, database modules, texture assets, repositories, routes, schemas, services, and server entry point. This establishes the foundational structure for a Yggdrasil-compatible REST API with modular error handling, database setup, and route organization.
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
class DefaultError extends Error {
|
||||
constructor(code, message, error) {
|
||||
super(message)
|
||||
this.code = code
|
||||
this.error = error
|
||||
this.message = message || "Internal Server Error"
|
||||
this.isOperational = true
|
||||
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
}
|
||||
|
||||
serialize() {
|
||||
const response = {
|
||||
code: this.code,
|
||||
message: this.message,
|
||||
}
|
||||
if (this.error) {
|
||||
response.error = this.error
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DefaultError
|
||||
@@ -0,0 +1,43 @@
|
||||
class ServiceError extends Error {
|
||||
constructor(code, path, error, errorMessage, details = null) {
|
||||
super(errorMessage || error || "Accounts API Error")
|
||||
this.code = code
|
||||
this.path = path
|
||||
this.errorType = error
|
||||
this.errorMessage = errorMessage
|
||||
this.developerMessage = errorMessage
|
||||
this.details = details
|
||||
this.isOperational = true
|
||||
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
}
|
||||
|
||||
serialize() {
|
||||
const response = {}
|
||||
|
||||
if (this.path && this.path.trim() !== "") {
|
||||
response.path = this.path.replace(/:(\w+)/g, "<$1>")
|
||||
}
|
||||
|
||||
if (this.errorType && this.errorType.trim() !== "") {
|
||||
response.error = this.errorType
|
||||
response.errorType = this.errorType
|
||||
}
|
||||
|
||||
if (this.details) {
|
||||
response.details = this.details
|
||||
}
|
||||
|
||||
if (this.errorMessage && this.errorMessage.trim() !== "") {
|
||||
response.errorMessage = this.errorMessage
|
||||
}
|
||||
|
||||
if (this.developerMessage && this.developerMessage.trim() !== "") {
|
||||
response.developerMessage = this.developerMessage
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ServiceError
|
||||
@@ -0,0 +1,24 @@
|
||||
class SessionError extends Error {
|
||||
constructor(statusCode, error, errorMessage, path) {
|
||||
super(errorMessage)
|
||||
this.path = path
|
||||
this.error = error
|
||||
this.statusCode = statusCode
|
||||
this.errorMessage = errorMessage
|
||||
this.isOperational = true
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
}
|
||||
|
||||
serialize() {
|
||||
const response = {
|
||||
path: this.path,
|
||||
errorMessage: this.errorMessage
|
||||
}
|
||||
if (this.error != undefined) {
|
||||
response.error = this.error
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SessionError
|
||||
@@ -0,0 +1,119 @@
|
||||
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
|
||||
@@ -0,0 +1,26 @@
|
||||
class YggdrasilError extends Error {
|
||||
constructor(statusCode, error, errorMessage, cause) {
|
||||
super(errorMessage)
|
||||
this.statusCode = statusCode
|
||||
|
||||
this.error = error
|
||||
this.errorMessage = errorMessage
|
||||
this.cause = cause
|
||||
|
||||
this.isOperational = true
|
||||
Error.captureStackTrace(this, this.constructor)
|
||||
}
|
||||
|
||||
serialize() {
|
||||
const response = {
|
||||
error: this.error,
|
||||
errorMessage: this.errorMessage
|
||||
}
|
||||
if (this.cause) {
|
||||
response.cause = this.cause
|
||||
}
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = YggdrasilError
|
||||
@@ -0,0 +1,13 @@
|
||||
const DefaultError = require("./DefaultError")
|
||||
const ServiceError = require("./ServiceError")
|
||||
const SessionError = require("./SessionError")
|
||||
const YggdrasilError = require("./YggdrasilError")
|
||||
const ValidationError = require("./ValidationError")
|
||||
|
||||
module.exports = {
|
||||
DefaultError,
|
||||
SessionError,
|
||||
ServiceError,
|
||||
YggdrasilError,
|
||||
ValidationError
|
||||
}
|
||||
Reference in New Issue
Block a user