feat: 调整登录有效期选择

This commit is contained in:
chaos-zhu
2026-06-27 12:52:32 +08:00
parent 44703b4a8e
commit b7a64fd5c0
10 changed files with 44 additions and 99 deletions
+4 -28
View File
@@ -1,36 +1,12 @@
enum LoginExpiry { temporary, currentDay, threeDays, sevenDays }
enum LoginExpiry { threeDays, sevenDays, thirtyDays }
String jwtExpiresFor(LoginExpiry expiry, {DateTime? now}) {
String jwtExpiresFor(LoginExpiry expiry) {
switch (expiry) {
case LoginExpiry.temporary:
return '1h';
case LoginExpiry.currentDay:
final current = now ?? DateTime.now();
final tomorrow = DateTime(current.year, current.month, current.day + 1);
final seconds = tomorrow.difference(current).inSeconds;
return '${seconds}s';
case LoginExpiry.threeDays:
return '3d';
case LoginExpiry.sevenDays:
return '7d';
case LoginExpiry.thirtyDays:
return '30d';
}
}
int jwtExpireAtFor(LoginExpiry expiry, {DateTime? now}) {
final current = now ?? DateTime.now();
final expires = jwtExpiresFor(expiry, now: current);
final match = RegExp(r'^(\d+)([smhd])$').firstMatch(expires);
if (match == null) {
throw const FormatException('invalid jwt expiry');
}
final count = int.parse(match.group(1)!);
final unit = match.group(2)!;
final multiplier = switch (unit) {
's' => 1000,
'm' => 60 * 1000,
'h' => 60 * 60 * 1000,
'd' => 24 * 60 * 60 * 1000,
_ => 1000,
};
return current.millisecondsSinceEpoch + count * multiplier;
}
@@ -63,8 +63,7 @@ class LoginController {
required String mfa2Token,
required bool httpRiskAccepted,
required bool savePassword,
LoginExpiry expiry = LoginExpiry.temporary,
DateTime? now,
LoginExpiry expiry = LoginExpiry.threeDays,
}) async {
final String normalized;
try {
@@ -99,8 +98,7 @@ class LoginController {
final response = await api.postJson('/login', {
'loginName': username,
'ciphertext': ciphertext,
'jwtExpires': jwtExpiresFor(expiry, now: now),
'jwtExpireAt': jwtExpireAtFor(expiry, now: now),
'jwtExpires': jwtExpiresFor(expiry),
if (mfa2Token.isNotEmpty) 'mfa2Token': mfa2Token,
});
+2 -2
View File
@@ -41,7 +41,7 @@ class _LoginPageState extends State<LoginPage> {
final FocusNode _userFocus = FocusNode();
final FocusNode _passwordFocus = FocusNode();
LoginExpiry _expiry = LoginExpiry.currentDay;
LoginExpiry _expiry = LoginExpiry.threeDays;
bool _savePassword = false;
bool _httpRiskAccepted = false;
bool _submitting = false;
@@ -564,9 +564,9 @@ class _ExpiryPicker extends StatelessWidget {
final ValueChanged<LoginExpiry> onChanged;
static const _optionKeys = <LoginExpiry, String>{
LoginExpiry.currentDay: 'login.expiry.currentDay',
LoginExpiry.threeDays: 'login.expiry.threeDays',
LoginExpiry.sevenDays: 'login.expiry.sevenDays',
LoginExpiry.thirtyDays: 'login.expiry.thirtyDays',
};
@override
+1 -2
View File
@@ -49,10 +49,9 @@ const Map<String, String> stringsEn = {
'login.errMissingFields': 'Server login response is missing required fields',
// Login expiry options
'login.expiry.temporary': 'Temporary, 1 hour',
'login.expiry.currentDay': 'Today',
'login.expiry.threeDays': '3 days',
'login.expiry.sevenDays': '7 days',
'login.expiry.thirtyDays': '30 days',
// Main shell tabs
'tabs.servers': 'Servers',
+3 -4
View File
@@ -46,10 +46,9 @@ const Map<String, String> stringsZh = {
'login.errSchemeUnsupported': '服务端地址仅支持 http 或 https',
'login.errLoginGeneric': '登录失败',
'login.errMissingFields': '服务端登录响应缺少必要字段',
'login.expiry.temporary': '临时会话,1 小时',
'login.expiry.currentDay': '今天',
'login.expiry.threeDays': '3 天',
'login.expiry.sevenDays': '7 天',
'login.expiry.threeDays': '3天',
'login.expiry.sevenDays': '7天',
'login.expiry.thirtyDays': '30天',
// Tabs
'tabs.servers': '服务器',
+2 -13
View File
@@ -2,23 +2,12 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:easynode_native/core/utils/jwt_expiry.dart';
void main() {
test('maps temporary expiry to one hour', () {
expect(jwtExpiresFor(LoginExpiry.temporary), '1h');
});
test('maps three days and seven days', () {
expect(jwtExpiresFor(LoginExpiry.threeDays), '3d');
expect(jwtExpiresFor(LoginExpiry.sevenDays), '7d');
});
test('maps current day to remaining seconds until midnight', () {
final fixed = DateTime(2026, 5, 16, 23, 59, 30);
expect(jwtExpiresFor(LoginExpiry.currentDay, now: fixed), '30s');
});
test('jwtExpireAtFor returns milliseconds advanced by the duration', () {
final fixed = DateTime.utc(2026, 5, 16, 0, 0, 0);
final expectedMs = fixed.millisecondsSinceEpoch + 60 * 60 * 1000;
expect(jwtExpireAtFor(LoginExpiry.temporary, now: fixed), expectedMs);
test('maps thirty days', () {
expect(jwtExpiresFor(LoginExpiry.thirtyDays), '30d');
});
}