You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

86 lines
2.5 KiB

import 'dart:convert';
import 'dart:math';
import 'package:cryptography/cryptography.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../../data/models/appconfig.dart';
/// 生成加密评论页 URL(与 App Comment 服务端 SIGN_SECRET 一致)
///
/// [userId]、[packageName]、[appName]、[appVersion] 必填;[userName]、[lang] 可选。
Future<String> buildCommentUrl({
required String userId,
String? userName,
required String packageName,
required String appName,
required String appVersion,
String lang = 'en',
}) async {
final signSecret = _commentSignSecret();
var commentServer = _commentServerUrl();
if (signSecret.isEmpty || commentServer.isEmpty) {
throw Exception(
'COMMENT_SIGN_SECRET / COMMENT_SERVER_URL 未配置:'
'请在 getAppConfig 的 env 中下发,或在 .env 中填写(本地调试)',
);
}
commentServer = commentServer.trim().replaceAll(RegExp(r'/$'), '');
final payload = {
'userId': userId,
'userName': userName ?? userId,
'packageName': packageName,
'appName': appName,
'appVersion': appVersion,
'lang': lang,
'ts': DateTime.now().millisecondsSinceEpoch ~/ 1000,
'nonce': _randomHex(8),
};
final algo = AesGcm.with256bits();
final key = await algo.newSecretKeyFromBytes(_hexDecode(signSecret));
final nonce = algo.newNonce();
final box = await algo.encrypt(
utf8.encode(jsonEncode(payload)),
secretKey: key,
nonce: nonce,
);
// 格式: iv(12 bytes) + tag(16 bytes) + ciphertext
final blob = <int>[...nonce, ...box.mac.bytes, ...box.cipherText];
final token = base64Url.encode(blob).replaceAll('=', '');
return '$commentServer/?d=$token';
}
/// 优先使用服务端 [getAppConfig] 下发的 `env`,否则回退 `.env`(本地调试)。
String _commentSignSecret() {
if (AppConfig.isInitialized()) {
final v = AppConfig.env('COMMENT_SIGN_SECRET');
if (v != null && v.isNotEmpty) return v;
}
return dotenv.env['COMMENT_SIGN_SECRET'] ?? '';
}
String _commentServerUrl() {
if (AppConfig.isInitialized()) {
final v = AppConfig.env('COMMENT_SERVER_URL');
if (v != null && v.isNotEmpty) return v;
}
return dotenv.env['COMMENT_SERVER_URL'] ?? '';
}
List<int> _hexDecode(String hex) => List.generate(
hex.length ~/ 2,
(i) => int.parse(hex.substring(i * 2, i * 2 + 2), radix: 16),
);
String _randomHex(int byteLen) {
final r = Random.secure();
return List.generate(
byteLen,
(_) => r.nextInt(256).toRadixString(16).padLeft(2, '0'),
).join();
}