Rotate bot key

This commit is contained in:
2025-08-24 01:49:56 +08:00
parent 3959f2260b
commit 5060bd30c9
2 changed files with 116 additions and 72 deletions

View File

@@ -904,5 +904,8 @@
"revoke": "Revoke", "revoke": "Revoke",
"keyName": "Key Name", "keyName": "Key Name",
"newKeyGenerated": "New Key Generated", "newKeyGenerated": "New Key Generated",
"copyKeyHint": "Please copy this key and store it somewhere safe. You will not be able to see it again." "copyKeyHint": "Please copy this key and store it somewhere safe. You will not be able to see it again.",
"rotateKey": "Rotate Key",
"rotateBotKey": "Rotate Bot Key",
"rotateBotKeyHint": "Are you sure you want to rotate this key? The old key will become invalid immediately. This action cannot be undone."
} }

View File

@@ -6,6 +6,7 @@ import 'package:gap/gap.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:island/models/bot_key.dart'; import 'package:island/models/bot_key.dart';
import 'package:island/pods/network.dart'; import 'package:island/pods/network.dart';
import 'package:island/services/time.dart';
import 'package:island/widgets/alert.dart'; import 'package:island/widgets/alert.dart';
import 'package:island/widgets/content/sheet.dart'; import 'package:island/widgets/content/sheet.dart';
import 'package:island/widgets/response.dart'; import 'package:island/widgets/response.dart';
@@ -95,7 +96,6 @@ class BotKeysScreen extends HookConsumerWidget {
isScrollControlled: true, isScrollControlled: true,
builder: builder:
(context) => SheetScaffold( (context) => SheetScaffold(
heightFactor: 0.65,
titleText: 'newBotKey'.tr(), titleText: 'newBotKey'.tr(),
child: Padding( child: Padding(
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
@@ -136,6 +136,28 @@ class BotKeysScreen extends HookConsumerWidget {
); );
} }
void rotateKey(String keyId) {
showConfirmAlert('rotateBotKeyHint'.tr(), 'rotateBotKey'.tr()).then((
confirm,
) async {
if (confirm) {
try {
if (context.mounted) showLoadingModal(context);
final client = ref.read(apiClientProvider);
final resp = await client.post(
'/develop/developers/$publisherName/projects/$projectId/bots/$botId/keys/$keyId/rotate',
);
final rotatedApiKey = SnAccountApiKey.fromJson(resp.data);
showNewKeySheet(rotatedApiKey);
} catch (err) {
showErrorAlert(err.toString());
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
});
}
void revokeKey(String keyId) { void revokeKey(String keyId) {
showConfirmAlert('revokeBotKeyHint'.tr(), 'revokeBotKey'.tr()).then(( showConfirmAlert('revokeBotKeyHint'.tr(), 'revokeBotKey'.tr()).then((
confirm, confirm,
@@ -158,6 +180,8 @@ class BotKeysScreen extends HookConsumerWidget {
}); });
} }
return keys.when(
data: (data) {
return Column( return Column(
children: [ children: [
ListTile( ListTile(
@@ -168,15 +192,17 @@ class BotKeysScreen extends HookConsumerWidget {
), ),
const Divider(height: 1), const Divider(height: 1),
Expanded( Expanded(
child: keys.when( child:
data: (data) { data.isEmpty
if (data.isEmpty) { ? Center(child: Text('noBotKeys'.tr()))
return Center(child: Text('noBotKeys'.tr())); : RefreshIndicator(
}
return RefreshIndicator(
onRefresh: onRefresh:
() => ref.refresh( () => ref.refresh(
botKeysProvider(publisherName, projectId, botId).future, botKeysProvider(
publisherName,
projectId,
botId,
).future,
), ),
child: ListView.builder( child: ListView.builder(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
@@ -185,13 +211,24 @@ class BotKeysScreen extends HookConsumerWidget {
final apiKey = data[index]; final apiKey = data[index];
return ListTile( return ListTile(
title: Text(apiKey.label), title: Text(apiKey.label),
subtitle: Text( subtitle: Text(apiKey.createdAt.formatSystem()),
'Created: ${DateFormat.yMMMd().format(apiKey.createdAt)}', contentPadding: EdgeInsets.only(
left: 16,
right: 12,
), ),
contentPadding: EdgeInsets.only(left: 16, right: 12),
trailing: PopupMenuButton( trailing: PopupMenuButton(
itemBuilder: itemBuilder:
(context) => [ (context) => [
PopupMenuItem(
value: 'rotate',
child: Row(
children: [
const Icon(Symbols.refresh),
const Gap(12),
Text('rotateKey'.tr()),
],
),
),
PopupMenuItem( PopupMenuItem(
value: 'revoke', value: 'revoke',
child: Row( child: Row(
@@ -203,14 +240,18 @@ class BotKeysScreen extends HookConsumerWidget {
const Gap(12), const Gap(12),
Text( Text(
'revoke'.tr(), 'revoke'.tr(),
style: TextStyle(color: Colors.red), style: TextStyle(
color: Colors.red,
),
), ),
], ],
), ),
), ),
], ],
onSelected: (value) { onSelected: (value) {
if (value == 'revoke') { if (value == 'rotate') {
rotateKey(apiKey.id);
} else if (value == 'revoke') {
revokeKey(apiKey.id); revokeKey(apiKey.id);
} }
}, },
@@ -218,6 +259,9 @@ class BotKeysScreen extends HookConsumerWidget {
); );
}, },
), ),
),
),
],
); );
}, },
loading: () => const Center(child: CircularProgressIndicator()), loading: () => const Center(child: CircularProgressIndicator()),
@@ -229,9 +273,6 @@ class BotKeysScreen extends HookConsumerWidget {
botKeysProvider(publisherName, projectId, botId), botKeysProvider(publisherName, projectId, botId),
), ),
), ),
),
),
],
); );
} }
} }