Unify bridge sync field names

This commit is contained in:
Haitao Pan 2026-04-11 20:25:40 +08:00
parent 4476741750
commit e7eeef193c
10 changed files with 94 additions and 59 deletions

View File

@ -801,7 +801,7 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
if (bridgeHost.isNotEmpty && normalizedHost == bridgeHost) {
final bridgeToken =
(await storeInternal.loadAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
))?.trim() ??
'';
if (bridgeToken.isNotEmpty) {

View File

@ -171,8 +171,8 @@ class _SettingsPageState extends State<SettingsPage> {
Widget _buildTokenConfiguredSummary(AccountSyncState? accountState) {
final configured = <String>[
if (accountState?.tokenConfigured.openclaw == true)
appText('Gateway Token', 'Gateway Token'),
if (accountState?.tokenConfigured.bridge == true)
appText('Bridge Token', 'Bridge Token'),
if (accountState?.tokenConfigured.apisix == true)
appText('AI Gateway Token', 'AI Gateway Token'),
if (accountState?.tokenConfigured.vault == true) 'Vault Token',

View File

@ -55,7 +55,7 @@ extension SettingsControllerAccountExtension on SettingsController {
refName: gatewayTokenRefForProfileInternal(resolvedProfileIndex),
fallbackRefName: SecretStore.gatewayTokenRefKey(resolvedProfileIndex),
accountTarget: resolvedProfileIndex == kGatewayRemoteProfileIndex
? kAccountManagedSecretTargetOpenclawGatewayToken
? kAccountManagedSecretTargetBridgeAuthToken
: '',
);
}

View File

@ -148,6 +148,7 @@ Future<void> completeAccountSignInSettingsInternal(
controller,
baseUrl: baseUrl,
bridgeTokenOverride: _resolveBridgeAuthorizationToken(payload),
bridgeServerUrlOverride: _resolveBridgeServerUrl(payload),
quiet: true,
);
await controller.reloadDerivedStateInternal();
@ -225,6 +226,7 @@ Future<AccountSyncResult> syncAccountSettingsInternal(
String baseUrl = '',
bool quiet = false,
String bridgeTokenOverride = '',
String bridgeServerUrlOverride = '',
}) async {
final sessionToken =
(await controller.storeInternal.loadAccountSessionToken())?.trim() ?? '';
@ -250,7 +252,7 @@ Future<AccountSyncResult> syncAccountSettingsInternal(
final bridgeToken = bridgeTokenOverride.trim().isNotEmpty
? bridgeTokenOverride.trim()
: ((await controller.storeInternal.loadAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
))?.trim() ??
'');
if (bridgeToken.isEmpty) {
@ -266,8 +268,33 @@ Future<AccountSyncResult> syncAccountSettingsInternal(
return result;
}
final bridgeServerUrl = bridgeServerUrlOverride.trim().isNotEmpty
? bridgeServerUrlOverride.trim()
: controller.accountSyncStateInternal?.syncedDefaults.bridgeServerUrl
.trim()
.isNotEmpty ==
true
? controller.accountSyncStateInternal!.syncedDefaults.bridgeServerUrl
.trim()
: controller
.snapshotInternal
.acpBridgeServerModeConfig
.cloudSynced
.remoteServerSummary
.endpoint
.trim()
.isNotEmpty
? controller
.snapshotInternal
.acpBridgeServerModeConfig
.cloudSynced
.remoteServerSummary
.endpoint
.trim()
: _kProductionBridgeEndpoint;
await controller.storeInternal.saveAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
value: bridgeToken,
);
await controller.storeInternal.clearAccountManagedSecret(
@ -278,14 +305,17 @@ Future<AccountSyncResult> syncAccountSettingsInternal(
);
final nextState = AccountSyncState.defaults().copyWith(
syncedDefaults: AccountRemoteProfile.defaults().copyWith(
bridgeServerUrl: bridgeServerUrl,
),
syncState: 'ready',
syncMessage: 'Bridge access synced',
lastSyncAtMs: DateTime.now().millisecondsSinceEpoch,
lastSyncSource: _kProductionBridgeEndpoint,
lastSyncSource: bridgeServerUrl,
lastSyncError: '',
profileScope: 'bridge',
tokenConfigured: const AccountTokenConfigured(
openclaw: true,
bridge: true,
vault: false,
apisix: false,
),
@ -299,10 +329,7 @@ Future<AccountSyncResult> syncAccountSettingsInternal(
accountIdentifier: '',
lastSyncAt: nextState.lastSyncAtMs,
remoteServerSummary: currentModeConfig.cloudSynced.remoteServerSummary
.copyWith(
endpoint: _kProductionBridgeEndpoint,
hasAdvancedOverrides: false,
),
.copyWith(endpoint: bridgeServerUrl, hasAdvancedOverrides: false),
),
);
final sanitizedSettings = _sanitizeBridgeOnlyAccountSyncSettings(
@ -444,13 +471,15 @@ SettingsSnapshot _sanitizeBridgeOnlyAccountSyncSettings(
}
String _resolveBridgeAuthorizationToken(Map<String, dynamic> payload) {
final explicit = _stringValue(payload['internalServiceToken']).isNotEmpty
? _stringValue(payload['internalServiceToken'])
: _stringValue(payload['internal_service_token']).isNotEmpty
? _stringValue(payload['internal_service_token'])
: _stringValue(payload['bridgeAuthToken']).isNotEmpty
? _stringValue(payload['bridgeAuthToken'])
: _stringValue(payload['bridge_auth_token']);
final explicit = _stringValue(payload['BRIDGE_AUTH_TOKEN']);
if (explicit.isNotEmpty) {
return explicit;
}
return '';
}
String _resolveBridgeServerUrl(Map<String, dynamic> payload) {
final explicit = _stringValue(payload['BRIDGE_SERVER_URL']);
if (explicit.isNotEmpty) {
return explicit;
}

View File

@ -174,7 +174,7 @@ bool hasStoredGatewayTokenForProfileSettingsInternal(
(!controller.snapshotInternal.accountLocalMode &&
profileIndex == kGatewayRemoteProfileIndex &&
controller.secureRefsInternal.containsKey(
kAccountManagedSecretTargetOpenclawGatewayToken,
kAccountManagedSecretTargetBridgeAuthToken,
));
bool hasStoredGatewayPasswordForProfileSettingsInternal(
@ -195,7 +195,7 @@ String? storedGatewayTokenMaskForProfileSettingsInternal(
(!controller.snapshotInternal.accountLocalMode &&
profileIndex == kGatewayRemoteProfileIndex
? controller
.secureRefsInternal[kAccountManagedSecretTargetOpenclawGatewayToken]
.secureRefsInternal[kAccountManagedSecretTargetBridgeAuthToken]
: null);
String? storedGatewayPasswordMaskForProfileSettingsInternal(

View File

@ -67,38 +67,38 @@ class AccountSessionSummary {
class AccountTokenConfigured {
const AccountTokenConfigured({
required this.openclaw,
required this.bridge,
required this.vault,
required this.apisix,
});
final bool openclaw;
final bool bridge;
final bool vault;
final bool apisix;
factory AccountTokenConfigured.defaults() {
return const AccountTokenConfigured(
openclaw: false,
bridge: false,
vault: false,
apisix: false,
);
}
AccountTokenConfigured copyWith({bool? openclaw, bool? vault, bool? apisix}) {
AccountTokenConfigured copyWith({bool? bridge, bool? vault, bool? apisix}) {
return AccountTokenConfigured(
openclaw: openclaw ?? this.openclaw,
bridge: bridge ?? this.bridge,
vault: vault ?? this.vault,
apisix: apisix ?? this.apisix,
);
}
Map<String, dynamic> toJson() {
return {'openclaw': openclaw, 'vault': vault, 'apisix': apisix};
return {'bridge': bridge, 'vault': vault, 'apisix': apisix};
}
factory AccountTokenConfigured.fromJson(Map<String, dynamic> json) {
return AccountTokenConfigured(
openclaw: json['openclaw'] as bool? ?? false,
bridge: json['bridge'] as bool? ?? false,
vault: json['vault'] as bool? ?? false,
apisix: json['apisix'] as bool? ?? false,
);
@ -165,16 +165,16 @@ class AccountSecretLocator {
class AccountRemoteProfile {
const AccountRemoteProfile({
required this.openclawUrl,
required this.openclawOrigin,
required this.bridgeServerUrl,
required this.bridgeServerOrigin,
required this.vaultUrl,
required this.vaultNamespace,
required this.apisixUrl,
required this.secretLocators,
});
final String openclawUrl;
final String openclawOrigin;
final String bridgeServerUrl;
final String bridgeServerOrigin;
final String vaultUrl;
final String vaultNamespace;
final String apisixUrl;
@ -182,8 +182,8 @@ class AccountRemoteProfile {
factory AccountRemoteProfile.defaults() {
return const AccountRemoteProfile(
openclawUrl: '',
openclawOrigin: '',
bridgeServerUrl: '',
bridgeServerOrigin: '',
vaultUrl: '',
vaultNamespace: '',
apisixUrl: '',
@ -192,16 +192,16 @@ class AccountRemoteProfile {
}
AccountRemoteProfile copyWith({
String? openclawUrl,
String? openclawOrigin,
String? bridgeServerUrl,
String? bridgeServerOrigin,
String? vaultUrl,
String? vaultNamespace,
String? apisixUrl,
List<AccountSecretLocator>? secretLocators,
}) {
return AccountRemoteProfile(
openclawUrl: openclawUrl ?? this.openclawUrl,
openclawOrigin: openclawOrigin ?? this.openclawOrigin,
bridgeServerUrl: bridgeServerUrl ?? this.bridgeServerUrl,
bridgeServerOrigin: bridgeServerOrigin ?? this.bridgeServerOrigin,
vaultUrl: vaultUrl ?? this.vaultUrl,
vaultNamespace: vaultNamespace ?? this.vaultNamespace,
apisixUrl: apisixUrl ?? this.apisixUrl,
@ -211,8 +211,8 @@ class AccountRemoteProfile {
Map<String, dynamic> toJson() {
return {
'openclawUrl': openclawUrl,
'openclawOrigin': openclawOrigin,
'BRIDGE_SERVER_URL': bridgeServerUrl,
'bridgeServerOrigin': bridgeServerOrigin,
'vaultUrl': vaultUrl,
'vaultNamespace': vaultNamespace,
'apisixUrl': apisixUrl,
@ -238,9 +238,10 @@ class AccountRemoteProfile {
final defaults = AccountRemoteProfile.defaults();
return AccountRemoteProfile(
openclawUrl: json['openclawUrl'] as String? ?? defaults.openclawUrl,
openclawOrigin:
json['openclawOrigin'] as String? ?? defaults.openclawOrigin,
bridgeServerUrl:
json['BRIDGE_SERVER_URL'] as String? ?? defaults.bridgeServerUrl,
bridgeServerOrigin:
json['bridgeServerOrigin'] as String? ?? defaults.bridgeServerOrigin,
vaultUrl: json['vaultUrl'] as String? ?? defaults.vaultUrl,
vaultNamespace:
json['vaultNamespace'] as String? ?? defaults.vaultNamespace,
@ -690,14 +691,13 @@ class AccountSyncResult {
final String message;
}
const String kAccountManagedSecretTargetOpenclawGatewayToken =
'openclaw.gateway_token';
const String kAccountManagedSecretTargetBridgeAuthToken = 'bridge.auth_token';
const String kAccountManagedSecretTargetAIGatewayAccessToken =
'ai_gateway.access_token';
const String kAccountManagedSecretTargetOllamaCloudApiKey =
'ollama_cloud.api_key';
const List<String> kAccountManagedSecretTargets = <String>[
kAccountManagedSecretTargetOpenclawGatewayToken,
kAccountManagedSecretTargetBridgeAuthToken,
kAccountManagedSecretTargetAIGatewayAccessToken,
kAccountManagedSecretTargetOllamaCloudApiKey,
];

View File

@ -359,12 +359,12 @@ class _FakeSettingsController extends SettingsController {
lastSyncSource: 'https://accounts.svc.plus',
profileScope: 'tenant-shared',
tokenConfigured: const AccountTokenConfigured(
openclaw: true,
bridge: true,
vault: false,
apisix: true,
),
syncedDefaults: AccountRemoteProfile.defaults().copyWith(
openclawUrl: 'wss://gateway.svc.plus',
bridgeServerUrl: 'https://xworkmate-bridge.svc.plus',
apisixUrl: 'https://apisix.svc.plus',
),
);
@ -385,12 +385,12 @@ class _FakeSettingsController extends SettingsController {
lastSyncSource: baseUrl,
profileScope: 'tenant-shared',
tokenConfigured: const AccountTokenConfigured(
openclaw: true,
bridge: true,
vault: false,
apisix: true,
),
syncedDefaults: AccountRemoteProfile.defaults().copyWith(
openclawUrl: 'wss://gateway.svc.plus',
bridgeServerUrl: 'https://xworkmate-bridge.svc.plus',
apisixUrl: 'https://apisix.svc.plus',
),
);

View File

@ -75,7 +75,7 @@ void main() {
value: 'stale-ollama-token',
);
await store.saveAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
value: 'bridge-token',
);
@ -113,11 +113,11 @@ void main() {
);
expect(controller.snapshot.accountLocalMode, isFalse);
expect(controller.accountSyncState?.profileScope, 'bridge');
expect(controller.accountSyncState?.tokenConfigured.openclaw, isTrue);
expect(controller.accountSyncState?.tokenConfigured.bridge, isTrue);
expect(controller.accountSyncState?.tokenConfigured.apisix, isFalse);
expect(
await store.loadAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
),
'bridge-token',
);

View File

@ -49,7 +49,7 @@ void main() {
value: 'managed-secret',
);
await store.saveAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
value: 'bridge-token',
);
await store.saveAccountSyncState(
@ -106,7 +106,7 @@ void main() {
);
expect(
await store.loadAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
),
isNull,
);

View File

@ -55,16 +55,20 @@ void main() {
expect(controller.accountSession?.totpPending, isFalse);
expect(controller.accountSyncState?.syncState, 'ready');
expect(controller.accountSyncState?.profileScope, 'bridge');
expect(controller.accountSyncState?.tokenConfigured.openclaw, isTrue);
expect(controller.accountSyncState?.tokenConfigured.bridge, isTrue);
expect(controller.accountSyncState?.tokenConfigured.apisix, isFalse);
expect(await store.loadAccountSessionToken(), 'session-token');
expect(
await store.loadAccountManagedSecret(
target: kAccountManagedSecretTargetOpenclawGatewayToken,
target: kAccountManagedSecretTargetBridgeAuthToken,
),
'bridge-token',
);
expect(client.loadSessionCalls, 0);
expect(
controller.accountSyncState?.syncedDefaults.bridgeServerUrl,
'https://xworkmate-bridge.svc.plus',
);
expect(
controller
.snapshot
@ -147,7 +151,8 @@ class _SuccessfulAccountRuntimeClient extends AccountRuntimeClient {
expect(password, '***REMOVED-CREDENTIAL***');
return <String, dynamic>{
'token': 'session-token',
'internalServiceToken': 'bridge-token',
'BRIDGE_AUTH_TOKEN': 'bridge-token',
'BRIDGE_SERVER_URL': 'https://xworkmate-bridge.svc.plus',
'expiresAt': '2026-04-12T00:00:00Z',
'user': <String, dynamic>{
'id': 'u-1',
@ -198,7 +203,8 @@ class _MfaAccountRuntimeClient extends AccountRuntimeClient {
lastVerifiedCode = code;
return <String, dynamic>{
'token': 'session-token',
'internalServiceToken': 'bridge-token',
'BRIDGE_AUTH_TOKEN': 'bridge-token',
'BRIDGE_SERVER_URL': 'https://xworkmate-bridge.svc.plus',
'expiresAt': '2026-04-12T00:00:00Z',
'user': <String, dynamic>{
'id': 'u-1',