🧱 Solar Network SDK in Dart
This commit is contained in:
71
packages/solar_network_sdk/README.md
Normal file
71
packages/solar_network_sdk/README.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# Solar Network SDK
|
||||
|
||||
A Flutter SDK for interacting with the Solar Network API.
|
||||
|
||||
## Features
|
||||
|
||||
- **Web Authentication**: Local HTTP server for web-based authentication flows
|
||||
- **Token Management**: Secure token storage with SharedPreferences support
|
||||
- **Network Status**: Track online/offline/maintenance states
|
||||
- **Device Info**: Platform-aware user agent generation
|
||||
|
||||
## Installation
|
||||
|
||||
Add to your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
solar_network_sdk:
|
||||
path: ../packages/solar_network_sdk
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Initialization
|
||||
|
||||
```dart
|
||||
import 'package:solar_network_sdk/solar_network_sdk.dart';
|
||||
|
||||
final tokenStorage = SharedPreferencesTokenStorage(key: 'solar_token');
|
||||
final sdk = SolarNetworkSDK.create(
|
||||
serverUrl: 'https://api.solian.app',
|
||||
tokenStorage: tokenStorage,
|
||||
);
|
||||
```
|
||||
|
||||
### Web Authentication
|
||||
|
||||
```dart
|
||||
final server = WebAuthServer(
|
||||
getToken: () => tokenStorage.getToken(),
|
||||
webUrl: 'https://app.solian.fr',
|
||||
getDio: () => sdk.apiClient,
|
||||
);
|
||||
|
||||
final port = await server.start();
|
||||
final authUrl = 'https://app.solian.fr/auth/web?port=$port';
|
||||
|
||||
// Open authUrl in browser, then wait for auth
|
||||
final result = await WebAuthClient(
|
||||
baseUrl: 'http://127.0.0.1',
|
||||
port: port,
|
||||
webUrl: 'https://app.solian.fr',
|
||||
).waitForAuth();
|
||||
```
|
||||
|
||||
### Token Management
|
||||
|
||||
```dart
|
||||
// Get stored token
|
||||
final token = await tokenStorage.getToken();
|
||||
|
||||
// Clear token on logout
|
||||
await tokenStorage.clearToken();
|
||||
```
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `dio: ^5.9.1`
|
||||
- `shared_preferences: ^2.5.4`
|
||||
- `device_info_plus: ^11.3.0`
|
||||
- `package_info_plus: ^9.0.0`
|
||||
90
packages/solar_network_sdk/example/lib/main.dart
Normal file
90
packages/solar_network_sdk/example/lib/main.dart
Normal file
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:solar_network_sdk/solar_network_sdk.dart';
|
||||
|
||||
void main() {
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(title: 'SDK Example', home: const HomePage());
|
||||
}
|
||||
}
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
|
||||
@override
|
||||
State<HomePage> createState() => _HomePageState();
|
||||
}
|
||||
|
||||
class _HomePageState extends State<HomePage> {
|
||||
late final Dio _dio;
|
||||
late final SharedPreferencesTokenStorage _tokenStorage;
|
||||
late final WebAuthServer _webAuthServer;
|
||||
int? _serverPort;
|
||||
String _status = 'Not started';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_dio = Dio(BaseOptions(baseUrl: 'https://api.solian.app'));
|
||||
_tokenStorage = SharedPreferencesTokenStorage(key: 'solar_token');
|
||||
_webAuthServer = WebAuthServer(
|
||||
webUrl: 'https://app.solian.fr',
|
||||
getDio: () => _dio,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _startAuthServer() async {
|
||||
try {
|
||||
final port = await _webAuthServer.start();
|
||||
setState(() {
|
||||
_serverPort = port;
|
||||
_status = 'Running on port $port';
|
||||
});
|
||||
} catch (e) {
|
||||
setState(() {
|
||||
_status = 'Error: $e';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _stopAuthServer() {
|
||||
_webAuthServer.stop();
|
||||
setState(() {
|
||||
_serverPort = null;
|
||||
_status = 'Stopped';
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Solar Network SDK Example')),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text('Status: $_status'),
|
||||
const SizedBox(height: 20),
|
||||
if (_serverPort == null)
|
||||
ElevatedButton(
|
||||
onPressed: _startAuthServer,
|
||||
child: const Text('Start Auth Server'),
|
||||
)
|
||||
else
|
||||
ElevatedButton(
|
||||
onPressed: _stopAuthServer,
|
||||
child: const Text('Stop Auth Server'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
465
packages/solar_network_sdk/example/pubspec.lock
Normal file
465
packages/solar_network_sdk/example/pubspec.lock
Normal file
@@ -0,0 +1,465 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
device_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.5.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_platform_interface
|
||||
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.3"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.1"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.20"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
solar_network_sdk:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32_registry
|
||||
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sdks:
|
||||
dart: ">=3.10.0 <4.0.0"
|
||||
flutter: ">=3.35.0"
|
||||
22
packages/solar_network_sdk/example/pubspec.yaml
Normal file
22
packages/solar_network_sdk/example/pubspec.yaml
Normal file
@@ -0,0 +1,22 @@
|
||||
name: solar_network_sdk_example
|
||||
description: "Example app for Solar Network SDK"
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ^3.10.0
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
solar_network_sdk:
|
||||
path: ../
|
||||
dio: ^5.9.1
|
||||
shared_preferences: ^2.5.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
flutter:
|
||||
uses-material-design: true
|
||||
12
packages/solar_network_sdk/lib/solar_network_sdk.dart
Normal file
12
packages/solar_network_sdk/lib/solar_network_sdk.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
export 'src/core/network/constants/endpoints.dart';
|
||||
export 'src/core/network/models/app_token.dart';
|
||||
export 'src/core/network/network_status_service.dart';
|
||||
export 'src/core/network/models/user_agent.dart';
|
||||
export 'src/core/storage/token_storage.dart';
|
||||
export 'src/core/storage/storage_interface.dart';
|
||||
export 'src/core/utils/device_info.dart';
|
||||
export 'src/auth/models/auth_session.dart';
|
||||
export 'src/auth/models/auth_challenge.dart';
|
||||
export 'src/auth/services/auth_service.dart';
|
||||
export 'src/auth/web_auth/web_auth_server.dart';
|
||||
export 'src/auth/web_auth/web_auth_client.dart';
|
||||
@@ -0,0 +1,154 @@
|
||||
class SnAuthChallenge {
|
||||
final String id;
|
||||
final DateTime? expiredAt;
|
||||
final int stepRemain;
|
||||
final int stepTotal;
|
||||
final int failedAttempts;
|
||||
final List<String> blacklistFactors;
|
||||
final List<dynamic> audiences;
|
||||
final List<dynamic> scopes;
|
||||
final String ipAddress;
|
||||
final String userAgent;
|
||||
final String? nonce;
|
||||
final String? countryCode;
|
||||
final String? country;
|
||||
final String? city;
|
||||
final String accountId;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final DateTime? deletedAt;
|
||||
|
||||
SnAuthChallenge({
|
||||
required this.id,
|
||||
this.expiredAt,
|
||||
required this.stepRemain,
|
||||
required this.stepTotal,
|
||||
required this.failedAttempts,
|
||||
required this.blacklistFactors,
|
||||
required this.audiences,
|
||||
required this.scopes,
|
||||
required this.ipAddress,
|
||||
required this.userAgent,
|
||||
this.nonce,
|
||||
this.countryCode,
|
||||
this.country,
|
||||
this.city,
|
||||
required this.accountId,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.deletedAt,
|
||||
});
|
||||
|
||||
factory SnAuthChallenge.fromJson(Map<String, dynamic> json) {
|
||||
return SnAuthChallenge(
|
||||
id: json['id'] as String,
|
||||
expiredAt: json['expiredAt'] != null
|
||||
? DateTime.parse(json['expiredAt'] as String)
|
||||
: null,
|
||||
stepRemain: json['stepRemain'] as int,
|
||||
stepTotal: json['stepTotal'] as int,
|
||||
failedAttempts: json['failedAttempts'] as int,
|
||||
blacklistFactors: (json['blacklistFactors'] as List<dynamic>)
|
||||
.map((e) => e.toString())
|
||||
.toList(),
|
||||
audiences: json['audiences'] as List<dynamic>,
|
||||
scopes: json['scopes'] as List<dynamic>,
|
||||
ipAddress: json['ipAddress'] as String,
|
||||
userAgent: json['userAgent'] as String,
|
||||
nonce: json['nonce'] as String?,
|
||||
countryCode: json['countryCode'] as String?,
|
||||
country: json['country'] as String?,
|
||||
city: json['city'] as String?,
|
||||
accountId: json['accountId'] as String,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
deletedAt: json['deletedAt'] != null
|
||||
? DateTime.parse(json['deletedAt'] as String)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SnAuthFactor {
|
||||
final String id;
|
||||
final int type;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final DateTime? deletedAt;
|
||||
final DateTime? expiredAt;
|
||||
final DateTime? enabledAt;
|
||||
final int trustworthy;
|
||||
final Map<String, dynamic>? createdResponse;
|
||||
|
||||
SnAuthFactor({
|
||||
required this.id,
|
||||
required this.type,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.deletedAt,
|
||||
this.expiredAt,
|
||||
this.enabledAt,
|
||||
required this.trustworthy,
|
||||
this.createdResponse,
|
||||
});
|
||||
|
||||
factory SnAuthFactor.fromJson(Map<String, dynamic> json) {
|
||||
return SnAuthFactor(
|
||||
id: json['id'] as String,
|
||||
type: json['type'] as int,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
deletedAt: json['deletedAt'] != null
|
||||
? DateTime.parse(json['deletedAt'] as String)
|
||||
: null,
|
||||
expiredAt: json['expiredAt'] != null
|
||||
? DateTime.parse(json['expiredAt'] as String)
|
||||
: null,
|
||||
enabledAt: json['enabledAt'] != null
|
||||
? DateTime.parse(json['enabledAt'] as String)
|
||||
: null,
|
||||
trustworthy: json['trustworthy'] as int,
|
||||
createdResponse: json['createdResponse'] as Map<String, dynamic>?,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SnAccountConnection {
|
||||
final String id;
|
||||
final String accountId;
|
||||
final String provider;
|
||||
final String providedIdentifier;
|
||||
final Map<String, dynamic> meta;
|
||||
final DateTime lastUsedAt;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final DateTime? deletedAt;
|
||||
|
||||
SnAccountConnection({
|
||||
required this.id,
|
||||
required this.accountId,
|
||||
required this.provider,
|
||||
required this.providedIdentifier,
|
||||
this.meta = const {},
|
||||
required this.lastUsedAt,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.deletedAt,
|
||||
});
|
||||
|
||||
factory SnAccountConnection.fromJson(Map<String, dynamic> json) {
|
||||
return SnAccountConnection(
|
||||
id: json['id'] as String,
|
||||
accountId: json['accountId'] as String,
|
||||
provider: json['provider'] as String,
|
||||
providedIdentifier: json['providedIdentifier'] as String,
|
||||
meta: Map<String, dynamic>.from(json['meta'] as Map? ?? {}),
|
||||
lastUsedAt: DateTime.parse(json['lastUsedAt'] as String),
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
deletedAt: json['deletedAt'] != null
|
||||
? DateTime.parse(json['deletedAt'] as String)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
class SnAuthSession {
|
||||
final String id;
|
||||
final String? label;
|
||||
final DateTime lastGrantedAt;
|
||||
final DateTime? expiredAt;
|
||||
final List<dynamic> audiences;
|
||||
final List<dynamic> scopes;
|
||||
final String? ipAddress;
|
||||
final String? userAgent;
|
||||
final String? location;
|
||||
final int type;
|
||||
final String accountId;
|
||||
final DateTime createdAt;
|
||||
final DateTime updatedAt;
|
||||
final DateTime? deletedAt;
|
||||
|
||||
SnAuthSession({
|
||||
required this.id,
|
||||
this.label,
|
||||
required this.lastGrantedAt,
|
||||
this.expiredAt,
|
||||
required this.audiences,
|
||||
required this.scopes,
|
||||
this.ipAddress,
|
||||
this.userAgent,
|
||||
this.location,
|
||||
required this.type,
|
||||
required this.accountId,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
this.deletedAt,
|
||||
});
|
||||
|
||||
factory SnAuthSession.fromJson(Map<String, dynamic> json) {
|
||||
return SnAuthSession(
|
||||
id: json['id'] as String,
|
||||
label: json['label'] as String?,
|
||||
lastGrantedAt: DateTime.parse(json['lastGrantedAt'] as String),
|
||||
expiredAt: json['expiredAt'] != null
|
||||
? DateTime.parse(json['expiredAt'] as String)
|
||||
: null,
|
||||
audiences: json['audiences'] as List<dynamic>,
|
||||
scopes: json['scopes'] as List<dynamic>,
|
||||
ipAddress: json['ipAddress'] as String?,
|
||||
userAgent: json['userAgent'] as String?,
|
||||
location: json['location']?.toString(),
|
||||
type: json['type'] as int,
|
||||
accountId: json['accountId'] as String,
|
||||
createdAt: DateTime.parse(json['createdAt'] as String),
|
||||
updatedAt: DateTime.parse(json['updatedAt'] as String),
|
||||
deletedAt: json['deletedAt'] != null
|
||||
? DateTime.parse(json['deletedAt'] as String)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'label': label,
|
||||
'lastGrantedAt': lastGrantedAt.toIso8601String(),
|
||||
'expiredAt': expiredAt?.toIso8601String(),
|
||||
'audiences': audiences,
|
||||
'scopes': scopes,
|
||||
'ipAddress': ipAddress,
|
||||
'userAgent': userAgent,
|
||||
'location': location,
|
||||
'type': type,
|
||||
'accountId': accountId,
|
||||
'createdAt': createdAt.toIso8601String(),
|
||||
'updatedAt': updatedAt.toIso8601String(),
|
||||
'deletedAt': deletedAt?.toIso8601String(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import '../../core/storage/token_storage.dart';
|
||||
|
||||
class AuthService {
|
||||
final Dio _dio;
|
||||
final String _serverUrl;
|
||||
final TokenStorage _tokenStorage;
|
||||
|
||||
AuthService({
|
||||
required Dio dio,
|
||||
required String serverUrl,
|
||||
required TokenStorage tokenStorage,
|
||||
}) : _dio = dio,
|
||||
_serverUrl = serverUrl,
|
||||
_tokenStorage = tokenStorage;
|
||||
|
||||
Future<String> loginWithSession(
|
||||
String signedChallenge,
|
||||
String challenge,
|
||||
) async {
|
||||
final response = await _dio.post(
|
||||
'$_serverUrl/pass/auth/login/session',
|
||||
data: {'signedChallenge': signedChallenge, 'challenge': challenge},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final token = response.data['token'] as String;
|
||||
await _tokenStorage.setToken(token);
|
||||
return token;
|
||||
}
|
||||
|
||||
throw Exception('Login failed');
|
||||
}
|
||||
|
||||
Future<void> logout() async {
|
||||
await _tokenStorage.clearToken();
|
||||
}
|
||||
|
||||
Future<String?> getCurrentToken() async {
|
||||
return _tokenStorage.getToken();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
class WebAuthClient {
|
||||
final String _baseUrl;
|
||||
final int _port;
|
||||
final String _webUrl;
|
||||
|
||||
WebAuthClient({
|
||||
required String baseUrl,
|
||||
required int port,
|
||||
required String webUrl,
|
||||
}) : _baseUrl = baseUrl,
|
||||
_port = port,
|
||||
_webUrl = webUrl;
|
||||
|
||||
Future<String> getAuthenticationUrl() async {
|
||||
return '$_webUrl/auth/web?port=$_port';
|
||||
}
|
||||
|
||||
Future<WebAuthResult> waitForAuth() async {
|
||||
final client = http.Client();
|
||||
try {
|
||||
final response = await client.get(
|
||||
Uri.parse('http://127.0.0.1:$_port/alive'),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
return WebAuthResult(
|
||||
status: WebAuthStatus.challenge,
|
||||
challenge: data['challenge'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
throw Exception('Failed to get challenge');
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<WebAuthResult> exchangeToken(
|
||||
String signedChallenge, [
|
||||
Map<String, dynamic>? deviceInfo,
|
||||
]) async {
|
||||
final client = http.Client();
|
||||
try {
|
||||
final response = await client.post(
|
||||
Uri.parse('http://127.0.0.1:$_port/exchange'),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode({
|
||||
'signedChallenge': signedChallenge,
|
||||
if (deviceInfo != null) 'deviceInfo': deviceInfo,
|
||||
}),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
final data = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
return WebAuthResult(
|
||||
status: WebAuthStatus.success,
|
||||
token: data['token'] as String?,
|
||||
);
|
||||
} else {
|
||||
final errorData = jsonDecode(response.body) as Map<String, dynamic>;
|
||||
return WebAuthResult(
|
||||
status: WebAuthStatus.error,
|
||||
error: errorData['error'] as String?,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum WebAuthStatus { challenge, success, error }
|
||||
|
||||
class WebAuthResult {
|
||||
final WebAuthStatus status;
|
||||
final String? challenge;
|
||||
final String? token;
|
||||
final String? error;
|
||||
|
||||
WebAuthResult({required this.status, this.challenge, this.token, this.error});
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'package:dio/dio.dart';
|
||||
|
||||
class WebAuthServer {
|
||||
final String _webUrl;
|
||||
final Dio Function() _getDio;
|
||||
|
||||
HttpServer? _server;
|
||||
String? _challenge;
|
||||
DateTime? _challengeTimestamp;
|
||||
final _challengeTtl = const Duration(seconds: 30);
|
||||
final int _portStart;
|
||||
|
||||
WebAuthServer({
|
||||
required String webUrl,
|
||||
required Dio Function() getDio,
|
||||
int portStart = 40000,
|
||||
}) : _webUrl = webUrl,
|
||||
_getDio = getDio,
|
||||
_portStart = portStart;
|
||||
|
||||
Future<int> start() async {
|
||||
if (_server != null) {
|
||||
return _server!.port;
|
||||
}
|
||||
|
||||
final port = await _findUnusedPort(_portStart, _portStart + 1000);
|
||||
_server = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
|
||||
_server!.listen(_handleRequest);
|
||||
return port;
|
||||
}
|
||||
|
||||
void stop() {
|
||||
_server?.close(force: true);
|
||||
_server = null;
|
||||
}
|
||||
|
||||
Future<int> _findUnusedPort(int start, int end) async {
|
||||
for (var port = start; port <= end; port++) {
|
||||
try {
|
||||
var socket = await ServerSocket.bind(
|
||||
InternetAddress.loopbackIPv4,
|
||||
port,
|
||||
);
|
||||
await socket.close();
|
||||
return port;
|
||||
} catch (_) {}
|
||||
}
|
||||
throw Exception('No unused port found in range $start-$end');
|
||||
}
|
||||
|
||||
String _generateChallenge() {
|
||||
final random = Random.secure();
|
||||
final values = List<int>.generate(32, (i) => random.nextInt(256));
|
||||
return base64Url.encode(values);
|
||||
}
|
||||
|
||||
void _addCorsHeaders(HttpResponse response) {
|
||||
response.headers.add('Access-Control-Allow-Origin', _webUrl);
|
||||
response.headers.add('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
||||
response.headers.add('Access-Control-Allow-Headers', '*');
|
||||
}
|
||||
|
||||
Future<void> _handleRequest(HttpRequest request) async {
|
||||
try {
|
||||
_addCorsHeaders(request.response);
|
||||
|
||||
if (request.method == 'OPTIONS') {
|
||||
request.response.statusCode = HttpStatus.noContent;
|
||||
await request.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.method == 'GET' && request.uri.path == '/alive') {
|
||||
await _handleAlive(request);
|
||||
} else if (request.method == 'POST' && request.uri.path == '/exchange') {
|
||||
await _handleExchange(request);
|
||||
} else {
|
||||
request.response.statusCode = HttpStatus.notFound;
|
||||
request.response.write(jsonEncode({'error': 'Not Found'}));
|
||||
await request.response.close();
|
||||
}
|
||||
} catch (e) {
|
||||
request.response.statusCode = HttpStatus.internalServerError;
|
||||
request.response.write(jsonEncode({'error': 'Internal Server Error'}));
|
||||
await request.response.close();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleAlive(HttpRequest request) async {
|
||||
_challenge = _generateChallenge();
|
||||
_challengeTimestamp = DateTime.now();
|
||||
|
||||
final response = {'status': 'ok', 'challenge': _challenge};
|
||||
|
||||
request.response.statusCode = HttpStatus.ok;
|
||||
request.response.headers.contentType = ContentType.json;
|
||||
request.response.write(jsonEncode(response));
|
||||
await request.response.close();
|
||||
}
|
||||
|
||||
Future<void> _handleExchange(HttpRequest request) async {
|
||||
if (_challenge == null ||
|
||||
_challengeTimestamp == null ||
|
||||
DateTime.now().difference(_challengeTimestamp!) > _challengeTtl) {
|
||||
request.response.statusCode = HttpStatus.badRequest;
|
||||
request.response.write(
|
||||
jsonEncode({
|
||||
'error': 'Invalid or expired challenge. Please call /alive first.',
|
||||
}),
|
||||
);
|
||||
await request.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
final requestBody = await utf8.decodeStream(request);
|
||||
Map<String, dynamic> data;
|
||||
try {
|
||||
data = jsonDecode(requestBody);
|
||||
} catch (e) {
|
||||
request.response.statusCode = HttpStatus.badRequest;
|
||||
request.response.write(jsonEncode({'error': 'Invalid JSON body'}));
|
||||
await request.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
final String? signedChallenge = data['signedChallenge'];
|
||||
final Map<String, dynamic>? deviceInfo = data['deviceInfo'];
|
||||
|
||||
if (signedChallenge == null) {
|
||||
request.response.statusCode = HttpStatus.badRequest;
|
||||
request.response.write(jsonEncode({'error': 'Missing signedChallenge'}));
|
||||
await request.response.close();
|
||||
return;
|
||||
}
|
||||
|
||||
final currentChallenge = _challenge!;
|
||||
_challenge = null;
|
||||
_challengeTimestamp = null;
|
||||
|
||||
try {
|
||||
final dio = _getDio();
|
||||
|
||||
final response = await dio.post(
|
||||
'/pass/auth/login/session',
|
||||
data: {
|
||||
'signedChallenge': signedChallenge,
|
||||
'challenge': currentChallenge,
|
||||
...?deviceInfo,
|
||||
},
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final webToken = response.data['token'];
|
||||
request.response.statusCode = HttpStatus.ok;
|
||||
request.response.write(jsonEncode({'token': webToken}));
|
||||
} else {
|
||||
throw Exception(
|
||||
'Backend exchange failed with status ${response.statusCode}',
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
request.response.statusCode = HttpStatus.internalServerError;
|
||||
request.response.write(jsonEncode({'error': e.toString()}));
|
||||
} finally {
|
||||
await request.response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
class Endpoints {
|
||||
static const baseAuth = '/pass/auth';
|
||||
static const loginSession = '$baseAuth/login/session';
|
||||
static const challenge = '$baseAuth/challenge';
|
||||
static const sessions = '$baseAuth/sessions';
|
||||
static const factors = '$baseAuth/factors';
|
||||
static const connections = '$baseAuth/connections';
|
||||
|
||||
static const baseDrive = '/drive';
|
||||
static const filesUpload = '$baseDrive/files/upload';
|
||||
static const files = '$baseDrive/files';
|
||||
static const filePools = '$baseDrive/pools';
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:dio/dio.dart';
|
||||
import '../models/app_token.dart';
|
||||
|
||||
abstract class TokenStorage {
|
||||
Future<AppToken?> getToken();
|
||||
Future<void> setToken(AppToken token);
|
||||
Future<void> clearToken();
|
||||
}
|
||||
|
||||
class InMemoryTokenStorage implements TokenStorage {
|
||||
AppToken? _token;
|
||||
|
||||
@override
|
||||
Future<AppToken?> getToken() async => _token;
|
||||
|
||||
@override
|
||||
Future<void> setToken(AppToken token) async => _token = token;
|
||||
|
||||
@override
|
||||
Future<void> clearToken() async => _token = null;
|
||||
}
|
||||
|
||||
class _AuthInterceptor extends Interceptor {
|
||||
final TokenStorage tokenStorage;
|
||||
|
||||
_AuthInterceptor({required this.tokenStorage});
|
||||
|
||||
@override
|
||||
Future<void> onRequest(
|
||||
RequestOptions options,
|
||||
RequestInterceptorHandler handler,
|
||||
) async {
|
||||
try {
|
||||
final token = await tokenStorage.getToken();
|
||||
if (token != null) {
|
||||
options.headers['Authorization'] = 'AtField ${token.token}';
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
return handler.next(options);
|
||||
}
|
||||
|
||||
@override
|
||||
void onResponse(Response response, ResponseInterceptorHandler handler) {
|
||||
return handler.next(response);
|
||||
}
|
||||
|
||||
@override
|
||||
void onError(DioException error, ErrorInterceptorHandler handler) {
|
||||
return handler.next(error);
|
||||
}
|
||||
}
|
||||
|
||||
class _RetryInterceptor extends Interceptor {
|
||||
final int retries;
|
||||
final List<Duration> retryDelays;
|
||||
|
||||
_RetryInterceptor({required this.retries, required this.retryDelays})
|
||||
: assert(retryDelays.length == retries);
|
||||
|
||||
@override
|
||||
Future<void> onError(
|
||||
DioException error,
|
||||
ErrorInterceptorHandler handler,
|
||||
) async {
|
||||
final requestOptions = error.requestOptions;
|
||||
if (requestOptions.method != 'GET') {
|
||||
return handler.next(error);
|
||||
}
|
||||
|
||||
int attempt = 0;
|
||||
while (attempt < retries) {
|
||||
attempt++;
|
||||
await Future.delayed(retryDelays[attempt - 1]);
|
||||
|
||||
try {
|
||||
final response = await Dio().fetch(requestOptions);
|
||||
return handler.resolve(response);
|
||||
} catch (e) {
|
||||
if (attempt == retries) {
|
||||
return handler.next(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
class AppToken {
|
||||
final String token;
|
||||
final DateTime? expiresAt;
|
||||
|
||||
AppToken({required this.token, this.expiresAt});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'token': token,
|
||||
if (expiresAt != null) 'expiresAt': expiresAt!.toIso8601String(),
|
||||
};
|
||||
}
|
||||
|
||||
factory AppToken.fromJson(Map<String, dynamic> json) {
|
||||
return AppToken(
|
||||
token: json['token'] as String,
|
||||
expiresAt: json['expiresAt'] != null
|
||||
? DateTime.parse(json['expiresAt'] as String)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class UserAgentConfig {
|
||||
final String appName;
|
||||
final String? customUserAgent;
|
||||
|
||||
UserAgentConfig({this.appName = 'SolarNetworkSDK', this.customUserAgent});
|
||||
|
||||
Future<String> build() async {
|
||||
if (customUserAgent != null) return customUserAgent!;
|
||||
|
||||
final platformInfo = await _getPlatformInfo();
|
||||
final packageInfo = await PackageInfo.fromPlatform();
|
||||
|
||||
return '$appName/${packageInfo.version}+${packageInfo.buildNumber} ($platformInfo)';
|
||||
}
|
||||
|
||||
Future<String> _getPlatformInfo() async {
|
||||
String sanitizeForHeader(String input) {
|
||||
return input.runes.map((rune) {
|
||||
if (rune >= 32 && rune <= 126) {
|
||||
return String.fromCharCode(rune);
|
||||
} else {
|
||||
return '_';
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
|
||||
if (kIsWeb) {
|
||||
final deviceInfo = await DeviceInfoPlugin().webBrowserInfo;
|
||||
return 'Web; ${sanitizeForHeader(deviceInfo.vendor ?? 'Unknown')}';
|
||||
} else if (Platform.isAndroid) {
|
||||
final deviceInfo = await DeviceInfoPlugin().androidInfo;
|
||||
return 'Android; ${sanitizeForHeader(deviceInfo.brand)} ${sanitizeForHeader(deviceInfo.model)}; ${sanitizeForHeader(deviceInfo.id)}';
|
||||
} else if (Platform.isIOS) {
|
||||
final deviceInfo = await DeviceInfoPlugin().iosInfo;
|
||||
return 'iOS; ${sanitizeForHeader(deviceInfo.model)}; ${sanitizeForHeader(deviceInfo.name)}';
|
||||
} else if (Platform.isMacOS) {
|
||||
final deviceInfo = await DeviceInfoPlugin().macOsInfo;
|
||||
return 'MacOS; ${sanitizeForHeader(deviceInfo.model)}; ${sanitizeForHeader(deviceInfo.hostName)}';
|
||||
} else if (Platform.isWindows) {
|
||||
final deviceInfo = await DeviceInfoPlugin().windowsInfo;
|
||||
return 'Windows NT; ${sanitizeForHeader(deviceInfo.productName)}; ${sanitizeForHeader(deviceInfo.computerName)}';
|
||||
} else if (Platform.isLinux) {
|
||||
final deviceInfo = await DeviceInfoPlugin().linuxInfo;
|
||||
return 'Linux; ${sanitizeForHeader(deviceInfo.prettyName)}';
|
||||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
enum NetworkStatus { online, notReady, maintenance, offline }
|
||||
|
||||
class NetworkStatusService {
|
||||
NetworkStatus _status = NetworkStatus.online;
|
||||
|
||||
NetworkStatus get currentStatus => _status;
|
||||
|
||||
void setOnline() => _status = NetworkStatus.online;
|
||||
void setMaintenance() => _status = NetworkStatus.maintenance;
|
||||
void setNotReady() => _status = NetworkStatus.notReady;
|
||||
void setOffline() => _status = NetworkStatus.offline;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
abstract class StorageInterface {
|
||||
dynamic get(String key);
|
||||
Future<bool> set(String key, dynamic value);
|
||||
Future<bool> remove(String key);
|
||||
Set<String> getKeys();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
abstract class TokenStorage {
|
||||
Future<String?> getToken();
|
||||
Future<void> setToken(String token);
|
||||
Future<void> clearToken();
|
||||
}
|
||||
|
||||
class InMemoryTokenStorage implements TokenStorage {
|
||||
String? _token;
|
||||
|
||||
@override
|
||||
Future<String?> getToken() async => _token;
|
||||
|
||||
@override
|
||||
Future<void> setToken(String token) async => _token = token;
|
||||
|
||||
@override
|
||||
Future<void> clearToken() async => _token = null;
|
||||
}
|
||||
|
||||
class SharedPreferencesTokenStorage implements TokenStorage {
|
||||
final String _key;
|
||||
|
||||
SharedPreferencesTokenStorage({required String key}) : _key = key;
|
||||
|
||||
@override
|
||||
Future<String?> getToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
return prefs.getString(_key);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setToken(String token) async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString(_key, token);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clearToken() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.remove(_key);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:device_info_plus/device_info_plus.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
|
||||
class DeviceInfo {
|
||||
static Future<String> getPlatformInfo() async {
|
||||
String sanitizeForHeader(String input) {
|
||||
return input.runes.map((rune) {
|
||||
if (rune >= 32 && rune <= 126) {
|
||||
return String.fromCharCode(rune);
|
||||
} else {
|
||||
return '_';
|
||||
}
|
||||
}).join();
|
||||
}
|
||||
|
||||
if (kIsWeb) {
|
||||
final deviceInfo = await DeviceInfoPlugin().webBrowserInfo;
|
||||
return 'Web; ${sanitizeForHeader(deviceInfo.vendor ?? 'Unknown')}';
|
||||
} else if (Platform.isAndroid) {
|
||||
final deviceInfo = await DeviceInfoPlugin().androidInfo;
|
||||
return 'Android; ${sanitizeForHeader(deviceInfo.brand)} ${sanitizeForHeader(deviceInfo.model)}; ${sanitizeForHeader(deviceInfo.id)}';
|
||||
} else if (Platform.isIOS) {
|
||||
final deviceInfo = await DeviceInfoPlugin().iosInfo;
|
||||
return 'iOS; ${sanitizeForHeader(deviceInfo.model)}; ${sanitizeForHeader(deviceInfo.name)}';
|
||||
} else if (Platform.isMacOS) {
|
||||
final deviceInfo = await DeviceInfoPlugin().macOsInfo;
|
||||
return 'MacOS; ${sanitizeForHeader(deviceInfo.model)}; ${sanitizeForHeader(deviceInfo.hostName)}';
|
||||
} else if (Platform.isWindows) {
|
||||
final deviceInfo = await DeviceInfoPlugin().windowsInfo;
|
||||
return 'Windows NT; ${sanitizeForHeader(deviceInfo.productName)}; ${sanitizeForHeader(deviceInfo.computerName)}';
|
||||
} else if (Platform.isLinux) {
|
||||
final deviceInfo = await DeviceInfoPlugin().linuxInfo;
|
||||
return 'Linux; ${sanitizeForHeader(deviceInfo.prettyName)}';
|
||||
} else {
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
static Future<String> buildUserAgent(
|
||||
String appName,
|
||||
String version,
|
||||
String buildNumber,
|
||||
) async {
|
||||
final platformInfo = await getPlatformInfo();
|
||||
return '$appName/$version+$buildNumber ($platformInfo)';
|
||||
}
|
||||
}
|
||||
458
packages/solar_network_sdk/pubspec.lock
Normal file
458
packages/solar_network_sdk/pubspec.lock
Normal file
@@ -0,0 +1,458 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
crypto:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
device_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.5.0"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: device_info_plus_platform_interface
|
||||
sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.3"
|
||||
dio:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dio
|
||||
sha256: b9d46faecab38fc8cc286f80bc4d61a3bb5d4ac49e51ed877b4d6706efe57b25
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.9.1"
|
||||
dio_web_adapter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dio_web_adapter
|
||||
sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
http:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.0.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: cbc40be9be1c5af4dab4d6e0de4d5d3729e6f3d65b89d21e1815d57705644a6f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.20"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.15.0"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32_registry
|
||||
sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sdks:
|
||||
dart: ">=3.10.0 <4.0.0"
|
||||
flutter: ">=3.35.0"
|
||||
25
packages/solar_network_sdk/pubspec.yaml
Normal file
25
packages/solar_network_sdk/pubspec.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
name: solar_network_sdk
|
||||
description: "Solar Network SDK for Flutter - Core networking, authentication, and cloud file services"
|
||||
version: 1.0.0
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=3.10.0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dio: ^5.9.1
|
||||
shared_preferences: ^2.5.4
|
||||
crypto: ^3.0.7
|
||||
uuid: ^4.5.2
|
||||
device_info_plus: ^11.3.0
|
||||
package_info_plus: ^9.0.0
|
||||
http: ^1.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^6.0.0
|
||||
Reference in New Issue
Block a user