首次提交:太和e护APP调试模式websocket
This commit is contained in:
9
.dockerignore
Normal file
9
.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
||||
.dockerignore
|
||||
Dockerfile
|
||||
build/
|
||||
.dart_tool/
|
||||
.git/
|
||||
.github/
|
||||
.gitignore
|
||||
.idea/
|
||||
.packages
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
8
.idea/.gitignore
generated
vendored
Normal file
8
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
9
.idea/ble_debug_system.iml
generated
Normal file
9
.idea/ble_debug_system.iml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
6
.idea/misc.xml
generated
Normal file
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/ble_debug_system.iml" filepath="$PROJECT_DIR$/.idea/ble_debug_system.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
@@ -0,0 +1,3 @@
|
||||
## 1.0.0
|
||||
|
||||
- Initial version.
|
||||
21
Dockerfile
Normal file
21
Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
# Use latest stable channel SDK.
|
||||
FROM dart:stable AS build
|
||||
|
||||
# Resolve app dependencies.
|
||||
WORKDIR /app
|
||||
COPY pubspec.* ./
|
||||
RUN dart pub get
|
||||
|
||||
# Copy app source code (except anything in .dockerignore) and AOT compile app.
|
||||
COPY . .
|
||||
RUN dart compile exe bin/server.dart -o bin/server
|
||||
|
||||
# Build minimal serving image from AOT-compiled `/server`
|
||||
# and the pre-built AOT-runtime in the `/runtime/` directory of the base image.
|
||||
FROM scratch
|
||||
COPY --from=build /runtime/ /
|
||||
COPY --from=build /app/bin/server /app/bin/
|
||||
|
||||
# Start server.
|
||||
EXPOSE 8089
|
||||
CMD ["/app/bin/server"]
|
||||
49
README.md
Normal file
49
README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
A server app built using [Shelf](https://pub.dev/packages/shelf),
|
||||
configured to enable running with [Docker](https://www.docker.com/).
|
||||
|
||||
This sample code handles HTTP GET requests to `/` and `/echo/<message>`
|
||||
|
||||
# Running the sample
|
||||
|
||||
## Running with the Dart SDK
|
||||
|
||||
You can run the example with the [Dart SDK](https://dart.dev/get-dart)
|
||||
like this:
|
||||
|
||||
```
|
||||
$ dart run bin/server.dart
|
||||
Server listening on port 8080
|
||||
```
|
||||
|
||||
And then from a second terminal:
|
||||
```
|
||||
$ curl http://0.0.0.0:8080
|
||||
Hello, World!
|
||||
$ curl http://0.0.0.0:8080/echo/I_love_Dart
|
||||
I_love_Dart
|
||||
```
|
||||
|
||||
## Running with Docker
|
||||
|
||||
If you have [Docker Desktop](https://www.docker.com/get-started) installed, you
|
||||
can build and run with the `docker` command:
|
||||
|
||||
```
|
||||
$ docker build . -t myserver
|
||||
$ docker run -it -p 8080:8080 myserver
|
||||
Server listening on port 8080
|
||||
```
|
||||
|
||||
And then from a second terminal:
|
||||
```
|
||||
$ curl http://0.0.0.0:8080
|
||||
Hello, World!
|
||||
$ curl http://0.0.0.0:8080/echo/I_love_Dart
|
||||
I_love_Dart
|
||||
```
|
||||
|
||||
You should see the logging printed in the first terminal:
|
||||
```
|
||||
2021-05-06T15:47:04.620417 0:00:00.000158 GET [200] /
|
||||
2021-05-06T15:47:08.392928 0:00:00.001216 GET [200] /echo/I_love_Dart
|
||||
```
|
||||
30
analysis_options.yaml
Normal file
30
analysis_options.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
385
bin/server.dart
Normal file
385
bin/server.dart
Normal file
@@ -0,0 +1,385 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf/shelf_io.dart' as io;
|
||||
import 'package:shelf_web_socket/shelf_web_socket.dart';
|
||||
import 'package:web_socket_channel/web_socket_channel.dart';
|
||||
|
||||
// 用户连接信息
|
||||
class UserConnection {
|
||||
final String uuid;
|
||||
final String deviceModel;
|
||||
final WebSocketChannel channel;
|
||||
final DateTime connectedAt;
|
||||
String? currentDeviceId;
|
||||
|
||||
UserConnection({
|
||||
required this.uuid,
|
||||
required this.deviceModel,
|
||||
required this.channel,
|
||||
required this.connectedAt,
|
||||
this.currentDeviceId,
|
||||
});
|
||||
}
|
||||
|
||||
// WebSocket 管理器
|
||||
class WebSocketManager {
|
||||
final Map<String, UserConnection> _devices = {};
|
||||
final Map<String, WebSocketChannel> _webClients = {};
|
||||
|
||||
// 存储每个连接的注册信息
|
||||
final Map<WebSocketChannel, String> _channelToClientId = {};
|
||||
final Map<WebSocketChannel, String> _channelToDeviceUuid = {};
|
||||
|
||||
// 获取设备连接
|
||||
UserConnection? getDevice(String uuid) {
|
||||
return _devices[uuid];
|
||||
}
|
||||
|
||||
// 获取所有设备列表
|
||||
List<Map<String, dynamic>> getDeviceList() {
|
||||
return _devices.entries.map((entry) {
|
||||
return {
|
||||
'uuid': entry.key,
|
||||
'deviceModel': entry.value.deviceModel,
|
||||
'connectedAt': entry.value.connectedAt.toIso8601String(),
|
||||
'isConnected': true,
|
||||
};
|
||||
}).toList();
|
||||
}
|
||||
|
||||
// 注册设备
|
||||
void registerDevice(String uuid, String deviceModel, WebSocketChannel channel) {
|
||||
_devices[uuid] = UserConnection(
|
||||
uuid: uuid,
|
||||
deviceModel: deviceModel,
|
||||
channel: channel,
|
||||
connectedAt: DateTime.now(),
|
||||
);
|
||||
_channelToDeviceUuid[channel] = uuid;
|
||||
|
||||
print('✅ 调试设备注册: $deviceModel ($uuid)');
|
||||
_broadcastUserList();
|
||||
}
|
||||
|
||||
// 注册网页客户端
|
||||
void registerWebClient(String clientId, WebSocketChannel channel) {
|
||||
_webClients[clientId] = channel;
|
||||
_channelToClientId[channel] = clientId;
|
||||
|
||||
print('🌐 网页客户端注册: $clientId');
|
||||
|
||||
// 发送设备列表
|
||||
_sendUserListToClient(channel);
|
||||
}
|
||||
|
||||
// 处理网页消息
|
||||
void handleWebMessage(WebSocketChannel channel, Map<String, dynamic> data) {
|
||||
final clientId = _channelToClientId[channel];
|
||||
if (clientId == null) {
|
||||
print('未找到对应的网页客户端');
|
||||
return;
|
||||
}
|
||||
|
||||
print('💻 收到网页指令: ${data['type']} from client: $clientId');
|
||||
|
||||
final commandType = data['type'] as String;
|
||||
final targetUuid = data['targetUuid'] as String?;
|
||||
|
||||
switch (commandType) {
|
||||
case 'select_device':
|
||||
if (targetUuid != null) {
|
||||
final device = _devices[targetUuid];
|
||||
if (device != null) {
|
||||
print(' -> 网页选择控制设备: $targetUuid (${device.deviceModel})');
|
||||
channel.sink.add(jsonEncode({
|
||||
'type': 'device_selected',
|
||||
'targetUuid': targetUuid,
|
||||
'deviceModel': device.deviceModel,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
} else {
|
||||
print(' -> 设备不存在: $targetUuid');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'scan':
|
||||
if (targetUuid != null) {
|
||||
final device = _devices[targetUuid];
|
||||
if (device != null) {
|
||||
print(' -> 发送扫描命令到设备: $targetUuid');
|
||||
device.channel.sink.add(jsonEncode({
|
||||
'type': 'scan',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
} else {
|
||||
print(' -> 设备不存在: $targetUuid');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'stop_scan':
|
||||
if (targetUuid != null) {
|
||||
final device = _devices[targetUuid];
|
||||
if (device != null) {
|
||||
device.channel.sink.add(jsonEncode({
|
||||
'type': 'stop_scan',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
print(' -> 发送停止扫描命令到设备: $targetUuid');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'connect':
|
||||
if (targetUuid != null) {
|
||||
final device = _devices[targetUuid];
|
||||
final deviceId = data['deviceId'] as String?;
|
||||
if (device != null && deviceId != null) {
|
||||
device.currentDeviceId = deviceId;
|
||||
device.channel.sink.add(jsonEncode({
|
||||
'type': 'connect',
|
||||
'deviceId': deviceId,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
print(' -> 发送连接设备命令到: $targetUuid, 设备ID: $deviceId');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'disconnect':
|
||||
if (targetUuid != null) {
|
||||
final device = _devices[targetUuid];
|
||||
if (device != null) {
|
||||
device.currentDeviceId = null;
|
||||
device.channel.sink.add(jsonEncode({
|
||||
'type': 'disconnect',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
print(' -> 发送断开连接命令到设备: $targetUuid');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'send_data':
|
||||
if (targetUuid != null) {
|
||||
final device = _devices[targetUuid];
|
||||
final sendData = data['data'];
|
||||
if (device != null && sendData != null) {
|
||||
device.channel.sink.add(jsonEncode({
|
||||
'type': 'send_data',
|
||||
'data': sendData,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
print(' -> 发送数据到设备: $targetUuid, 数据: $sendData');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
print('未知命令: $commandType');
|
||||
}
|
||||
}
|
||||
|
||||
// 处理设备消息
|
||||
void handleDeviceMessage(WebSocketChannel channel, Map<String, dynamic> data) {
|
||||
final uuid = _channelToDeviceUuid[channel];
|
||||
if (uuid == null) {
|
||||
print('未找到对应的设备');
|
||||
return;
|
||||
}
|
||||
|
||||
print('📱 收到设备 $uuid 消息: ${data['type']}');
|
||||
|
||||
// 转发给所有网页客户端
|
||||
for (var entry in _webClients.entries) {
|
||||
entry.value.sink.add(jsonEncode({
|
||||
'type': 'device_message',
|
||||
'fromUser': uuid,
|
||||
'data': data,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// 移除设备
|
||||
void removeDevice(WebSocketChannel channel) {
|
||||
final uuid = _channelToDeviceUuid[channel];
|
||||
if (uuid != null) {
|
||||
_devices.remove(uuid);
|
||||
_channelToDeviceUuid.remove(channel);
|
||||
print('🗑️ 移除调试设备: $uuid');
|
||||
_broadcastUserList();
|
||||
}
|
||||
}
|
||||
|
||||
// 移除网页客户端
|
||||
void removeWebClient(WebSocketChannel channel) {
|
||||
final clientId = _channelToClientId[channel];
|
||||
if (clientId != null) {
|
||||
_webClients.remove(clientId);
|
||||
_channelToClientId.remove(channel);
|
||||
print('网页客户端断开: $clientId');
|
||||
}
|
||||
}
|
||||
|
||||
void _sendUserListToClient(WebSocketChannel client) {
|
||||
final deviceList = getDeviceList();
|
||||
client.sink.add(jsonEncode({
|
||||
'type': 'user_list',
|
||||
'users': deviceList,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
}));
|
||||
print('📡 发送设备列表到网页客户端: ${deviceList.length} 个设备');
|
||||
}
|
||||
|
||||
void _broadcastUserList() {
|
||||
final deviceList = getDeviceList();
|
||||
final message = jsonEncode({
|
||||
'type': 'user_list',
|
||||
'users': deviceList,
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
});
|
||||
|
||||
for (var client in _webClients.values) {
|
||||
client.sink.add(message);
|
||||
}
|
||||
print('📡 广播设备列表: ${deviceList.length} 个设备');
|
||||
}
|
||||
}
|
||||
|
||||
// WebSocket 处理函数 - 所有消息都在这里统一处理
|
||||
Future<Response> handleWebSocket(Request request, WebSocketManager manager) async {
|
||||
final handler = webSocketHandler((WebSocketChannel channel, String? protocol) {
|
||||
print('🔌 WebSocket 连接建立');
|
||||
|
||||
// 标识是否已经注册
|
||||
bool isRegistered = false;
|
||||
String? connectionType;
|
||||
|
||||
channel.stream.listen((message) {
|
||||
try {
|
||||
final data = jsonDecode(message);
|
||||
final type = data['type'] as String?;
|
||||
|
||||
print('收到消息,类型: $type, 已注册: $isRegistered');
|
||||
|
||||
if (!isRegistered) {
|
||||
// 处理注册消息
|
||||
if (type == 'device_register') {
|
||||
final uuid = data['uuid'] as String?;
|
||||
final deviceModel = data['deviceModel'] as String?;
|
||||
|
||||
if (uuid != null && deviceModel != null) {
|
||||
isRegistered = true;
|
||||
connectionType = 'device';
|
||||
manager.registerDevice(uuid, deviceModel, channel);
|
||||
} else {
|
||||
print('设备注册信息不完整');
|
||||
channel.sink.close();
|
||||
}
|
||||
} else if (type == 'web_register') {
|
||||
final clientId = data['clientId'] as String? ??
|
||||
'web_${DateTime.now().millisecondsSinceEpoch}';
|
||||
isRegistered = true;
|
||||
connectionType = 'web';
|
||||
manager.registerWebClient(clientId, channel);
|
||||
} else {
|
||||
print('等待注册消息,收到: $type');
|
||||
channel.sink.close();
|
||||
}
|
||||
} else {
|
||||
// 已注册,根据连接类型处理后续消息
|
||||
if (connectionType == 'web') {
|
||||
manager.handleWebMessage(channel, data);
|
||||
} else if (connectionType == 'device') {
|
||||
manager.handleDeviceMessage(channel, data);
|
||||
} else {
|
||||
print('未知连接类型: $connectionType');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print('解析消息失败: $e');
|
||||
}
|
||||
}, onDone: () {
|
||||
// 连接关闭时的清理
|
||||
if (connectionType == 'device') {
|
||||
manager.removeDevice(channel);
|
||||
} else if (connectionType == 'web') {
|
||||
manager.removeWebClient(channel);
|
||||
}
|
||||
print('🔌 连接关闭');
|
||||
}, onError: (error) {
|
||||
print('连接错误: $error');
|
||||
if (connectionType == 'device') {
|
||||
manager.removeDevice(channel);
|
||||
} else if (connectionType == 'web') {
|
||||
manager.removeWebClient(channel);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return handler(request);
|
||||
}
|
||||
|
||||
// CORS 中间件
|
||||
Middleware createCorsMiddleware() {
|
||||
return (Handler innerHandler) {
|
||||
return (Request request) async {
|
||||
if (request.method == 'OPTIONS') {
|
||||
return Response.ok('', headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Requested-With',
|
||||
'Access-Control-Allow-Credentials': 'true',
|
||||
});
|
||||
}
|
||||
|
||||
final response = await innerHandler(request);
|
||||
|
||||
return response.change(headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
|
||||
'Access-Control-Allow-Headers': 'Origin, Content-Type, X-Requested-With',
|
||||
'Access-Control-Allow-Credentials': 'true',
|
||||
});
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final manager = WebSocketManager();
|
||||
|
||||
Future<Response> router(Request request) async {
|
||||
final path = request.url.path;
|
||||
print('📨 请求: ${request.method} $path');
|
||||
|
||||
if (path == 'ws') {
|
||||
return await handleWebSocket(request, manager);
|
||||
}
|
||||
if (path == 'users') {
|
||||
return Response.ok(
|
||||
jsonEncode(manager.getDeviceList()),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
);
|
||||
}
|
||||
return Response.notFound('Not Found');
|
||||
}
|
||||
|
||||
final handler = const Pipeline()
|
||||
.addMiddleware(logRequests())
|
||||
.addMiddleware(createCorsMiddleware())
|
||||
.addHandler(router);
|
||||
|
||||
//端口
|
||||
final server = await io.serve(handler, '0.0.0.0', 8089);
|
||||
print('\n🚀 ========================================');
|
||||
print('✅ 服务器启动成功!');
|
||||
print('🌐 HTTP 地址: http://${server.address.host}:${server.port}');
|
||||
print('🔌 WebSocket 地址: ws://${server.address.host}:${server.port}/ws');
|
||||
print('📋 用户列表 API: http://${server.address.host}:${server.port}/users');
|
||||
print('========================================\n');
|
||||
}
|
||||
173
pubspec.lock
Normal file
173
pubspec.lock
Normal file
@@ -0,0 +1,173 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.13.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: df0c643f44ad098eb37988027a8e2b2b5a031fd3977f06bbfd3a76637e8df739
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.18.2"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
shelf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf
|
||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
shelf_cors_headers:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf_cors_headers
|
||||
sha256: a127c80f99bbef3474293db67a7608e3a0f1f0fcdb171dad77fa9bd2cd123ae4
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.1.5"
|
||||
shelf_static:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.0.4"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.10.2"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
web_socket_channel:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42"
|
||||
url: "https://pub.flutter-io.cn"
|
||||
source: hosted
|
||||
version: "2.4.5"
|
||||
sdks:
|
||||
dart: ">=3.5.4 <4.0.0"
|
||||
17
pubspec.yaml
Normal file
17
pubspec.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: ble_debug_system
|
||||
description: A server app using the shelf package and Docker.
|
||||
version: 1.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.4
|
||||
|
||||
dependencies:
|
||||
shelf: ^1.4.0
|
||||
shelf_web_socket: ^1.0.4
|
||||
web_socket_channel: ^2.4.0
|
||||
shelf_static: ^1.1.0
|
||||
shelf_cors_headers: ^0.1.5
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^2.0.0
|
||||
Reference in New Issue
Block a user