import 'dart:convert'; import 'package:flutter/foundation.dart'; import 'package:http/http.dart' as http; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/string_extension.dart'; import 'package:np_codegen/np_codegen.dart'; import 'package:to_string/to_string.dart'; import 'package:xml/xml.dart'; part 'api.g.dart'; part 'direct_api.dart'; part 'face_recognition_api.dart'; part 'files_api.dart'; part 'files_sharing_api.dart'; part 'systemtag_api.dart'; @toString class Response { Response(this.statusCode, this.headers, this.body); bool get isGood => _isHttpStatusGood(statusCode); @override String toString() => _$toString(); final int statusCode; @Format(r"...") final Map<String, String> headers; /// Content of the response body, String if isResponseString == true during /// request, Uint8List otherwise @Format( r"${kDebugMode ? body.toString().replaceAll(RegExp(r'\n\t'), '').slice(0, 200) : '...'}") final dynamic body; } class BasicAuth { BasicAuth(this.username, this.password); BasicAuth.fromAccount(Account account) : this( account.username2, account.password, ); @override toString() { final authString = base64.encode(utf8.encode("$username:$password")); return "Basic $authString"; } final String username; final String password; } @npLog class Api { Api(Account account) : _baseUrl = Uri.parse(account.url), _auth = BasicAuth.fromAccount(account); Api.fromBaseUrl(Uri baseUrl) : _baseUrl = baseUrl; ApiFiles files() => ApiFiles(this); ApiOcs ocs() => ApiOcs(this); ApiSystemtags systemtags() => ApiSystemtags(this); ApiSystemtagsRelations systemtagsRelations() => ApiSystemtagsRelations(this); static String getAuthorizationHeaderValue(Account account) { return BasicAuth.fromAccount(account).toString(); } Future<Response> request( String method, String endpoint, { Map<String, String>? header, Map<String, String>? queryParameters, String? body, Uint8List? bodyBytes, bool isResponseString = true, }) async { final url = _makeUri(endpoint, queryParameters: queryParameters); final req = http.Request(method, url); if (_auth != null) { req.headers.addAll({ "authorization": _auth.toString(), }); } if (header != null) { // turn all to lower case, since HTTP headers are case-insensitive, this // smooths our processing (if any) req.headers.addEntries( header.entries.map((e) => MapEntry(e.key.toLowerCase(), e.value))); } if (body != null) { req.body = body; } else if (bodyBytes != null) { req.bodyBytes = bodyBytes; } final response = await http.Response.fromStream(await http.Client().send(req)); if (!_isHttpStatusGood(response.statusCode)) { if (response.statusCode == 404) { _log.severe( "[request] HTTP $method (${response.statusCode}): $endpoint", ); } else { _log.severe( "[request] HTTP $method (${response.statusCode}): $endpoint", response.body, ); } } return Response(response.statusCode, response.headers, isResponseString ? response.body : response.bodyBytes); } Uri _makeUri( String endpoint, { Map<String, String>? queryParameters, }) { final path = _baseUrl.path + "/$endpoint"; if (_baseUrl.scheme == "http") { return Uri.http(_baseUrl.authority, path, queryParameters); } else { return Uri.https(_baseUrl.authority, path, queryParameters); } } final Uri _baseUrl; BasicAuth? _auth; } bool _isHttpStatusGood(int status) => status ~/ 100 == 2; class ApiOcs { ApiOcs(this._api); ApiOcsDav dav() => ApiOcsDav(this); ApiOcsFacerecognition facerecognition() => ApiOcsFacerecognition(this); ApiOcsFilesSharing filesSharing() => ApiOcsFilesSharing(this); final Api _api; } class ApiOcsDav { ApiOcsDav(this._ocs); ApiOcsDavDirect direct() => ApiOcsDavDirect(this); final ApiOcs _ocs; }