diff --git a/README.md b/README.md index e773e2c..cb3b2d6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # DevTop Essential +## What's different in this fork + + + + [![Build Status](https://travis-ci.com/zonayedpca/DevTop.svg?branch=master)](https://travis-ci.com/zonayedpca/DevTop) [![Build status](https://ci.appveyor.com/api/projects/status/pt576s64fh4n0e23?svg=true)](https://ci.appveyor.com/project/zonayedpca/devtop) diff --git a/src/services/apiService.js b/src/services/apiService.js new file mode 100644 index 0000000..8a874cb --- /dev/null +++ b/src/services/apiService.js @@ -0,0 +1,59 @@ +class ApiError extends Error { + constructor(message, status) { + super(message); + this.name = 'ApiError'; + this.status = status; + } +} + +async function handleResponse(response) { + if (response.ok) { + // A 204 No Content response has no body to parse. + if (response.status === 204) { + return null; + } + return response.json(); + } + + let serverMessage = `Request failed with status: ${response.status}`; + try { + const errorBody = await response.json(); + // Use the server's specific error message if it provides one. + serverMessage = errorBody.message || serverMessage; + } catch (e) { + // Ignore if the response body isn't valid JSON. + } + + // This is our centralized logic for creating user-friendly error messages. + let userFriendlyMessage = "An unexpected error occurred. Please try again."; + + if (response.status >= 500) { + userFriendlyMessage = "There's a problem with our server. Please try again in a few moments."; + } else if (response.status === 404) { + userFriendlyMessage = "We couldn't find what you were looking for."; + } else if (response.status === 401 || response.status === 403) { + userFriendlyMessage = "You don't have permission to do that."; + } else if (response.status >= 400) { + // For other client-side errors (like validation), the server's message is often best. + userFriendlyMessage = serverMessage; + } + + throw new ApiError(userFriendlyMessage, response.status); +} + +async function apiFetch(endpoint, options = {}) { + const { body, ...customConfig } = options; + + const headers = { 'Content-Type': 'application/json' }; + + const config = { + method: body ? 'POST' : 'GET', + ...customConfig, + headers: { + ...headers, + ...customConfig.headers, + }, + }; + + if (body) { + config \ No newline at end of file diff --git a/src/utils/errorHandler.js b/src/utils/errorHandler.js new file mode 100644 index 0000000..8068ed3 --- /dev/null +++ b/src/utils/errorHandler.js @@ -0,0 +1,41 @@ +/** + * A custom error class to distinguish between expected application errors + * and unexpected system errors. This allows us to show a friendly message + * for known issues and a generic one for everything else. + */ +export class AppError extends Error { + constructor(message, userMessage) { + super(message); // The 'internal' message for developers + this.name = 'AppError'; + this.userMessage = userMessage || 'An unexpected error occurred. Please try again.'; + } +} + +/** + * Centralized error handler. + * + * This function ensures all errors are handled consistently. It logs the full + * technical error for debugging purposes and displays a clean, user-friendly + * message to the console. It then exits the process with a failure code. + * + * @param {Error | AppError} error - The error object to handle. + */ +export function handleError(error) { + // For the developer: log the full error to the console for debugging. + console.error('\n[Full Error Log]'); + console.error(error); + console.error('----------------\n'); + + // For the user: show a clear, helpful message. + let userMessage = 'An unexpected error occurred. Please check the log above for details.'; + + if (error instanceof AppError) { + userMessage = error.userMessage; + } + + // Display the final message to stderr. + console.error(`❌ Error: ${userMessage}`); + + // Exit with a failure code, which is standard practice for CLI tools. + process.exit(1); +} \ No newline at end of file