mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: show server url (#3956)
* chore: data folder for cloud * chore: display server url * chore: fix test
This commit is contained in:
parent
4a1a143a66
commit
8179419f8b
@ -1,15 +1,18 @@
|
||||
# Initial Setup
|
||||
# Copy the 'dev.env' file to '.env':
|
||||
# Use the command 'cp dev.env .env' to create a copy of 'dev.env' named '.env'.
|
||||
# After copying, update the '.env' file with the necessary environment parameters.
|
||||
|
||||
# 1. Copy the dev.env file to .env:
|
||||
# cp dev.env .env
|
||||
# Update the environment parameters as needed.
|
||||
|
||||
# 2. Generate the env.dart from this .env file:
|
||||
# You can use the "Generate Env File" task in VSCode.
|
||||
# Alternatively, execute the following commands:
|
||||
# Generate the 'env.dart' from this '.env' file:
|
||||
# Option 1: Use the "Generate Env File" task in VSCode.
|
||||
# Option 2: Execute the commands in the appflowy_flutter directory:
|
||||
# cd appflowy_flutter
|
||||
# dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs
|
||||
|
||||
# Note on Configuration Priority:
|
||||
# If both Supabase config and AppFlowy cloud config are provided in the '.env' file,
|
||||
# the AppFlowy cloud config will be prioritized and the Supabase config ignored.
|
||||
# Ensure only one of these configurations is active at any given time.
|
||||
|
||||
|
||||
# Cloud Type Configuration
|
||||
# Use this configuration file to specify the cloud type and its associated settings. The available cloud types are:
|
||||
@ -25,17 +28,9 @@ SUPABASE_URL=
|
||||
SUPABASE_ANON_KEY=
|
||||
|
||||
# AppFlowy Cloud Configuration
|
||||
# If using AppFlowy Cloud (CLOUD_TYPE=2), provide the following details:
|
||||
# For instance:
|
||||
# APPFLOWY_CLOUD_BASE_URL=https://xxxxxxxxx
|
||||
# APPFLOWY_CLOUD_WS_BASE_URL=wss://xxxxxxxxx
|
||||
# APPFLOWY_CLOUD_GOTRUE_URL=https://xxxxxxxxx
|
||||
# If using Supabase (CLOUD_TYPE=2), provide the following details:
|
||||
#
|
||||
# When using localhost for development, you must run AppFlowy Cloud locally
|
||||
# first. Plese Please follow the instructions below:
|
||||
# https://github.com/AppFlowy-IO/AppFlowy-Cloud#development
|
||||
#
|
||||
# After running AppFlowy Cloud locally, you can use the following settings:
|
||||
# When using localhost for development. you can use the following settings:
|
||||
# APPFLOWY_CLOUD_BASE_URL=http://localhost:8000
|
||||
# APPFLOWY_CLOUD_WS_BASE_URL=ws://localhost:8000/ws
|
||||
# APPFLOWY_CLOUD_GOTRUE_URL=http://localhost:9998
|
||||
|
@ -39,7 +39,7 @@ void main() {
|
||||
|
||||
// should not see the sync setting page when sign in as annoymous
|
||||
await tester.openSettings();
|
||||
await tester.expectNoSettingsPage(SettingsPage.syncSetting);
|
||||
await tester.expectNoSettingsPage(SettingsPage.cloud);
|
||||
});
|
||||
|
||||
testWidgets('enable encryption', (tester) async {
|
||||
@ -48,7 +48,7 @@ void main() {
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// the switch should be off by default
|
||||
tester.assertEnableEncryptSwitchValue(false);
|
||||
@ -68,7 +68,7 @@ void main() {
|
||||
|
||||
// Open the setting page and sign out
|
||||
await tester.openSettings();
|
||||
await tester.openSettingsPage(SettingsPage.syncSetting);
|
||||
await tester.openSettingsPage(SettingsPage.cloud);
|
||||
|
||||
// the switch should be on by default
|
||||
tester.assertEnableSyncSwitchValue(true);
|
||||
|
@ -1,5 +1,5 @@
|
||||
import 'package:appflowy/user/presentation/screens/sign_in_screen/widgets/widgets.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/sync_setting_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_cloud_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
|
@ -70,9 +70,8 @@ class DartKeyValue implements KeyValueStorage {
|
||||
|
||||
/// Key-value store
|
||||
/// The data is stored in the local storage of the device.
|
||||
class RustKeyValue implements KeyValueStorage {
|
||||
@override
|
||||
Future<void> set(String key, String value) async {
|
||||
class RustKeyValue {
|
||||
static Future<void> set(String key, String value) async {
|
||||
await ConfigEventSetKeyValue(
|
||||
KeyValuePB.create()
|
||||
..key = key
|
||||
@ -80,15 +79,13 @@ class RustKeyValue implements KeyValueStorage {
|
||||
).send();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, String>> get(String key) async {
|
||||
static Future<Either<FlowyError, String>> get(String key) async {
|
||||
final payload = KeyPB.create()..key = key;
|
||||
final response = await ConfigEventGetKeyValue(payload).send();
|
||||
return response.swap().map((r) => r.value);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<FlowyError, T>> getWithFormat<T>(
|
||||
static Future<Either<FlowyError, T>> getWithFormat<T>(
|
||||
String key,
|
||||
T Function(String value) formatter,
|
||||
) async {
|
||||
@ -99,15 +96,9 @@ class RustKeyValue implements KeyValueStorage {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> remove(String key) async {
|
||||
static Future<void> remove(String key) async {
|
||||
await ConfigEventRemoveKeyValue(
|
||||
KeyPB.create()..key = key,
|
||||
).send();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> clear() async {
|
||||
// TODO(Lucas): implement clear
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,6 @@ class KVKeys {
|
||||
/// The key for the path location of the local data for the whole app.
|
||||
static const String pathLocation = '$prefix.path_location';
|
||||
|
||||
/// The key for the last time login type.
|
||||
///
|
||||
/// The value is one of the following:
|
||||
/// - local
|
||||
/// - supabase
|
||||
static const String loginType = '$prefix.login_type';
|
||||
|
||||
/// The key for saving the window size
|
||||
///
|
||||
/// The value is a json string with the following format:
|
||||
|
@ -5,10 +5,12 @@ part 'backend_env.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class AppFlowyEnv {
|
||||
final int cloud_type;
|
||||
final SupabaseConfiguration supabase_config;
|
||||
final AppFlowyCloudConfiguration appflowy_cloud_config;
|
||||
|
||||
AppFlowyEnv({
|
||||
required this.cloud_type,
|
||||
required this.supabase_config,
|
||||
required this.appflowy_cloud_config,
|
||||
});
|
||||
@ -22,12 +24,10 @@ class AppFlowyEnv {
|
||||
@JsonSerializable()
|
||||
class SupabaseConfiguration {
|
||||
/// Indicates whether the sync feature is enabled.
|
||||
final bool enable_sync;
|
||||
final String url;
|
||||
final String anon_key;
|
||||
|
||||
SupabaseConfiguration({
|
||||
this.enable_sync = true,
|
||||
required this.url,
|
||||
required this.anon_key,
|
||||
});
|
||||
|
@ -34,22 +34,38 @@ class InitRustSDKTask extends LaunchTask {
|
||||
}
|
||||
|
||||
AppFlowyEnv getAppFlowyEnv() {
|
||||
final supabaseConfig = SupabaseConfiguration(
|
||||
enable_sync: true,
|
||||
url: Env.supabaseUrl,
|
||||
anon_key: Env.supabaseAnonKey,
|
||||
);
|
||||
if (isCloudEnabled) {
|
||||
final supabaseConfig = SupabaseConfiguration(
|
||||
url: Env.supabaseUrl,
|
||||
anon_key: Env.supabaseAnonKey,
|
||||
);
|
||||
|
||||
final appflowyCloudConfig = AppFlowyCloudConfiguration(
|
||||
base_url: Env.afCloudBaseUrl,
|
||||
ws_base_url: Env.afCloudWSBaseUrl,
|
||||
gotrue_url: Env.afCloudGoTrueUrl,
|
||||
);
|
||||
final appflowyCloudConfig = AppFlowyCloudConfiguration(
|
||||
base_url: Env.afCloudBaseUrl,
|
||||
ws_base_url: Env.afCloudWSBaseUrl,
|
||||
gotrue_url: Env.afCloudGoTrueUrl,
|
||||
);
|
||||
|
||||
return AppFlowyEnv(
|
||||
supabase_config: supabaseConfig,
|
||||
appflowy_cloud_config: appflowyCloudConfig,
|
||||
);
|
||||
return AppFlowyEnv(
|
||||
cloud_type: Env.cloudType,
|
||||
supabase_config: supabaseConfig,
|
||||
appflowy_cloud_config: appflowyCloudConfig,
|
||||
);
|
||||
} else {
|
||||
// Use the default configuration if the cloud feature is disabled
|
||||
final supabaseConfig = SupabaseConfiguration(url: '', anon_key: '');
|
||||
final appflowyCloudConfig = AppFlowyCloudConfiguration(
|
||||
base_url: '',
|
||||
ws_base_url: '',
|
||||
gotrue_url: '',
|
||||
);
|
||||
|
||||
return AppFlowyEnv(
|
||||
cloud_type: 0,
|
||||
supabase_config: supabaseConfig,
|
||||
appflowy_cloud_config: appflowyCloudConfig,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The default directory to store the user data. The directory can be
|
||||
|
@ -1,5 +1,3 @@
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/historical_user_bloc.dart';
|
||||
@ -41,7 +39,6 @@ class SignInAnonymousButton extends StatelessWidget {
|
||||
: LocaleKeys.signIn_continueAnonymousUser.tr();
|
||||
final onTap = state.historicalUsers.isEmpty
|
||||
? () {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'local');
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInAsGuest());
|
||||
|
@ -1,8 +1,5 @@
|
||||
import 'package:appflowy/core/config/kv.dart';
|
||||
import 'package:appflowy/core/config/kv_keys.dart';
|
||||
import 'package:appflowy/generated/flowy_svgs.g.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/user/application/sign_in_bloc.dart';
|
||||
import 'package:appflowy/user/presentation/presentation.dart';
|
||||
import 'package:appflowy/util/platform_extension.dart';
|
||||
@ -165,19 +162,16 @@ class _ThirdPartySignInButton extends StatelessWidget {
|
||||
}
|
||||
|
||||
void _signInWithGoogle(BuildContext context) {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||
context.read<SignInBloc>().add(
|
||||
const SignInEvent.signedInWithOAuth('google'),
|
||||
);
|
||||
}
|
||||
|
||||
void _signInWithGithub(BuildContext context) {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||
context.read<SignInBloc>().add(const SignInEvent.signedInWithOAuth('github'));
|
||||
}
|
||||
|
||||
void _signInWithDiscord(BuildContext context) {
|
||||
getIt<KeyValueStorage>().set(KVKeys.loginType, 'supabase');
|
||||
context
|
||||
.read<SignInBloc>()
|
||||
.add(const SignInEvent.signedInWithOAuth('discord'));
|
||||
|
@ -12,7 +12,7 @@ import '../../../core/notification/user_notification.dart';
|
||||
class UserCloudConfigListener {
|
||||
final String userId;
|
||||
StreamSubscription<SubscribeObject>? _subscription;
|
||||
void Function(Either<UserCloudConfigPB, FlowyError>)? _onSettingChanged;
|
||||
void Function(Either<CloudSettingPB, FlowyError>)? _onSettingChanged;
|
||||
|
||||
UserNotificationParser? _userParser;
|
||||
UserCloudConfigListener({
|
||||
@ -20,7 +20,7 @@ class UserCloudConfigListener {
|
||||
});
|
||||
|
||||
void start({
|
||||
void Function(Either<UserCloudConfigPB, FlowyError>)? onSettingChanged,
|
||||
void Function(Either<CloudSettingPB, FlowyError>)? onSettingChanged,
|
||||
}) {
|
||||
_onSettingChanged = onSettingChanged;
|
||||
_userParser = UserNotificationParser(
|
||||
@ -45,8 +45,8 @@ class UserCloudConfigListener {
|
||||
switch (ty) {
|
||||
case UserNotification.DidUpdateCloudConfig:
|
||||
result.fold(
|
||||
(payload) => _onSettingChanged
|
||||
?.call(left(UserCloudConfigPB.fromBuffer(payload))),
|
||||
(payload) =>
|
||||
_onSettingChanged?.call(left(CloudSettingPB.fromBuffer(payload))),
|
||||
(error) => _onSettingChanged?.call(right(error)),
|
||||
);
|
||||
break;
|
||||
|
@ -15,7 +15,7 @@ class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
|
||||
|
||||
CloudSettingBloc({
|
||||
required String userId,
|
||||
required UserCloudConfigPB config,
|
||||
required CloudSettingPB config,
|
||||
}) : _listener = UserCloudConfigListener(userId: userId),
|
||||
super(CloudSettingState.initial(config)) {
|
||||
on<CloudSettingEvent>((event, emit) async {
|
||||
@ -38,7 +38,7 @@ class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
|
||||
final update = UpdateCloudConfigPB.create()..enableSync = enable;
|
||||
updateCloudConfig(update);
|
||||
},
|
||||
didReceiveConfig: (UserCloudConfigPB config) {
|
||||
didReceiveConfig: (CloudSettingPB config) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
config: config,
|
||||
@ -64,7 +64,7 @@ class CloudSettingBloc extends Bloc<CloudSettingEvent, CloudSettingState> {
|
||||
class CloudSettingEvent with _$CloudSettingEvent {
|
||||
const factory CloudSettingEvent.initial() = _Initial;
|
||||
const factory CloudSettingEvent.didReceiveConfig(
|
||||
UserCloudConfigPB config,
|
||||
CloudSettingPB config,
|
||||
) = _DidSyncSupabaseConfig;
|
||||
const factory CloudSettingEvent.enableSync(bool enable) = _EnableSync;
|
||||
const factory CloudSettingEvent.enableEncrypt(bool enable) = _EnableEncrypt;
|
||||
@ -73,13 +73,12 @@ class CloudSettingEvent with _$CloudSettingEvent {
|
||||
@freezed
|
||||
class CloudSettingState with _$CloudSettingState {
|
||||
const factory CloudSettingState({
|
||||
required UserCloudConfigPB config,
|
||||
required CloudSettingPB config,
|
||||
required Either<Unit, String> successOrFailure,
|
||||
required LoadingState loadingState,
|
||||
}) = _CloudSettingState;
|
||||
|
||||
factory CloudSettingState.initial(UserCloudConfigPB config) =>
|
||||
CloudSettingState(
|
||||
factory CloudSettingState.initial(CloudSettingPB config) => CloudSettingState(
|
||||
config: config,
|
||||
successOrFailure: left(unit),
|
||||
loadingState: LoadingState.finish(left(unit)),
|
||||
|
@ -14,7 +14,7 @@ enum SettingsPage {
|
||||
files,
|
||||
user,
|
||||
notifications,
|
||||
syncSetting,
|
||||
cloud,
|
||||
shortcuts,
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/settings/application_data_storage.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -32,7 +34,13 @@ class SettingsLocationCubit extends Cubit<SettingsLocationState> {
|
||||
}
|
||||
|
||||
Future<void> _init() async {
|
||||
final path = await getIt<ApplicationDataStorage>().getPath();
|
||||
emit(SettingsLocationState.didReceivedPath(path));
|
||||
// The backend might change the real path that storge the data. So it needs
|
||||
// to get the path from the backend instead of the KeyValueStorage
|
||||
await UserEventGetUserSetting().send().then((result) {
|
||||
result.fold(
|
||||
(l) => emit(SettingsLocationState.didReceivedPath(l.userFolder)),
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_notifications_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/sync_setting_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/setting_cloud_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_appearance_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_customize_shortcuts_view.dart';
|
||||
import 'package:appflowy/workspace/presentation/settings/widgets/settings_file_system_view.dart';
|
||||
@ -104,8 +104,8 @@ class SettingsDialog extends StatelessWidget {
|
||||
);
|
||||
case SettingsPage.notifications:
|
||||
return const SettingsNotificationsView();
|
||||
case SettingsPage.syncSetting:
|
||||
return SyncSettingView(userId: user.id.toString());
|
||||
case SettingsPage.cloud:
|
||||
return SettingCloudView(userId: user.id.toString());
|
||||
case SettingsPage.shortcuts:
|
||||
return const SettingsCustomizeShortcutsWrapper();
|
||||
default:
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:appflowy/env/env.dart';
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/workspace/application/settings/setting_supabase_bloc.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
@ -14,13 +15,13 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class SyncSettingView extends StatelessWidget {
|
||||
class SettingCloudView extends StatelessWidget {
|
||||
final String userId;
|
||||
const SyncSettingView({required this.userId, super.key});
|
||||
const SettingCloudView({required this.userId, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FutureBuilder<Either<UserCloudConfigPB, FlowyError>>(
|
||||
return FutureBuilder<Either<CloudSettingPB, FlowyError>>(
|
||||
future: UserEventGetCloudConfig().send(),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.data != null &&
|
||||
@ -34,10 +35,15 @@ class SyncSettingView extends StatelessWidget {
|
||||
)..add(const CloudSettingEvent.initial()),
|
||||
child: BlocBuilder<CloudSettingBloc, CloudSettingState>(
|
||||
builder: (context, state) {
|
||||
return const Column(
|
||||
return Column(
|
||||
children: [
|
||||
EnableSync(),
|
||||
EnableEncrypt(),
|
||||
const EnableSync(),
|
||||
// Currently the appflowy cloud is not support end-to-end encryption.
|
||||
if (!isAppFlowyCloudEnabled) const EnableEncrypt(),
|
||||
if (isAppFlowyCloudEnabled)
|
||||
AppFlowyCloudInformationWidget(
|
||||
url: state.config.serverUrl,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -58,6 +64,34 @@ class SyncSettingView extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
class AppFlowyCloudInformationWidget extends StatelessWidget {
|
||||
final String url;
|
||||
|
||||
const AppFlowyCloudInformationWidget({required this.url, super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
// Wrap the Opacity widget with Expanded
|
||||
child: Opacity(
|
||||
opacity: 0.6,
|
||||
child: FlowyText(
|
||||
"${LocaleKeys.settings_menu_cloudURL.tr()}: $url",
|
||||
maxLines: null, // Allow the text to wrap
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class EnableEncrypt extends StatelessWidget {
|
||||
const EnableEncrypt({super.key});
|
||||
|
||||
@ -121,7 +155,6 @@ class EnableEncrypt extends StatelessWidget {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(text: state.config.encryptSecret),
|
||||
);
|
||||
// TODO(Lucas): bring the toast to the top of the dialog.
|
||||
showMessageToast(LocaleKeys.message_copy_success.tr());
|
||||
},
|
||||
),
|
@ -48,45 +48,10 @@ class SettingsFileLocationCustomizerState
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// display file paths.
|
||||
Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FlowyText.medium(
|
||||
LocaleKeys.settings_files_defaultLocation.tr(),
|
||||
fontSize: 13,
|
||||
overflow: TextOverflow.visible,
|
||||
).padding(horizontal: 5),
|
||||
const VSpace(5),
|
||||
_CopyableText(
|
||||
usingPath: path,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_path(path),
|
||||
|
||||
// display the icons
|
||||
Flexible(
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Flexible(
|
||||
child: _ChangeStoragePathButton(
|
||||
usingPath: path,
|
||||
),
|
||||
),
|
||||
const HSpace(10),
|
||||
_OpenStorageButton(
|
||||
usingPath: path,
|
||||
),
|
||||
_RecoverDefaultStorageButton(
|
||||
usingPath: path,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buttons(path),
|
||||
],
|
||||
);
|
||||
},
|
||||
@ -95,6 +60,55 @@ class SettingsFileLocationCustomizerState
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _path(String path) {
|
||||
return Flexible(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
FlowyText.medium(
|
||||
LocaleKeys.settings_files_defaultLocation.tr(),
|
||||
fontSize: 13,
|
||||
overflow: TextOverflow.visible,
|
||||
).padding(horizontal: 5),
|
||||
const VSpace(5),
|
||||
_CopyableText(
|
||||
usingPath: path,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buttons(String path) {
|
||||
final List<Widget> children = [];
|
||||
children.addAll([
|
||||
Flexible(
|
||||
child: _ChangeStoragePathButton(
|
||||
usingPath: path,
|
||||
),
|
||||
),
|
||||
const HSpace(10),
|
||||
]);
|
||||
|
||||
children.add(
|
||||
_OpenStorageButton(
|
||||
usingPath: path,
|
||||
),
|
||||
);
|
||||
|
||||
children.add(
|
||||
_RecoverDefaultStorageButton(
|
||||
usingPath: path,
|
||||
),
|
||||
);
|
||||
|
||||
return Flexible(
|
||||
child: Row(mainAxisAlignment: MainAxisAlignment.end, children: children),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _CopyableText extends StatelessWidget {
|
||||
|
@ -67,9 +67,9 @@ class SettingsMenu extends StatelessWidget {
|
||||
if (showSyncSetting) ...[
|
||||
const SizedBox(height: 10),
|
||||
SettingsMenuElement(
|
||||
page: SettingsPage.syncSetting,
|
||||
page: SettingsPage.cloud,
|
||||
selectedPage: currentPage,
|
||||
label: LocaleKeys.settings_menu_syncSetting.tr(),
|
||||
label: LocaleKeys.settings_menu_cloudSetting.tr(),
|
||||
icon: Icons.sync,
|
||||
changeSelectedPage: changeSelectedPage,
|
||||
),
|
||||
|
16
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
16
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -138,7 +138,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -768,7 +768,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1449,7 +1449,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2808,7 +2808,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2824,7 +2824,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -3251,7 +3251,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -4994,7 +4994,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -5739,7 +5739,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
@ -56,7 +56,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fe977fc8285addd5386e940738cdffbbda9eb44e" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b578c83cc912255e48dea9e33a203a069ce7d0c5" }
|
||||
# Please use the following script to update collab.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
@ -1,4 +1,5 @@
|
||||
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig, DEFAULT_NAME};
|
||||
use flowy_core::config::AppFlowyCoreConfig;
|
||||
use flowy_core::{AppFlowyCore, DEFAULT_NAME};
|
||||
|
||||
pub fn init_flowy_core() -> AppFlowyCore {
|
||||
let config_json = include_str!("../tauri.conf.json");
|
||||
|
@ -262,6 +262,8 @@
|
||||
"logoutPrompt": "Are you sure to logout?",
|
||||
"selfEncryptionLogoutPrompt": "Are you sure you want to log out? Please ensure you have copied the encryption secret",
|
||||
"syncSetting": "Sync Setting",
|
||||
"cloudSetting": "Cloud Setting",
|
||||
"cloudURL": "Server URL",
|
||||
"enableSync": "Enable sync",
|
||||
"enableEncrypt": "Encrypt data",
|
||||
"enableEncryptPrompt": "Activate encryption to secure your data with this secret. Store it safely; once enabled, it can't be turned off. If lost, your data becomes irretrievable. Click to copy",
|
||||
|
39
frontend/rust-lib/Cargo.lock
generated
39
frontend/rust-lib/Cargo.lock
generated
@ -124,7 +124,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "app-error"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -666,7 +666,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "client-api"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -1150,7 +1150,7 @@ dependencies = [
|
||||
"cssparser-macros",
|
||||
"dtoa-short",
|
||||
"itoa",
|
||||
"phf 0.11.2",
|
||||
"phf 0.8.0",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
@ -1250,6 +1250,7 @@ dependencies = [
|
||||
"protobuf",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"tokio",
|
||||
"tracing",
|
||||
]
|
||||
@ -1276,7 +1277,7 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
||||
[[package]]
|
||||
name = "database-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2467,7 +2468,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"futures-util",
|
||||
@ -2483,7 +2484,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "gotrue-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
@ -2844,7 +2845,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "infra"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"reqwest",
|
||||
@ -3657,7 +3658,7 @@ version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||
dependencies = [
|
||||
"phf_macros 0.8.0",
|
||||
"phf_macros",
|
||||
"phf_shared 0.8.0",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
@ -3677,7 +3678,6 @@ version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||
dependencies = [
|
||||
"phf_macros 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
]
|
||||
|
||||
@ -3745,19 +3745,6 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_macros"
|
||||
version = "0.11.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3444646e286606587e49f3bcf1679b8cef1dc2c5ecc29ddacaffc305180d464b"
|
||||
dependencies = [
|
||||
"phf_generator 0.11.2",
|
||||
"phf_shared 0.11.2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf_shared"
|
||||
version = "0.8.0"
|
||||
@ -3961,7 +3948,7 @@ checksum = "8bdf592881d821b83d471f8af290226c8d51402259e9bb5be7f9f8bdebbb11ac"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"heck 0.4.1",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"log",
|
||||
"multimap",
|
||||
"once_cell",
|
||||
@ -3982,7 +3969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "265baba7fabd416cf5078179f7d2cbeca4ce7a9041111900675ea7c4cb8a4c32"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.10.5",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.31",
|
||||
@ -4321,7 +4308,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "realtime-entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
@ -4965,7 +4952,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "shared_entity"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=fe977fc8285addd5386e940738cdffbbda9eb44e#fe977fc8285addd5386e940738cdffbbda9eb44e"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b578c83cc912255e48dea9e33a203a069ce7d0c5#b578c83cc912255e48dea9e33a203a069ce7d0c5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app-error",
|
||||
|
@ -99,7 +99,7 @@ incremental = false
|
||||
# Run the script:
|
||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||
# ⚠️⚠️⚠️️
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "fe977fc8285addd5386e940738cdffbbda9eb44e" }
|
||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b578c83cc912255e48dea9e33a203a069ce7d0c5" }
|
||||
# Please use the following script to update collab.
|
||||
# Working directory: frontend
|
||||
#
|
||||
|
@ -17,6 +17,7 @@ byteorder = { version = "1.4.3" }
|
||||
protobuf.workspace = true
|
||||
tokio = { workspace = true, features = ["full", "rt-multi-thread", "tracing"] }
|
||||
serde.workspace = true
|
||||
serde_repr.workspace = true
|
||||
serde_json.workspace = true
|
||||
bytes.workspace = true
|
||||
crossbeam-utils = "0.8.15"
|
||||
|
@ -1,21 +1,63 @@
|
||||
use serde::Deserialize;
|
||||
use serde_repr::Deserialize_repr;
|
||||
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
pub struct AppFlowyEnv {
|
||||
cloud_type: CloudType,
|
||||
supabase_config: SupabaseConfiguration,
|
||||
appflowy_cloud_config: AFCloudConfiguration,
|
||||
}
|
||||
|
||||
const CLOUT_TYPE_STR: &str = "APPFLOWY_CLOUD_ENV_CLOUD_TYPE";
|
||||
|
||||
#[derive(Deserialize_repr, Debug, Clone)]
|
||||
#[repr(u8)]
|
||||
pub enum CloudType {
|
||||
Local = 0,
|
||||
Supabase = 1,
|
||||
AppFlowyCloud = 2,
|
||||
}
|
||||
|
||||
impl CloudType {
|
||||
fn write_env(&self) {
|
||||
let s = self.clone() as u8;
|
||||
std::env::set_var(CLOUT_TYPE_STR, s.to_string());
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn from_str(s: &str) -> Self {
|
||||
match s {
|
||||
"0" => CloudType::Local,
|
||||
"1" => CloudType::Supabase,
|
||||
"2" => CloudType::AppFlowyCloud,
|
||||
_ => CloudType::Local,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn from_env() -> Self {
|
||||
let cloud_type_str = std::env::var(CLOUT_TYPE_STR).unwrap_or_default();
|
||||
CloudType::from_str(&cloud_type_str)
|
||||
}
|
||||
}
|
||||
|
||||
impl AppFlowyEnv {
|
||||
/// Parse the environment variable from the frontend application. The frontend will
|
||||
/// pass the environment variable as a json string after launching.
|
||||
pub fn write_env_from(env_str: &str) {
|
||||
if let Ok(env) = serde_json::from_str::<AppFlowyEnv>(env_str) {
|
||||
env.supabase_config.write_env();
|
||||
env.appflowy_cloud_config.write_env();
|
||||
let _ = env.cloud_type.write_env();
|
||||
let is_valid = env.appflowy_cloud_config.write_env().is_ok();
|
||||
// Note on Configuration Priority:
|
||||
// If both Supabase config and AppFlowy cloud config are provided in the '.env' file,
|
||||
// the AppFlowy cloud config will be prioritized and the Supabase config ignored.
|
||||
// Ensure only one of these configurations is active at any given time.
|
||||
if !is_valid {
|
||||
let _ = env.supabase_config.write_env();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ use lazy_static::lazy_static;
|
||||
use parking_lot::Mutex;
|
||||
use tracing::{error, trace};
|
||||
|
||||
use flowy_core::config::AppFlowyCoreConfig;
|
||||
use flowy_core::*;
|
||||
use flowy_notification::{register_notification_sender, unregister_all_notification_sender};
|
||||
use lib_dispatch::prelude::ToBytes;
|
||||
|
@ -5,7 +5,8 @@ use std::sync::Arc;
|
||||
use nanoid::nanoid;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use flowy_core::{AppFlowyCore, AppFlowyCoreConfig};
|
||||
use flowy_core::config::AppFlowyCoreConfig;
|
||||
use flowy_core::AppFlowyCore;
|
||||
use flowy_notification::register_notification_sender;
|
||||
use flowy_user::entities::AuthTypePB;
|
||||
|
||||
|
@ -13,8 +13,8 @@ use flowy_notification::entities::SubscribeObject;
|
||||
use flowy_notification::NotificationSender;
|
||||
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
|
||||
use flowy_user::entities::{
|
||||
AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB, UpdateCloudConfigPB,
|
||||
UpdateUserProfilePayloadPB, UserCloudConfigPB, UserProfilePB,
|
||||
AuthTypePB, CloudSettingPB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, SignUpPayloadPB,
|
||||
UpdateCloudConfigPB, UpdateUserProfilePayloadPB, UserProfilePB,
|
||||
};
|
||||
use flowy_user::errors::{FlowyError, FlowyResult};
|
||||
use flowy_user::event_map::UserEvent::*;
|
||||
@ -29,7 +29,7 @@ impl EventIntegrationTest {
|
||||
.event(GetCloudConfig)
|
||||
.async_send()
|
||||
.await
|
||||
.parse::<UserCloudConfigPB>();
|
||||
.parse::<CloudSettingPB>();
|
||||
let update = UpdateCloudConfigPB {
|
||||
enable_sync: None,
|
||||
enable_encrypt: Some(true),
|
||||
|
86
frontend/rust-lib/flowy-core/src/config.rs
Normal file
86
frontend/rust-lib/flowy-core/src/config.rs
Normal file
@ -0,0 +1,86 @@
|
||||
use std::fmt;
|
||||
use std::path::Path;
|
||||
|
||||
use base64::Engine;
|
||||
use tracing::{error, info};
|
||||
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
use flowy_user::manager::URL_SAFE_ENGINE;
|
||||
|
||||
use crate::integrate::log::create_log_filter;
|
||||
use crate::integrate::util::copy_dir_recursive;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppFlowyCoreConfig {
|
||||
/// Different `AppFlowyCoreConfig` instance should have different name
|
||||
pub(crate) name: String,
|
||||
/// Panics if the `root` path is not existing
|
||||
pub storage_path: String,
|
||||
pub(crate) log_filter: String,
|
||||
cloud_config: Option<AFCloudConfiguration>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for AppFlowyCoreConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut debug = f.debug_struct("AppFlowy Configuration");
|
||||
debug.field("storage_path", &self.storage_path);
|
||||
if let Some(config) = &self.cloud_config {
|
||||
debug.field("base_url", &config.base_url);
|
||||
debug.field("ws_url", &config.ws_base_url);
|
||||
}
|
||||
debug.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn migrate_local_version_data_folder(root: &str, url: &str) -> String {
|
||||
// Isolate the user data folder by using the base url of AppFlowy cloud. This is to avoid
|
||||
// the user data folder being shared by different AppFlowy cloud.
|
||||
let server_base64 = URL_SAFE_ENGINE.encode(&url);
|
||||
let storage_path = format!("{}_{}", root, server_base64);
|
||||
|
||||
// Copy the user data folder from the root path to the isolated path
|
||||
// The root path without any suffix is the created by the local version AppFlowy
|
||||
if !Path::new(&storage_path).exists() && Path::new(root).exists() {
|
||||
info!("Copy dir from {} to {}", root, storage_path);
|
||||
let src = Path::new(root);
|
||||
match copy_dir_recursive(&src, Path::new(&storage_path)) {
|
||||
Ok(_) => storage_path,
|
||||
Err(err) => {
|
||||
// when the copy dir failed, use the root path as the storage path
|
||||
error!("Copy dir failed: {}", err);
|
||||
root.to_string()
|
||||
},
|
||||
}
|
||||
} else {
|
||||
storage_path
|
||||
}
|
||||
}
|
||||
|
||||
impl AppFlowyCoreConfig {
|
||||
pub fn new(root: &str, name: String) -> Self {
|
||||
let cloud_config = AFCloudConfiguration::from_env().ok();
|
||||
let storage_path = match &cloud_config {
|
||||
None => {
|
||||
let supabase_config = SupabaseConfiguration::from_env().ok();
|
||||
match &supabase_config {
|
||||
None => root.to_string(),
|
||||
Some(config) => migrate_local_version_data_folder(root, &config.url),
|
||||
}
|
||||
},
|
||||
Some(config) => migrate_local_version_data_folder(root, &config.base_url),
|
||||
};
|
||||
|
||||
AppFlowyCoreConfig {
|
||||
name,
|
||||
storage_path,
|
||||
log_filter: create_log_filter("info".to_owned(), vec![]),
|
||||
cloud_config,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_filter(mut self, level: &str, with_crates: Vec<String>) -> Self {
|
||||
self.log_filter = create_log_filter(level.to_owned(), with_crates);
|
||||
self
|
||||
}
|
||||
}
|
@ -124,13 +124,7 @@ impl ServerProvider {
|
||||
Ok::<Arc<dyn AppFlowyServer>, FlowyError>(server)
|
||||
},
|
||||
ServerType::Supabase => {
|
||||
let config = match SupabaseConfiguration::from_env() {
|
||||
Ok(config) => config,
|
||||
Err(e) => {
|
||||
*self.enable_sync.write() = false;
|
||||
return Err(e);
|
||||
},
|
||||
};
|
||||
let config = SupabaseConfiguration::from_env()?;
|
||||
let uid = self.uid.clone();
|
||||
tracing::trace!("🔑Supabase config: {:?}", config);
|
||||
let encryption = Arc::downgrade(&*self.encryption.read());
|
||||
|
@ -21,6 +21,8 @@ use flowy_error::FlowyError;
|
||||
use flowy_folder_deps::cloud::{
|
||||
FolderCloudService, FolderData, FolderSnapshot, Workspace, WorkspaceRecord,
|
||||
};
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
use flowy_storage::{FileStorageService, StorageObject};
|
||||
use flowy_user::event_map::UserCloudServiceProvider;
|
||||
use flowy_user_deps::cloud::UserCloudService;
|
||||
@ -68,13 +70,10 @@ impl UserCloudServiceProvider for ServerProvider {
|
||||
}
|
||||
|
||||
fn set_enable_sync(&self, uid: i64, enable_sync: bool) {
|
||||
match self.get_server(&self.get_server_type()) {
|
||||
Ok(server) => {
|
||||
server.set_enable_sync(uid, enable_sync);
|
||||
*self.enable_sync.write() = enable_sync;
|
||||
*self.uid.write() = Some(uid);
|
||||
},
|
||||
Err(e) => tracing::error!("🔴Failed to enable sync: {:?}", e),
|
||||
if let Ok(server) = self.get_server(&self.get_server_type()) {
|
||||
server.set_enable_sync(uid, enable_sync);
|
||||
*self.enable_sync.write() = enable_sync;
|
||||
*self.uid.write() = Some(uid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,8 +135,16 @@ impl UserCloudServiceProvider for ServerProvider {
|
||||
Ok(user_service)
|
||||
}
|
||||
|
||||
fn service_name(&self) -> String {
|
||||
self.get_server_type().to_string()
|
||||
fn service_url(&self) -> String {
|
||||
match self.get_server_type() {
|
||||
ServerType::Local => "".to_string(),
|
||||
ServerType::AFCloud => AFCloudConfiguration::from_env()
|
||||
.map(|config| config.base_url)
|
||||
.unwrap_or_default(),
|
||||
ServerType::Supabase => SupabaseConfiguration::from_env()
|
||||
.map(|config| config.url)
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,7 +47,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
||||
self
|
||||
.server_provider
|
||||
.set_enable_sync(user_id, cloud_config.enable_sync);
|
||||
if cloud_config.enable_encrypt() {
|
||||
if cloud_config.enable_encrypt {
|
||||
self
|
||||
.server_provider
|
||||
.set_encrypt_secret(cloud_config.encrypt_secret.clone());
|
||||
|
@ -1,11 +1,9 @@
|
||||
#![allow(unused_doc_comments)]
|
||||
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Weak;
|
||||
use std::time::Duration;
|
||||
use std::{fmt, sync::Arc};
|
||||
|
||||
use base64::Engine;
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{debug, error, event, info, instrument};
|
||||
|
||||
@ -13,24 +11,24 @@ use collab_integrate::collab_builder::{AppFlowyCollabBuilder, CollabSource};
|
||||
use flowy_database2::DatabaseManager;
|
||||
use flowy_document2::manager::DocumentManager;
|
||||
use flowy_folder2::manager::FolderManager;
|
||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||
use flowy_sqlite::kv::StorePreferences;
|
||||
use flowy_storage::FileStorageService;
|
||||
use flowy_task::{TaskDispatcher, TaskRunner};
|
||||
use flowy_user::event_map::UserCloudServiceProvider;
|
||||
use flowy_user::manager::{UserManager, UserSessionConfig, URL_SAFE_ENGINE};
|
||||
use flowy_user::manager::{UserManager, UserSessionConfig};
|
||||
use lib_dispatch::prelude::*;
|
||||
use lib_dispatch::runtime::AFPluginRuntime;
|
||||
use module::make_plugins;
|
||||
pub use module::*;
|
||||
|
||||
use crate::config::AppFlowyCoreConfig;
|
||||
use crate::deps_resolve::*;
|
||||
use crate::integrate::collab_interact::CollabInteractImpl;
|
||||
use crate::integrate::log::{create_log_filter, init_log};
|
||||
use crate::integrate::log::init_log;
|
||||
use crate::integrate::server::{current_server_type, ServerProvider, ServerType};
|
||||
use crate::integrate::user::UserStatusCallbackImpl;
|
||||
use crate::integrate::util::copy_dir_recursive;
|
||||
|
||||
pub mod config;
|
||||
mod deps_resolve;
|
||||
mod integrate;
|
||||
pub mod module;
|
||||
@ -39,72 +37,6 @@ pub mod module;
|
||||
/// Don't change this.
|
||||
pub const DEFAULT_NAME: &str = "appflowy";
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppFlowyCoreConfig {
|
||||
/// Different `AppFlowyCoreConfig` instance should have different name
|
||||
name: String,
|
||||
/// Panics if the `root` path is not existing
|
||||
pub storage_path: String,
|
||||
log_filter: String,
|
||||
cloud_config: Option<AFCloudConfiguration>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for AppFlowyCoreConfig {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut debug = f.debug_struct("AppFlowy Configuration");
|
||||
debug.field("storage_path", &self.storage_path);
|
||||
if let Some(config) = &self.cloud_config {
|
||||
debug.field("base_url", &config.base_url);
|
||||
debug.field("ws_url", &config.ws_base_url);
|
||||
}
|
||||
debug.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl AppFlowyCoreConfig {
|
||||
pub fn new(root: &str, name: String) -> Self {
|
||||
let cloud_config = AFCloudConfiguration::from_env().ok();
|
||||
let storage_path = match &cloud_config {
|
||||
None => root.to_string(),
|
||||
Some(config) => {
|
||||
// Isolate the user data folder by the base url of AppFlowy cloud. This is to avoid
|
||||
// the user data folder being shared by different AppFlowy cloud.
|
||||
let server_base64 = URL_SAFE_ENGINE.encode(&config.base_url);
|
||||
let storage_path = format!("{}_{}", root, server_base64);
|
||||
|
||||
// Copy the user data folder from the root path to the isolated path
|
||||
// The root path only exists when using the local version of appflowy
|
||||
if !Path::new(&storage_path).exists() && Path::new(root).exists() {
|
||||
info!("Copy dir from {} to {}", root, storage_path);
|
||||
let src = Path::new(root);
|
||||
match copy_dir_recursive(&src, Path::new(&storage_path)) {
|
||||
Ok(_) => storage_path,
|
||||
Err(err) => {
|
||||
// when the copy dir failed, use the root path as the storage path
|
||||
error!("Copy dir failed: {}", err);
|
||||
root.to_string()
|
||||
},
|
||||
}
|
||||
} else {
|
||||
storage_path
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
AppFlowyCoreConfig {
|
||||
name,
|
||||
storage_path,
|
||||
log_filter: create_log_filter("info".to_owned(), vec![]),
|
||||
cloud_config: AFCloudConfiguration::from_env().ok(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn log_filter(mut self, level: &str, with_crates: Vec<String>) -> Self {
|
||||
self.log_filter = create_log_filter(level.to_owned(), with_crates);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AppFlowyCore {
|
||||
#[allow(dead_code)]
|
||||
|
@ -159,7 +159,7 @@ impl FolderManager {
|
||||
} => {
|
||||
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
||||
if is_exist {
|
||||
event!(Level::INFO, "Restore folder from local disk");
|
||||
event!(Level::INFO, "Init folder from local disk");
|
||||
let collab = self
|
||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||
.await?;
|
||||
|
@ -2,11 +2,11 @@ use std::fmt::Display;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
|
||||
pub const APPFLOWY_CLOUD_BASE_URL: &str = "APPFLOWY_CLOUD_BASE_URL";
|
||||
pub const APPFLOWY_CLOUD_WS_BASE_URL: &str = "APPFLOWY_CLOUD_WS_BASE_URL";
|
||||
pub const APPFLOWY_CLOUD_GOTRUE_URL: &str = "APPFLOWY_CLOUD_GOTRUE_URL";
|
||||
pub const APPFLOWY_CLOUD_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_BASE_URL";
|
||||
pub const APPFLOWY_CLOUD_WS_BASE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_WS_BASE_URL";
|
||||
pub const APPFLOWY_CLOUD_GOTRUE_URL: &str = "APPFLOWY_CLOUD_ENV_APPFLOWY_CLOUD_GOTRUE_URL";
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
pub struct AFCloudConfiguration {
|
||||
@ -60,10 +60,25 @@ impl AFCloudConfiguration {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<(), FlowyError> {
|
||||
if self.base_url.is_empty() || self.ws_base_url.is_empty() || self.gotrue_url.is_empty() {
|
||||
return Err(FlowyError::new(
|
||||
ErrorCode::InvalidAuthConfig,
|
||||
format!(
|
||||
"Invalid APPFLOWY_CLOUD_BASE_URL: {}, APPFLOWY_CLOUD_WS_BASE_URL: {}, APPFLOWY_CLOUD_GOTRUE_URL: {}",
|
||||
self.base_url, self.ws_base_url, self.gotrue_url,
|
||||
)),
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the configuration to the environment variables.
|
||||
pub fn write_env(&self) {
|
||||
pub fn write_env(&self) -> FlowyResult<()> {
|
||||
self.validate()?;
|
||||
std::env::set_var(APPFLOWY_CLOUD_BASE_URL, &self.base_url);
|
||||
std::env::set_var(APPFLOWY_CLOUD_WS_BASE_URL, &self.ws_base_url);
|
||||
std::env::set_var(APPFLOWY_CLOUD_GOTRUE_URL, &self.gotrue_url);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,9 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use flowy_error::{ErrorCode, FlowyError};
|
||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||
|
||||
pub const ENABLE_SUPABASE_SYNC: &str = "ENABLE_SUPABASE_SYNC";
|
||||
pub const SUPABASE_URL: &str = "SUPABASE_URL";
|
||||
pub const SUPABASE_ANON_KEY: &str = "SUPABASE_ANON_KEY";
|
||||
|
||||
pub const SUPABASE_DB: &str = "SUPABASE_DB";
|
||||
pub const SUPABASE_DB_USER: &str = "SUPABASE_DB_USER";
|
||||
pub const SUPABASE_DB_PASSWORD: &str = "SUPABASE_DB_PASSWORD";
|
||||
pub const SUPABASE_DB_PORT: &str = "SUPABASE_DB_PORT";
|
||||
pub const SUPABASE_URL: &str = "APPFLOWY_CLOUD_ENV_SUPABASE_URL";
|
||||
pub const SUPABASE_ANON_KEY: &str = "APPFLOWY_CLOUD_ENV_SUPABASE_ANON_KEY";
|
||||
|
||||
/// The configuration for the postgres database. It supports deserializing from the json string that
|
||||
/// passed from the frontend application. [AppFlowyEnv::parser]
|
||||
@ -39,9 +33,21 @@ impl SupabaseConfiguration {
|
||||
Ok(Self { url, anon_key })
|
||||
}
|
||||
|
||||
pub fn validate(&self) -> Result<(), FlowyError> {
|
||||
if self.url.is_empty() || self.anon_key.is_empty() {
|
||||
return Err(FlowyError::new(
|
||||
ErrorCode::InvalidAuthConfig,
|
||||
"Missing SUPABASE_URL or SUPABASE_ANON_KEY",
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write the configuration to the environment variables.
|
||||
pub fn write_env(&self) {
|
||||
pub fn write_env(&self) -> FlowyResult<()> {
|
||||
self.validate()?;
|
||||
std::env::set_var(SUPABASE_URL, &self.url);
|
||||
std::env::set_var(SUPABASE_ANON_KEY, &self.anon_key);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,6 @@ const DB_NAME: &str = "cache.db";
|
||||
pub struct StorePreferences {
|
||||
database: Option<Database>,
|
||||
}
|
||||
|
||||
impl StorePreferences {
|
||||
#[tracing::instrument(level = "trace", err)]
|
||||
pub fn new(root: &str) -> Result<Self, anyhow::Error> {
|
||||
@ -86,7 +85,6 @@ impl StorePreferences {
|
||||
.and_then(|v| serde_json::from_str(&v).ok())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn remove(&self, key: &str) {
|
||||
if let Some(conn) = self
|
||||
.database
|
||||
|
@ -20,7 +20,7 @@ use crate::entities::{
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UserCloudConfig {
|
||||
pub enable_sync: bool,
|
||||
enable_encrypt: bool,
|
||||
pub enable_encrypt: bool,
|
||||
// The secret used to encrypt the user's data
|
||||
pub encrypt_secret: String,
|
||||
}
|
||||
@ -34,10 +34,6 @@ impl UserCloudConfig {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn enable_encrypt(&self) -> bool {
|
||||
self.enable_encrypt
|
||||
}
|
||||
|
||||
pub fn with_enable_encrypt(mut self, enable_encrypt: bool) -> Self {
|
||||
self.enable_encrypt = enable_encrypt;
|
||||
// When the enable_encrypt is true, the encrypt_secret should not be empty
|
||||
|
@ -176,8 +176,8 @@ pub struct OauthProviderDataPB {
|
||||
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
|
||||
pub enum AuthTypePB {
|
||||
Local = 0,
|
||||
AFCloud = 1,
|
||||
Supabase = 2,
|
||||
Supabase = 1,
|
||||
AFCloud = 2,
|
||||
}
|
||||
|
||||
impl Default for AuthTypePB {
|
||||
|
@ -3,7 +3,6 @@ use std::collections::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||
use flowy_user_deps::cloud::UserCloudConfig;
|
||||
|
||||
use crate::entities::EncryptionTypePB;
|
||||
|
||||
@ -137,15 +136,18 @@ impl std::default::Default for AppearanceSettingsPB {
|
||||
}
|
||||
|
||||
#[derive(Default, ProtoBuf)]
|
||||
pub struct UserCloudConfigPB {
|
||||
pub struct CloudSettingPB {
|
||||
#[pb(index = 1)]
|
||||
enable_sync: bool,
|
||||
pub(crate) enable_sync: bool,
|
||||
|
||||
#[pb(index = 2)]
|
||||
enable_encrypt: bool,
|
||||
pub(crate) enable_encrypt: bool,
|
||||
|
||||
#[pb(index = 3)]
|
||||
pub encrypt_secret: String,
|
||||
|
||||
#[pb(index = 4)]
|
||||
pub server_url: String,
|
||||
}
|
||||
|
||||
#[derive(Default, ProtoBuf)]
|
||||
@ -178,16 +180,6 @@ pub struct UserEncryptionConfigurationPB {
|
||||
pub require_secret: bool,
|
||||
}
|
||||
|
||||
impl From<UserCloudConfig> for UserCloudConfigPB {
|
||||
fn from(value: UserCloudConfig) -> Self {
|
||||
Self {
|
||||
enable_sync: value.enable_sync,
|
||||
enable_encrypt: value.enable_encrypt(),
|
||||
encrypt_secret: value.encrypt_secret,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ProtoBuf_Enum, Debug, Clone, Eq, PartialEq, Default)]
|
||||
pub enum NetworkTypePB {
|
||||
#[default]
|
||||
|
@ -385,7 +385,6 @@ pub async fn set_cloud_config_handler(
|
||||
manager
|
||||
.set_encrypt_secret(session.user_id, encrypt_secret, encryption_type.clone())
|
||||
.await?;
|
||||
save_cloud_config(session.user_id, &store_preferences, config.clone())?;
|
||||
|
||||
let params =
|
||||
UpdateUserProfileParams::new(session.user_id).with_encryption_type(encryption_type);
|
||||
@ -393,28 +392,40 @@ pub async fn set_cloud_config_handler(
|
||||
}
|
||||
}
|
||||
|
||||
let config_pb = UserCloudConfigPB::from(config);
|
||||
save_cloud_config(session.user_id, &store_preferences, config.clone())?;
|
||||
|
||||
let payload = CloudSettingPB {
|
||||
enable_sync: config.enable_sync,
|
||||
enable_encrypt: config.enable_encrypt,
|
||||
encrypt_secret: config.encrypt_secret,
|
||||
server_url: manager.cloud_services.service_url(),
|
||||
};
|
||||
|
||||
send_notification(
|
||||
&session.user_id.to_string(),
|
||||
UserNotification::DidUpdateCloudConfig,
|
||||
)
|
||||
.payload(config_pb)
|
||||
.payload(payload)
|
||||
.send();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||
#[tracing::instrument(level = "info", skip_all, err)]
|
||||
pub async fn get_cloud_config_handler(
|
||||
manager: AFPluginState<Weak<UserManager>>,
|
||||
store_preferences: AFPluginState<Weak<StorePreferences>>,
|
||||
) -> DataResult<UserCloudConfigPB, FlowyError> {
|
||||
) -> DataResult<CloudSettingPB, FlowyError> {
|
||||
let manager = upgrade_manager(manager)?;
|
||||
let session = manager.get_session()?;
|
||||
|
||||
let store_preferences = upgrade_store_preferences(store_preferences)?;
|
||||
// Generate the default config if the config is not exist
|
||||
let config = get_or_create_cloud_config(session.user_id, &store_preferences);
|
||||
data_result_ok(config.into())
|
||||
data_result_ok(CloudSettingPB {
|
||||
enable_sync: config.enable_sync,
|
||||
enable_encrypt: config.enable_encrypt,
|
||||
encrypt_secret: config.encrypt_secret,
|
||||
server_url: manager.cloud_services.service_url(),
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "debug", skip(manager), err)]
|
||||
|
@ -119,7 +119,7 @@ pub enum UserEvent {
|
||||
#[event(input = "UpdateCloudConfigPB")]
|
||||
SetCloudConfig = 13,
|
||||
|
||||
#[event(output = "UserCloudConfigPB")]
|
||||
#[event(output = "CloudSettingPB")]
|
||||
GetCloudConfig = 14,
|
||||
|
||||
#[event(input = "UserSecretPB")]
|
||||
@ -248,7 +248,7 @@ pub trait UserCloudServiceProvider: Send + Sync + 'static {
|
||||
fn get_authenticator(&self) -> Authenticator;
|
||||
fn set_device_id(&self, device_id: &str);
|
||||
fn get_user_service(&self) -> Result<Arc<dyn UserCloudService>, FlowyError>;
|
||||
fn service_name(&self) -> String;
|
||||
fn service_url(&self) -> String;
|
||||
}
|
||||
|
||||
impl<T> UserCloudServiceProvider for Arc<T>
|
||||
@ -283,8 +283,8 @@ where
|
||||
(**self).get_user_service()
|
||||
}
|
||||
|
||||
fn service_name(&self) -> String {
|
||||
(**self).service_name()
|
||||
fn service_url(&self) -> String {
|
||||
(**self).service_url()
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user