Files
focusflow_api/lib/src/middleware/error_handler.dart
Oracle Public Cloud User 8958455a12 Initial scaffold: FocusFlow ADHD Task Manager backend
Dart Shelf API with modules: auth (JWT + PBKDF2), tasks (CRUD + dopamine scorer),
streaks (forgiveness + freeze), rewards (variable reward engine), time perception,
sync (offline-first push/pull), rooms (body doubling placeholder).
Includes DB migration (001_initial_schema.sql) and Docker Compose.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 15:53:40 +00:00

71 lines
2.3 KiB
Dart

import 'dart:convert';
import 'package:logging/logging.dart';
import 'package:shelf/shelf.dart';
final _log = Logger('ErrorHandler');
// ── ApiException ──────────────────────────────────────────────────────
/// Typed exception that maps directly to an HTTP status code.
class ApiException implements Exception {
final int statusCode;
final String message;
final dynamic errors;
const ApiException(this.statusCode, this.message, {this.errors});
// Factory constructors for common HTTP errors
factory ApiException.badRequest(String message, {dynamic errors}) =>
ApiException(400, message, errors: errors);
factory ApiException.unauthorized([String message = 'Unauthorized']) =>
ApiException(401, message);
factory ApiException.forbidden([String message = 'Forbidden']) =>
ApiException(403, message);
factory ApiException.notFound([String message = 'Resource not found']) =>
ApiException(404, message);
factory ApiException.conflict([String message = 'Conflict']) =>
ApiException(409, message);
factory ApiException.tooManyRequests(
[String message = 'Too many requests']) =>
ApiException(429, message);
@override
String toString() => 'ApiException($statusCode): $message';
}
// ── Middleware ─────────────────────────────────────────────────────────
/// Catches all exceptions and returns a consistent JSON error response.
Middleware errorHandlerMiddleware() {
return (Handler innerHandler) {
return (Request request) async {
try {
return await innerHandler(request);
} on ApiException catch (e) {
return _jsonError(e.statusCode, e.message, errors: e.errors);
} catch (e, st) {
_log.severe('Unhandled exception', e, st);
return _jsonError(500, 'Internal server error');
}
};
};
}
Response _jsonError(int statusCode, String message, {dynamic errors}) {
return Response(
statusCode,
body: jsonEncode({
'success': false,
'message': message,
if (errors != null) 'errors': errors,
}),
headers: {'Content-Type': 'application/json'},
);
}