- api_client.dart: connectTimeout 10s → 60s — Flutter was cancelling requests to /recommendations after 10s while the server waited for OpenAI, causing 'context canceled' on the server side - product/repository.go ListForPrompt: expires_at is computed via (added_at + storage_days * INTERVAL '1 day'), not a stored column; wrap in CTE to reference it by alias in SELECT/ORDER BY - home/handler.go getExpiringSoon: same fix — use CTE to compute expires_at before filtering/ordering by it Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
69 lines
2.1 KiB
Dart
69 lines
2.1 KiB
Dart
import 'package:dio/dio.dart';
|
|
import '../auth/secure_storage.dart';
|
|
import 'auth_interceptor.dart';
|
|
|
|
class ApiClient {
|
|
late final Dio _dio;
|
|
|
|
ApiClient({required String baseUrl, required SecureStorageService storage}) {
|
|
_dio = Dio(BaseOptions(
|
|
baseUrl: baseUrl,
|
|
connectTimeout: const Duration(seconds: 60),
|
|
receiveTimeout: const Duration(seconds: 120),
|
|
headers: {'Content-Type': 'application/json'},
|
|
));
|
|
|
|
_dio.interceptors.addAll([
|
|
AuthInterceptor(storage: storage, dio: _dio),
|
|
LogInterceptor(requestBody: true, responseBody: true),
|
|
]);
|
|
}
|
|
|
|
/// Exposed for testing only.
|
|
ApiClient.withDio(this._dio);
|
|
|
|
Future<Map<String, dynamic>> get(String path,
|
|
{Map<String, dynamic>? params}) async {
|
|
final response = await _dio.get(path, queryParameters: params);
|
|
return response.data;
|
|
}
|
|
|
|
Future<Map<String, dynamic>> post(String path, {dynamic data}) async {
|
|
final response = await _dio.post(path, data: data);
|
|
return response.data;
|
|
}
|
|
|
|
Future<Map<String, dynamic>> put(String path, {dynamic data}) async {
|
|
final response = await _dio.put(path, data: data);
|
|
return response.data;
|
|
}
|
|
|
|
Future<void> patch(String path,
|
|
{dynamic data, Map<String, dynamic>? params}) async {
|
|
await _dio.patch(path, data: data, queryParameters: params);
|
|
}
|
|
|
|
/// Posts data and expects a JSON array response.
|
|
Future<List<dynamic>> postList(String path, {dynamic data}) async {
|
|
final response = await _dio.post(path, data: data);
|
|
return response.data as List<dynamic>;
|
|
}
|
|
|
|
Future<Map<String, dynamic>> delete(String path) async {
|
|
final response = await _dio.delete(path);
|
|
return response.data;
|
|
}
|
|
|
|
/// Returns a list for endpoints that respond with a JSON array.
|
|
Future<List<dynamic>> getList(String path,
|
|
{Map<String, dynamic>? params}) async {
|
|
final response = await _dio.get(path, queryParameters: params);
|
|
return response.data as List<dynamic>;
|
|
}
|
|
|
|
/// Deletes a resource and expects no response body (204 No Content).
|
|
Future<void> deleteVoid(String path) async {
|
|
await _dio.delete(path);
|
|
}
|
|
}
|