import 'dart:convert'; import 'package:result_monad/result_monad.dart'; import '../models/exec_error.dart'; import '../models/server_data.dart'; import '../utils/network_utils.dart'; class FediverseServiceValidator { static const softwareTypeDiaspora = 'diaspora'; final knownServers = {}; FutureResult getServerData(String url) async { final uri = Uri.tryParse(url); if (uri == null || uri.scheme != 'https') { return buildErrorResult( type: ErrorType.parsingError, message: 'Invalid URL: $url', ); } final domain = uri.host; ServerData? data = knownServers[domain]; if (data != null) { return Result.ok(data); } final serverData = await refreshServerData(domain); knownServers[domain] = serverData; return Result.ok(serverData); } FutureResult checkIfFediverseLink(String url) async { final dataResult = await getServerData(url); return dataResult.mapValue((d) => d.isFediverse); } static Future refreshServerData(String domainName) async { final uri = Uri.https( domainName, '/.well-known/nodeinfo', ); final result = await getUrl(uri) .andThenSuccessAsync((page) async { return jsonDecode(page.data); }) .andThenAsync( (json) async => json is Map ? Result.ok(json) : Result.error(''), ) .andThenSuccessAsync((json) async => json['links'] ?? []) .andThenAsync( (nodeInfos) async => nodeInfos.isNotEmpty ? Result.ok(nodeInfos.last) : Result.error(''), ) .andThenAsync((nodeInfo) async { final rel = nodeInfo['rel']?.toString() ?? ''; if (!rel.startsWith('http://nodeinfo.diaspora.software/ns/schema/')) { return Result.error(''); } final nodeInfoUrl = Uri.tryParse(nodeInfo['href'] ?? ''); if (nodeInfoUrl == null) { return Result.error(''); } return await getUrl(nodeInfoUrl); }) .andThenSuccessAsync( (nodeInfoData) async => jsonDecode(nodeInfoData.data)) .andThenSuccessAsync((nodeInfoJson) async { final softwareName = nodeInfoJson['software']?['name']?.toString() ?? ''; final softwareVersion = nodeInfoJson['software']?['version']?.toString() ?? ''; final isFediverse = softwareName.isNotEmpty && softwareVersion.isNotEmpty; return ServerData( domainName: domainName, isFediverse: isFediverse, softwareName: softwareName, softwareVersion: softwareVersion, ); }); return result.getValueOrElse( () => ServerData(domainName: domainName, isFediverse: false)); } }