Refactor code structure for improved readability and maintainability

This commit is contained in:
2026-10-19 19:36:19 +03:00
parent 170a44c377
commit 5f7bffd6e4
17 changed files with 454 additions and 85 deletions

58
server/errors.js Normal file
View File

@@ -0,0 +1,58 @@
/**
* Errors the API reports to clients: an HTTP status, a stable machine-readable `code` and a
* message ready to show. Clients branch on the code, never on the wording of the message.
*
* 400 bad_request the request itself is wrong (missing or malformed parameters)
* 404 unknown_endpoint no such API address: the server is older than the client
* 404 document_not_found the document left the catalog
* 404 group_not_found the document has no such group
* 413 file_too_large an import over 10 MB
* 422 unreadable_file an import that is not a readable Word table
* 429 too_many_requests the client sends too much; Retry-After says when to come back
* 429 busy the server is parsing other imports
* 502 source_unavailable the university catalog did not answer
* 502 source_invalid the catalog answered, but its folders or tables are not recognised
* 504 source_timeout the university catalog answered too slowly
* 500 internal a fault of the server itself
*/
export class ApiError extends Error {
constructor(status, code, message) {
super(message);
this.status = status;
this.code = code;
}
}
export const apiError = (status, code, message) => new ApiError(status, code, message);
const SOURCE_DOWN = "Каталог университета сейчас не отвечает. Попробуйте позже.";
const SOURCE_SLOW = "Каталог университета отвечает слишком долго. Попробуйте позже.";
/**
* A failed download from the university as an API error: curl's exit code 28 (or our own
* kill on the time limit) is a timeout, any other failure means the source is unavailable.
*/
export function sourceError(error) {
if (error instanceof ApiError) return error;
const timedOut = error?.code === 28 || error?.killed || error?.signal === "SIGTERM";
return timedOut
? new ApiError(504, "source_timeout", SOURCE_SLOW)
: new ApiError(502, "source_unavailable", SOURCE_DOWN);
}
/** Status, code and message for any error that reached the error handler. */
export function describe(error) {
if (error instanceof ApiError)
return { status: error.status, code: error.code, message: error.message };
if (error?.code === "LIMIT_FILE_SIZE")
return {
status: 413,
code: "file_too_large",
message: "Файл больше 10 МБ. Выберите документ меньшего размера.",
};
return {
status: 500,
code: "internal",
message: "Не удалось загрузить расписание. Проверьте подключение и попробуйте ещё раз.",
};
}