feat(monitoring): add Redis exporter + Sentry docs to deployment guide
Some checks failed
Some checks failed
- Add redis-exporter container to docker-compose (oliver006/redis_exporter, 32MB) - Add Redis scrape target to Prometheus config - Add 4 Redis alert rules: RedisDown, HighMemory, HighConnections, RejectedConnections - Document Step 19b (Sentry Error Tracking) in Hetzner deployment guide - Document Step 19c (Redis Monitoring) in Hetzner deployment guide - Update resource budget and port reference tables Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,7 @@ apiClient.interceptors.response.use(
|
||||
};
|
||||
throw apiError;
|
||||
}
|
||||
|
||||
|
||||
// Handle network errors
|
||||
if (error.code === 'ECONNABORTED') {
|
||||
throw {
|
||||
@@ -58,7 +58,7 @@ apiClient.interceptors.response.use(
|
||||
statusCode: 408
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
throw {
|
||||
errorCode: 'NETWORK_ERROR',
|
||||
message: 'Network error. Please check your connection.',
|
||||
@@ -105,7 +105,7 @@ class ApiClient {
|
||||
if (error.errorCode) {
|
||||
throw error; // Re-throw API errors
|
||||
}
|
||||
|
||||
|
||||
// Handle network errors
|
||||
throw {
|
||||
errorCode: 'NETWORK_ERROR',
|
||||
@@ -130,28 +130,28 @@ export const ERROR_MESSAGES = {
|
||||
INVALID_CREDENTIALS: 'Invalid username or password. Please try again.',
|
||||
TOKEN_EXPIRED: 'Your session has expired. Please log in again.',
|
||||
USER_NOT_ACTIVE: 'Your account has been deactivated. Contact support.',
|
||||
|
||||
|
||||
// MarketplaceProduct errors
|
||||
PRODUCT_NOT_FOUND: 'MarketplaceProduct not found. It may have been removed.',
|
||||
PRODUCT_ALREADY_EXISTS: 'A product with this ID already exists.',
|
||||
INVALID_PRODUCT_DATA: 'Please check the product information and try again.',
|
||||
|
||||
|
||||
// Inventory errors
|
||||
INSUFFICIENT_INVENTORY: 'Not enough inventory available for this operation.',
|
||||
INVENTORY_NOT_FOUND: 'No inventory information found for this product.',
|
||||
NEGATIVE_INVENTORY_NOT_ALLOWED: 'Inventory quantity cannot be negative.',
|
||||
|
||||
|
||||
// Shop errors
|
||||
SHOP_NOT_FOUND: 'Shop not found or no longer available.',
|
||||
UNAUTHORIZED_SHOP_ACCESS: 'You do not have permission to access this shop.',
|
||||
SHOP_ALREADY_EXISTS: 'A shop with this code already exists.',
|
||||
MAX_SHOPS_REACHED: 'You have reached the maximum number of shops allowed.',
|
||||
|
||||
|
||||
// Import errors
|
||||
IMPORT_JOB_NOT_FOUND: 'Import job not found.',
|
||||
IMPORT_JOB_CANNOT_BE_CANCELLED: 'This import job cannot be cancelled at this time.',
|
||||
MARKETPLACE_CONNECTION_FAILED: 'Failed to connect to marketplace. Please try again.',
|
||||
|
||||
|
||||
// Generic fallbacks
|
||||
VALIDATION_ERROR: 'Please check your input and try again.',
|
||||
NETWORK_ERROR: 'Connection error. Please check your internet connection.',
|
||||
@@ -179,7 +179,7 @@ export const useApiError = () => {
|
||||
const handleApiCall = async (apiCall) => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
|
||||
try {
|
||||
const result = await apiCall();
|
||||
setIsLoading(false);
|
||||
@@ -233,7 +233,7 @@ const ProductForm = () => {
|
||||
if (apiError.field) {
|
||||
setFieldErrors({ [apiError.field]: apiError.message });
|
||||
}
|
||||
|
||||
|
||||
// Handle specific error codes
|
||||
switch (apiError.errorCode) {
|
||||
case 'PRODUCT_ALREADY_EXISTS':
|
||||
@@ -312,7 +312,7 @@ class ErrorBoundary extends React.Component {
|
||||
|
||||
componentDidCatch(error, errorInfo) {
|
||||
console.error('Error caught by boundary:', error, errorInfo);
|
||||
|
||||
|
||||
// Log to error reporting service
|
||||
if (window.errorReporting) {
|
||||
window.errorReporting.captureException(error, {
|
||||
@@ -442,7 +442,7 @@ const handleInventoryUpdate = async (gtin, location, quantity) => {
|
||||
switch (error.errorCode) {
|
||||
case 'INSUFFICIENT_INVENTORY':
|
||||
const { available_quantity, requested_quantity } = error.details;
|
||||
notificationManager.notify('error',
|
||||
notificationManager.notify('error',
|
||||
`Cannot remove ${requested_quantity} items. Only ${available_quantity} available.`
|
||||
);
|
||||
break;
|
||||
@@ -493,7 +493,7 @@ const ImportJobStatus = ({ jobId }) => {
|
||||
switch (error.errorCode) {
|
||||
case 'IMPORT_JOB_CANNOT_BE_CANCELLED':
|
||||
const { current_status } = error.details;
|
||||
notificationManager.notify('error',
|
||||
notificationManager.notify('error',
|
||||
`Cannot cancel job in ${current_status} status`
|
||||
);
|
||||
break;
|
||||
@@ -549,12 +549,12 @@ export const logError = (error, context = {}) => {
|
||||
try {
|
||||
const existingErrors = JSON.parse(localStorage.getItem('apiErrors') || '[]');
|
||||
existingErrors.push(errorData);
|
||||
|
||||
|
||||
// Keep only last 50 errors
|
||||
if (existingErrors.length > 50) {
|
||||
existingErrors.splice(0, existingErrors.length - 50);
|
||||
}
|
||||
|
||||
|
||||
localStorage.setItem('apiErrors', JSON.stringify(existingErrors));
|
||||
} catch (e) {
|
||||
console.warn('Failed to store error locally:', e);
|
||||
|
||||
Reference in New Issue
Block a user