🧱 Solar Network SDK in Dart

This commit is contained in:
2026-02-08 01:30:59 +08:00
parent a05cb390eb
commit 56d786853d
20 changed files with 1961 additions and 0 deletions

View 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`

View 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'),
),
],
),
),
);
}
}

View 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"

View 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

View 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';

View File

@@ -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,
);
}
}

View File

@@ -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(),
};
}
}

View File

@@ -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();
}
}

View File

@@ -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});
}

View File

@@ -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();
}
}
}

View File

@@ -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';
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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,
);
}
}

View File

@@ -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';
}
}
}

View File

@@ -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;
}

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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)';
}
}

View 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"

View 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