Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
59 changes: 59 additions & 0 deletions src/services/apiService.js
Original file line number Diff line number Diff line change
@@ -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
41 changes: 41 additions & 0 deletions src/utils/errorHandler.js
Original file line number Diff line number Diff line change
@@ -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);
}