mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: sign in with provider (#3592)
* chore: sign in with provider * feat: implement oauth flow of appflowy cloud * chore: rename env * chore: fix deadlock * fix: login bugs * chore: clippyt * chore: update client api * chore: fmt
This commit is contained in:
8
frontend/.vscode/tasks.json
vendored
8
frontend/.vscode/tasks.json
vendored
@ -285,6 +285,14 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/appflowy_flutter"
|
"cwd": "${workspaceFolder}/appflowy_flutter"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "AF: Generate AppFlowyEnv",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "dart run build_runner build --delete-conflicting-outputs",
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/appflowy_flutter/packages/appflowy_backend"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,11 @@
|
|||||||
|
|
||||||
# Configuring Cloud Type
|
# Configuring Cloud Type
|
||||||
# This configuration file is used to specify the cloud type and the necessary configurations for each cloud type. The available options are:
|
# This configuration file is used to specify the cloud type and the necessary configurations for each cloud type. The available options are:
|
||||||
# Supabase: Set CLOUD_TYPE to 1
|
# Local: 0
|
||||||
# AppFlowy Cloud: Set CLOUD_TYPE to 2
|
# Supabase: 1
|
||||||
|
# AppFlowy Cloud: 2
|
||||||
|
|
||||||
CLOUD_TYPE=1
|
CLOUD_TYPE=0
|
||||||
|
|
||||||
# Supabase Configuration
|
# Supabase Configuration
|
||||||
# If you're using Supabase (CLOUD_TYPE=1), you need to provide the following configurations:
|
# If you're using Supabase (CLOUD_TYPE=1), you need to provide the following configurations:
|
||||||
@ -18,4 +19,5 @@ SUPABASE_ANON_KEY=replace-with-your-supabase-key
|
|||||||
# AppFlowy Cloud Configuration
|
# AppFlowy Cloud Configuration
|
||||||
# If you're using AppFlowy Cloud (CLOUD_TYPE=2), you need to provide the following configurations:
|
# If you're using AppFlowy Cloud (CLOUD_TYPE=2), you need to provide the following configurations:
|
||||||
APPFLOWY_CLOUD_BASE_URL=replace-with-your-appflowy-cloud-url
|
APPFLOWY_CLOUD_BASE_URL=replace-with-your-appflowy-cloud-url
|
||||||
APPFLOWY_CLOUD_BASE_WS_URL=replace-with-your-appflowy-cloud-ws-url
|
APPFLOWY_CLOUD_WS_BASE_URL=replace-with-your-appflowy-cloud-ws-url
|
||||||
|
APPFLOWY_CLOUD_GOTRUE_URL=replace-with-your-appflowy-cloud-gotrue-url
|
31
frontend/appflowy_flutter/lib/env/env.dart
vendored
31
frontend/appflowy_flutter/lib/env/env.dart
vendored
@ -35,10 +35,17 @@ abstract class Env {
|
|||||||
|
|
||||||
@EnviedField(
|
@EnviedField(
|
||||||
obfuscate: true,
|
obfuscate: true,
|
||||||
varName: 'APPFLOWY_CLOUD_BASE_WS_URL',
|
varName: 'APPFLOWY_CLOUD_WS_BASE_URL',
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
)
|
)
|
||||||
static final String afCloudBaseWSUrl = _Env.afCloudBaseWSUrl;
|
static final String afCloudWSBaseUrl = _Env.afCloudWSBaseUrl;
|
||||||
|
|
||||||
|
@EnviedField(
|
||||||
|
obfuscate: true,
|
||||||
|
varName: 'APPFLOWY_CLOUD_GOTRUE_URL',
|
||||||
|
defaultValue: '',
|
||||||
|
)
|
||||||
|
static final String afCloudGoTrueUrl = _Env.afCloudGoTrueUrl;
|
||||||
|
|
||||||
// Supabase Configuration:
|
// Supabase Configuration:
|
||||||
@EnviedField(
|
@EnviedField(
|
||||||
@ -64,6 +71,24 @@ bool get isCloudEnabled {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool get isSupabaseEnabled {
|
||||||
|
// Only enable supabase in release and develop mode.
|
||||||
|
if (integrationMode().isRelease || integrationMode().isDevelop) {
|
||||||
|
return currentCloudType() == CloudType.supabase;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool get isAppFlowyCloudEnabled {
|
||||||
|
// Only enable appflowy cloud in release and develop mode.
|
||||||
|
if (integrationMode().isRelease || integrationMode().isDevelop) {
|
||||||
|
return currentCloudType() == CloudType.appflowyCloud;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum CloudType {
|
enum CloudType {
|
||||||
unknown,
|
unknown,
|
||||||
supabase,
|
supabase,
|
||||||
@ -84,7 +109,7 @@ CloudType currentCloudType() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value == 2) {
|
if (value == 2) {
|
||||||
if (Env.afCloudBaseUrl.isEmpty || Env.afCloudBaseWSUrl.isEmpty) {
|
if (Env.afCloudBaseUrl.isEmpty || Env.afCloudWSBaseUrl.isEmpty) {
|
||||||
Log.error("AppFlowy cloud is not configured");
|
Log.error("AppFlowy cloud is not configured");
|
||||||
return CloudType.unknown;
|
return CloudType.unknown;
|
||||||
} else {
|
} else {
|
||||||
|
@ -38,7 +38,8 @@ AppFlowyEnv getAppFlowyEnv() {
|
|||||||
|
|
||||||
final appflowyCloudConfig = AppFlowyCloudConfiguration(
|
final appflowyCloudConfig = AppFlowyCloudConfiguration(
|
||||||
base_url: Env.afCloudBaseUrl,
|
base_url: Env.afCloudBaseUrl,
|
||||||
base_ws_url: Env.afCloudBaseWSUrl,
|
ws_base_url: Env.afCloudWSBaseUrl,
|
||||||
|
gotrue_url: Env.afCloudGoTrueUrl,
|
||||||
);
|
);
|
||||||
|
|
||||||
return AppFlowyEnv(
|
return AppFlowyEnv(
|
||||||
|
@ -28,7 +28,7 @@ SupbaseRealtimeService? realtimeService;
|
|||||||
class InitSupabaseTask extends LaunchTask {
|
class InitSupabaseTask extends LaunchTask {
|
||||||
@override
|
@override
|
||||||
Future<void> initialize(LaunchContext context) async {
|
Future<void> initialize(LaunchContext context) async {
|
||||||
if (!isCloudEnabled) {
|
if (!isSupabaseEnabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,13 +1,23 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:app_links/app_links.dart';
|
||||||
import 'package:appflowy/user/application/auth/backend_auth_service.dart';
|
import 'package:appflowy/user/application/auth/backend_auth_service.dart';
|
||||||
import 'package:appflowy/user/application/auth/auth_service.dart';
|
import 'package:appflowy/user/application/auth/auth_service.dart';
|
||||||
import 'package:appflowy/user/application/user_service.dart';
|
import 'package:appflowy/user/application/user_service.dart';
|
||||||
|
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||||
|
import 'package:appflowy_backend/log.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||||
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
import 'package:appflowy_backend/protobuf/flowy-user/protobuf.dart';
|
||||||
import 'package:dartz/dartz.dart';
|
import 'package:dartz/dartz.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
|
import 'auth_error.dart';
|
||||||
|
import 'device_id.dart';
|
||||||
|
|
||||||
class AFCloudAuthService implements AuthService {
|
class AFCloudAuthService implements AuthService {
|
||||||
|
final _appLinks = AppLinks();
|
||||||
|
StreamSubscription<Uri?>? _deeplinkSubscription;
|
||||||
|
|
||||||
AFCloudAuthService();
|
AFCloudAuthService();
|
||||||
|
|
||||||
final BackendAuthService _backendAuthService = BackendAuthService(
|
final BackendAuthService _backendAuthService = BackendAuthService(
|
||||||
@ -38,8 +48,72 @@ class AFCloudAuthService implements AuthService {
|
|||||||
required String platform,
|
required String platform,
|
||||||
Map<String, String> params = const {},
|
Map<String, String> params = const {},
|
||||||
}) async {
|
}) async {
|
||||||
//
|
final provider = ProviderTypePBExtension.fromPlatform(platform);
|
||||||
throw UnimplementedError();
|
|
||||||
|
// Get the oauth url from the backend
|
||||||
|
final result = await UserEventGetOauthURLWithProvider(
|
||||||
|
OauthProviderPB.create()..provider = provider,
|
||||||
|
).send();
|
||||||
|
|
||||||
|
return result.fold(
|
||||||
|
(data) async {
|
||||||
|
// Open the webview with oauth url
|
||||||
|
final uri = Uri.parse(data.oauthUrl);
|
||||||
|
final isSuccess = await launchUrl(
|
||||||
|
uri,
|
||||||
|
mode: LaunchMode.externalApplication,
|
||||||
|
webOnlyWindowName: '_self',
|
||||||
|
);
|
||||||
|
|
||||||
|
final completer = Completer<Either<FlowyError, UserProfilePB>>();
|
||||||
|
_deeplinkSubscription = _appLinks.uriLinkStream.listen(
|
||||||
|
(Uri? uri) async {
|
||||||
|
await _handleUri(uri, completer);
|
||||||
|
},
|
||||||
|
onError: (Object err, StackTrace stackTrace) {
|
||||||
|
Log.error('onDeepLinkError: ${err.toString()}', stackTrace);
|
||||||
|
_deeplinkSubscription?.cancel();
|
||||||
|
completer.complete(left(AuthError.deeplinkError));
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isSuccess) {
|
||||||
|
_deeplinkSubscription?.cancel();
|
||||||
|
completer.complete(left(AuthError.signInWithOauthError));
|
||||||
|
}
|
||||||
|
|
||||||
|
return completer.future;
|
||||||
|
},
|
||||||
|
(r) => left(r),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleUri(
|
||||||
|
Uri? uri,
|
||||||
|
Completer<Either<FlowyError, UserProfilePB>> completer,
|
||||||
|
) async {
|
||||||
|
if (uri != null) {
|
||||||
|
if (_isAuthCallbackDeeplink(uri)) {
|
||||||
|
// Sign in with url
|
||||||
|
final deviceId = await getDeviceId();
|
||||||
|
final payload = OauthSignInPB(
|
||||||
|
authType: AuthTypePB.AFCloud,
|
||||||
|
map: {
|
||||||
|
AuthServiceMapKeys.signInURL: uri.toString(),
|
||||||
|
AuthServiceMapKeys.deviceId: deviceId
|
||||||
|
},
|
||||||
|
);
|
||||||
|
final result = await UserEventOauthSignIn(payload)
|
||||||
|
.send()
|
||||||
|
.then((value) => value.swap());
|
||||||
|
_deeplinkSubscription?.cancel();
|
||||||
|
completer.complete(result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Log.error('onDeepLinkError: Unexpect empty deep link callback');
|
||||||
|
_deeplinkSubscription?.cancel();
|
||||||
|
completer.complete(left(AuthError.emptyDeeplink));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -67,3 +141,22 @@ class AFCloudAuthService implements AuthService {
|
|||||||
return UserBackendService.getCurrentUserProfile();
|
return UserBackendService.getCurrentUserProfile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ProviderTypePBExtension on ProviderTypePB {
|
||||||
|
static ProviderTypePB fromPlatform(String platform) {
|
||||||
|
switch (platform) {
|
||||||
|
case 'github':
|
||||||
|
return ProviderTypePB.Github;
|
||||||
|
case 'google':
|
||||||
|
return ProviderTypePB.Google;
|
||||||
|
case 'discord':
|
||||||
|
return ProviderTypePB.Discord;
|
||||||
|
default:
|
||||||
|
throw UnimplementedError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _isAuthCallbackDeeplink(Uri uri) {
|
||||||
|
return (uri.fragment.contains('access_token'));
|
||||||
|
}
|
||||||
|
@ -17,4 +17,16 @@ class AuthError {
|
|||||||
static final supabaseGetUserError = FlowyError()
|
static final supabaseGetUserError = FlowyError()
|
||||||
..msg = 'unable to get user from supabase -10004'
|
..msg = 'unable to get user from supabase -10004'
|
||||||
..code = ErrorCode.UserUnauthorized;
|
..code = ErrorCode.UserUnauthorized;
|
||||||
|
|
||||||
|
static final signInWithOauthError = FlowyError()
|
||||||
|
..msg = 'sign in with oauth error -10003'
|
||||||
|
..code = ErrorCode.UserUnauthorized;
|
||||||
|
|
||||||
|
static final emptyDeeplink = FlowyError()
|
||||||
|
..msg = 'Unexpected empty deeplink'
|
||||||
|
..code = ErrorCode.UnexpectedEmpty;
|
||||||
|
|
||||||
|
static final deeplinkError = FlowyError()
|
||||||
|
..msg = 'Deeplink error'
|
||||||
|
..code = ErrorCode.Internal;
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ class AuthServiceMapKeys {
|
|||||||
static const String uuid = 'uuid';
|
static const String uuid = 'uuid';
|
||||||
static const String email = 'email';
|
static const String email = 'email';
|
||||||
static const String deviceId = 'device_id';
|
static const String deviceId = 'device_id';
|
||||||
|
static const String signInURL = 'sign_in_url';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `AuthService` is an abstract class that defines methods related to user authentication.
|
/// `AuthService` is an abstract class that defines methods related to user authentication.
|
||||||
|
@ -169,12 +169,12 @@ class SupabaseAuthService implements AuthService {
|
|||||||
Future<Either<FlowyError, UserProfilePB>> _setupAuth({
|
Future<Either<FlowyError, UserProfilePB>> _setupAuth({
|
||||||
required Map<String, String> map,
|
required Map<String, String> map,
|
||||||
}) async {
|
}) async {
|
||||||
final payload = OAuthPB(
|
final payload = OauthSignInPB(
|
||||||
authType: AuthTypePB.Supabase,
|
authType: AuthTypePB.Supabase,
|
||||||
map: map,
|
map: map,
|
||||||
);
|
);
|
||||||
|
|
||||||
return UserEventOAuth(payload).send().then((value) => value.swap());
|
return UserEventOauthSignIn(payload).send().then((value) => value.swap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class MockAuthService implements AuthService {
|
|||||||
final uuid = response.user!.id;
|
final uuid = response.user!.id;
|
||||||
final email = response.user!.email!;
|
final email = response.user!.email!;
|
||||||
|
|
||||||
final payload = OAuthPB(
|
final payload = OauthSignInPB(
|
||||||
authType: AuthTypePB.Supabase,
|
authType: AuthTypePB.Supabase,
|
||||||
map: {
|
map: {
|
||||||
AuthServiceMapKeys.uuid: uuid,
|
AuthServiceMapKeys.uuid: uuid,
|
||||||
@ -65,7 +65,7 @@ class MockAuthService implements AuthService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return UserEventOAuth(payload).send().then((value) => value.swap());
|
return UserEventOauthSignIn(payload).send().then((value) => value.swap());
|
||||||
} on AuthException catch (e) {
|
} on AuthException catch (e) {
|
||||||
Log.error(e);
|
Log.error(e);
|
||||||
return Left(AuthError.supabaseSignInError);
|
return Left(AuthError.supabaseSignInError);
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
|
|
||||||
// Run `dart run build_runner build` to generate the json serialization If the
|
// Run `dart run build_runner build` to generate the json serialization If the
|
||||||
// file `env_serde.i.dart` is existed, delete it first.
|
// file `env_serde.g.dart` is existed, delete it first.
|
||||||
//
|
//
|
||||||
// the file `env_serde.g.dart` will be generated in the same directory. Rename
|
// the file `env_serde.g.dart` will be generated in the same directory.
|
||||||
// the file to `env_serde.i.dart` because the file is ignored by default.
|
|
||||||
part 'env_serde.g.dart';
|
part 'env_serde.g.dart';
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
@ -45,11 +44,13 @@ class SupabaseConfiguration {
|
|||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AppFlowyCloudConfiguration {
|
class AppFlowyCloudConfiguration {
|
||||||
final String base_url;
|
final String base_url;
|
||||||
final String base_ws_url;
|
final String ws_base_url;
|
||||||
|
final String gotrue_url;
|
||||||
|
|
||||||
AppFlowyCloudConfiguration({
|
AppFlowyCloudConfiguration({
|
||||||
required this.base_url,
|
required this.base_url,
|
||||||
required this.base_ws_url,
|
required this.ws_base_url,
|
||||||
|
required this.gotrue_url,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory AppFlowyCloudConfiguration.fromJson(Map<String, dynamic> json) =>
|
factory AppFlowyCloudConfiguration.fromJson(Map<String, dynamic> json) =>
|
||||||
|
@ -26,7 +26,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.7"
|
version: "2.0.7"
|
||||||
app_links:
|
app_links:
|
||||||
dependency: "direct overridden"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
path: "."
|
path: "."
|
||||||
ref: c64ce17
|
ref: c64ce17
|
||||||
|
@ -115,6 +115,7 @@ dependencies:
|
|||||||
# TODO: Consider implementing custom package
|
# TODO: Consider implementing custom package
|
||||||
# to gather notification handling for all platforms
|
# to gather notification handling for all platforms
|
||||||
local_notifier: ^0.1.5
|
local_notifier: ^0.1.5
|
||||||
|
app_links: ^3.4.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_lints: ^2.0.1
|
flutter_lints: ^2.0.1
|
||||||
|
56
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
56
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -762,7 +762,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -775,7 +775,6 @@ dependencies = [
|
|||||||
"gotrue-entity",
|
"gotrue-entity",
|
||||||
"lib0",
|
"lib0",
|
||||||
"mime",
|
"mime",
|
||||||
"opener",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"scraper",
|
"scraper",
|
||||||
@ -853,7 +852,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -872,7 +871,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -902,7 +901,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-define"
|
name = "collab-define"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -916,7 +915,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -928,7 +927,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -948,7 +947,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -979,16 +978,18 @@ dependencies = [
|
|||||||
"collab-persistence",
|
"collab-persistence",
|
||||||
"collab-plugins",
|
"collab-plugins",
|
||||||
"futures",
|
"futures",
|
||||||
|
"lib-infra",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1009,7 +1010,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1036,7 +1037,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1289,7 +1290,7 @@ dependencies = [
|
|||||||
"cssparser-macros",
|
"cssparser-macros",
|
||||||
"dtoa-short",
|
"dtoa-short",
|
||||||
"itoa 1.0.6",
|
"itoa 1.0.6",
|
||||||
"phf 0.11.2",
|
"phf 0.8.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1435,13 +1436,15 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"collab-define",
|
"collab-define",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"thiserror",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
@ -2772,7 +2775,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2782,12 +2785,13 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -3220,7 +3224,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -4261,7 +4265,6 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.11.2",
|
|
||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4353,19 +4356,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"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.29",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -5579,9 +5569,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared_entity"
|
name = "shared_entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"database-entity",
|
||||||
"gotrue-entity",
|
"gotrue-entity",
|
||||||
"opener",
|
"opener",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -6494,7 +6485,9 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6711,6 +6704,7 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
|
"native-tls",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -38,7 +38,7 @@ custom-protocol = ["tauri/custom-protocol"]
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b0c213" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "926da91" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
@ -48,14 +48,14 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b0c
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
58
frontend/rust-lib/Cargo.lock
generated
58
frontend/rust-lib/Cargo.lock
generated
@ -660,7 +660,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "client-api"
|
name = "client-api"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -673,7 +673,6 @@ dependencies = [
|
|||||||
"gotrue-entity",
|
"gotrue-entity",
|
||||||
"lib0",
|
"lib0",
|
||||||
"mime",
|
"mime",
|
||||||
"opener",
|
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"scraper",
|
"scraper",
|
||||||
@ -720,7 +719,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab"
|
name = "collab"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -739,7 +738,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-database"
|
name = "collab-database"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -769,7 +768,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-define"
|
name = "collab-define"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -783,7 +782,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-derive"
|
name = "collab-derive"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -795,7 +794,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-document"
|
name = "collab-document"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -815,7 +814,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-folder"
|
name = "collab-folder"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -846,16 +845,18 @@ dependencies = [
|
|||||||
"collab-persistence",
|
"collab-persistence",
|
||||||
"collab-plugins",
|
"collab-plugins",
|
||||||
"futures",
|
"futures",
|
||||||
|
"lib-infra",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-persistence"
|
name = "collab-persistence"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -876,7 +877,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-plugins"
|
name = "collab-plugins"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -903,7 +904,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-user"
|
name = "collab-user"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=86c5e8#86c5e8891af93de2d035a57214894e854d39662a"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=82975da#82975da119a0c9d0e8b70585966bb89ed6c97cd3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1135,7 +1136,7 @@ dependencies = [
|
|||||||
"cssparser-macros",
|
"cssparser-macros",
|
||||||
"dtoa-short",
|
"dtoa-short",
|
||||||
"itoa",
|
"itoa",
|
||||||
"phf 0.11.2",
|
"phf 0.8.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1262,13 +1263,15 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "database-entity"
|
name = "database-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"collab-define",
|
"collab-define",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"thiserror",
|
||||||
"uuid",
|
"uuid",
|
||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
@ -2434,7 +2437,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue"
|
name = "gotrue"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2444,12 +2447,13 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tracing",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gotrue-entity"
|
name = "gotrue-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -2807,7 +2811,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "infra"
|
name = "infra"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -3570,7 +3574,7 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.8.0",
|
"phf_macros",
|
||||||
"phf_shared 0.8.0",
|
"phf_shared 0.8.0",
|
||||||
"proc-macro-hack",
|
"proc-macro-hack",
|
||||||
]
|
]
|
||||||
@ -3590,7 +3594,6 @@ version = "0.11.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"phf_macros 0.11.2",
|
|
||||||
"phf_shared 0.11.2",
|
"phf_shared 0.11.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -3658,19 +3661,6 @@ dependencies = [
|
|||||||
"syn 1.0.109",
|
"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]]
|
[[package]]
|
||||||
name = "phf_shared"
|
name = "phf_shared"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@ -4821,9 +4811,10 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "shared_entity"
|
name = "shared_entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=b0c213#b0c213b5c0b941e2013b0126e200f98201a82f54"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=926da91#926da912eab806e5b325e12103d341cea536aa2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"database-entity",
|
||||||
"gotrue-entity",
|
"gotrue-entity",
|
||||||
"opener",
|
"opener",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -5432,7 +5423,9 @@ checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"tokio-native-tls",
|
||||||
"tungstenite",
|
"tungstenite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5660,6 +5653,7 @@ dependencies = [
|
|||||||
"http",
|
"http",
|
||||||
"httparse",
|
"httparse",
|
||||||
"log",
|
"log",
|
||||||
|
"native-tls",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -82,7 +82,7 @@ incremental = false
|
|||||||
# Run the script:
|
# Run the script:
|
||||||
# scripts/tool/update_client_api_rev.sh new_rev_id
|
# scripts/tool/update_client_api_rev.sh new_rev_id
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b0c213" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "926da91" }
|
||||||
# Please use the following script to update collab.
|
# Please use the following script to update collab.
|
||||||
# Working directory: frontend
|
# Working directory: frontend
|
||||||
#
|
#
|
||||||
@ -92,11 +92,11 @@ client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "b0c
|
|||||||
# To switch to the local path, run:
|
# To switch to the local path, run:
|
||||||
# scripts/tool/update_collab_source.sh
|
# scripts/tool/update_collab_source.sh
|
||||||
# ⚠️⚠️⚠️️
|
# ⚠️⚠️⚠️️
|
||||||
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-define = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "86c5e8" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "82975da" }
|
||||||
|
@ -20,6 +20,8 @@ tracing = "0.1"
|
|||||||
parking_lot = "0.12.1"
|
parking_lot = "0.12.1"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
async-trait = "0.1.73"
|
async-trait = "0.1.73"
|
||||||
|
tokio = {version = "1.26", features = ["sync"]}
|
||||||
|
lib-infra = { path = "../../../shared-lib/lib-infra" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = []
|
||||||
|
@ -11,8 +11,10 @@ use collab_plugins::cloud_storage::network_state::{CollabNetworkReachability, Co
|
|||||||
use collab_plugins::local_storage::rocksdb::RocksdbDiskPlugin;
|
use collab_plugins::local_storage::rocksdb::RocksdbDiskPlugin;
|
||||||
use collab_plugins::local_storage::CollabPersistenceConfig;
|
use collab_plugins::local_storage::CollabPersistenceConfig;
|
||||||
use collab_plugins::snapshot::{CollabSnapshotPlugin, SnapshotPersistence};
|
use collab_plugins::snapshot::{CollabSnapshotPlugin, SnapshotPersistence};
|
||||||
use futures::executor::block_on;
|
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
use tracing::trace;
|
||||||
|
|
||||||
|
use lib_infra::future::{to_fut, Fut};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum CollabSource {
|
pub enum CollabSource {
|
||||||
@ -36,19 +38,17 @@ pub enum CollabPluginContext {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
pub trait CollabStorageProvider: Send + Sync + 'static {
|
pub trait CollabStorageProvider: Send + Sync + 'static {
|
||||||
fn storage_source(&self) -> CollabSource;
|
fn storage_source(&self) -> CollabSource;
|
||||||
|
|
||||||
async fn get_plugins(
|
fn get_plugins(
|
||||||
&self,
|
&self,
|
||||||
context: CollabPluginContext,
|
context: CollabPluginContext,
|
||||||
) -> Vec<Arc<dyn collab::core::collab_plugin::CollabPlugin>>;
|
) -> Fut<Vec<Arc<dyn collab::core::collab_plugin::CollabPlugin>>>;
|
||||||
|
|
||||||
fn is_sync_enabled(&self) -> bool;
|
fn is_sync_enabled(&self) -> bool;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl<T> CollabStorageProvider for Arc<T>
|
impl<T> CollabStorageProvider for Arc<T>
|
||||||
where
|
where
|
||||||
T: CollabStorageProvider,
|
T: CollabStorageProvider,
|
||||||
@ -57,8 +57,8 @@ where
|
|||||||
(**self).storage_source()
|
(**self).storage_source()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_plugins(&self, context: CollabPluginContext) -> Vec<Arc<dyn CollabPlugin>> {
|
fn get_plugins(&self, context: CollabPluginContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
|
||||||
(**self).get_plugins(context).await
|
(**self).get_plugins(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sync_enabled(&self) -> bool {
|
fn is_sync_enabled(&self) -> bool {
|
||||||
@ -69,7 +69,7 @@ where
|
|||||||
pub struct AppFlowyCollabBuilder {
|
pub struct AppFlowyCollabBuilder {
|
||||||
network_reachability: CollabNetworkReachability,
|
network_reachability: CollabNetworkReachability,
|
||||||
workspace_id: RwLock<Option<String>>,
|
workspace_id: RwLock<Option<String>>,
|
||||||
cloud_storage: RwLock<Arc<dyn CollabStorageProvider>>,
|
cloud_storage: tokio::sync::RwLock<Arc<dyn CollabStorageProvider>>,
|
||||||
snapshot_persistence: Mutex<Option<Arc<dyn SnapshotPersistence>>>,
|
snapshot_persistence: Mutex<Option<Arc<dyn SnapshotPersistence>>>,
|
||||||
device_id: Mutex<String>,
|
device_id: Mutex<String>,
|
||||||
}
|
}
|
||||||
@ -79,7 +79,7 @@ impl AppFlowyCollabBuilder {
|
|||||||
Self {
|
Self {
|
||||||
network_reachability: CollabNetworkReachability::new(),
|
network_reachability: CollabNetworkReachability::new(),
|
||||||
workspace_id: Default::default(),
|
workspace_id: Default::default(),
|
||||||
cloud_storage: RwLock::new(Arc::new(storage_provider)),
|
cloud_storage: tokio::sync::RwLock::new(Arc::new(storage_provider)),
|
||||||
snapshot_persistence: Default::default(),
|
snapshot_persistence: Default::default(),
|
||||||
device_id: Default::default(),
|
device_id: Default::default(),
|
||||||
}
|
}
|
||||||
@ -141,7 +141,7 @@ impl AppFlowyCollabBuilder {
|
|||||||
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
||||||
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
||||||
///
|
///
|
||||||
pub fn build(
|
pub async fn build(
|
||||||
&self,
|
&self,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
object_id: &str,
|
object_id: &str,
|
||||||
@ -149,14 +149,16 @@ impl AppFlowyCollabBuilder {
|
|||||||
raw_data: CollabRawData,
|
raw_data: CollabRawData,
|
||||||
collab_db: Weak<RocksCollabDB>,
|
collab_db: Weak<RocksCollabDB>,
|
||||||
) -> Result<Arc<MutexCollab>, Error> {
|
) -> Result<Arc<MutexCollab>, Error> {
|
||||||
self.build_with_config(
|
self
|
||||||
uid,
|
.build_with_config(
|
||||||
object_id,
|
uid,
|
||||||
object_type,
|
object_id,
|
||||||
collab_db,
|
object_type,
|
||||||
raw_data,
|
collab_db,
|
||||||
&CollabPersistenceConfig::default(),
|
raw_data,
|
||||||
)
|
&CollabPersistenceConfig::default(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new collaboration builder with the custom configuration.
|
/// Creates a new collaboration builder with the custom configuration.
|
||||||
@ -173,7 +175,7 @@ impl AppFlowyCollabBuilder {
|
|||||||
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
/// - `raw_data`: The raw data of the collaboration object, defined by the [CollabRawData] type.
|
||||||
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
/// - `collab_db`: A weak reference to the [RocksCollabDB].
|
||||||
///
|
///
|
||||||
pub fn build_with_config(
|
pub async fn build_with_config(
|
||||||
&self,
|
&self,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
object_id: &str,
|
object_id: &str,
|
||||||
@ -194,21 +196,26 @@ impl AppFlowyCollabBuilder {
|
|||||||
.build()?,
|
.build()?,
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
let cloud_storage = self.cloud_storage.read();
|
let cloud_storage_type = self.cloud_storage.read().await.storage_source();
|
||||||
let cloud_storage_type = cloud_storage.storage_source();
|
|
||||||
let collab_object = self.collab_object(uid, object_id, object_type)?;
|
let collab_object = self.collab_object(uid, object_id, object_type)?;
|
||||||
match cloud_storage_type {
|
match cloud_storage_type {
|
||||||
CollabSource::AFCloud => {
|
CollabSource::AFCloud => {
|
||||||
#[cfg(feature = "appflowy_cloud_integrate")]
|
#[cfg(feature = "appflowy_cloud_integrate")]
|
||||||
{
|
{
|
||||||
|
trace!("init appflowy cloud collab plugins");
|
||||||
let local_collab = Arc::downgrade(&collab);
|
let local_collab = Arc::downgrade(&collab);
|
||||||
let plugins = block_on(
|
let plugins = self
|
||||||
cloud_storage.get_plugins(CollabPluginContext::AppFlowyCloud {
|
.cloud_storage
|
||||||
|
.read()
|
||||||
|
.await
|
||||||
|
.get_plugins(CollabPluginContext::AppFlowyCloud {
|
||||||
uid,
|
uid,
|
||||||
collab_object: collab_object.clone(),
|
collab_object: collab_object.clone(),
|
||||||
local_collab,
|
local_collab,
|
||||||
}),
|
})
|
||||||
);
|
.await;
|
||||||
|
|
||||||
|
trace!("add appflowy cloud collab plugins: {}", plugins.len());
|
||||||
for plugin in plugins {
|
for plugin in plugins {
|
||||||
collab.lock().add_plugin(plugin);
|
collab.lock().add_plugin(plugin);
|
||||||
}
|
}
|
||||||
@ -217,14 +224,20 @@ impl AppFlowyCollabBuilder {
|
|||||||
CollabSource::Supabase => {
|
CollabSource::Supabase => {
|
||||||
#[cfg(feature = "supabase_integrate")]
|
#[cfg(feature = "supabase_integrate")]
|
||||||
{
|
{
|
||||||
|
trace!("init supabase collab plugins");
|
||||||
let local_collab = Arc::downgrade(&collab);
|
let local_collab = Arc::downgrade(&collab);
|
||||||
let local_collab_db = collab_db.clone();
|
let local_collab_db = collab_db.clone();
|
||||||
let plugins = block_on(cloud_storage.get_plugins(CollabPluginContext::Supabase {
|
let plugins = self
|
||||||
uid,
|
.cloud_storage
|
||||||
collab_object: collab_object.clone(),
|
.read()
|
||||||
local_collab,
|
.await
|
||||||
local_collab_db,
|
.get_plugins(CollabPluginContext::Supabase {
|
||||||
}));
|
uid,
|
||||||
|
collab_object: collab_object.clone(),
|
||||||
|
local_collab,
|
||||||
|
local_collab_db,
|
||||||
|
})
|
||||||
|
.await;
|
||||||
for plugin in plugins {
|
for plugin in plugins {
|
||||||
collab.lock().add_plugin(plugin);
|
collab.lock().add_plugin(plugin);
|
||||||
}
|
}
|
||||||
@ -248,7 +261,7 @@ impl AppFlowyCollabBuilder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
block_on(collab.async_initialize());
|
collab.lock().initialize();
|
||||||
Ok(collab)
|
Ok(collab)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,8 +274,8 @@ impl CollabStorageProvider for DefaultCollabStorageProvider {
|
|||||||
CollabSource::Local
|
CollabSource::Local
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_plugins(&self, _context: CollabPluginContext) -> Vec<Arc<dyn CollabPlugin>> {
|
fn get_plugins(&self, _context: CollabPluginContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
|
||||||
vec![]
|
to_fut(async move { vec![] })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sync_enabled(&self) -> bool {
|
fn is_sync_enabled(&self) -> bool {
|
||||||
|
@ -20,8 +20,7 @@ use flowy_storage::{FileStorageService, StorageObject};
|
|||||||
use flowy_user::event_map::UserCloudServiceProvider;
|
use flowy_user::event_map::UserCloudServiceProvider;
|
||||||
use flowy_user_deps::cloud::UserCloudService;
|
use flowy_user_deps::cloud::UserCloudService;
|
||||||
use flowy_user_deps::entities::AuthType;
|
use flowy_user_deps::entities::AuthType;
|
||||||
use lib_infra::async_trait::async_trait;
|
use lib_infra::future::{to_fut, Fut, FutureResult};
|
||||||
use lib_infra::future::FutureResult;
|
|
||||||
|
|
||||||
use crate::integrate::server::{ServerProvider, ServerType, SERVER_PROVIDER_TYPE_KEY};
|
use crate::integrate::server::{ServerProvider, ServerType, SERVER_PROVIDER_TYPE_KEY};
|
||||||
|
|
||||||
@ -258,48 +257,53 @@ impl DocumentCloudService for ServerProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl CollabStorageProvider for ServerProvider {
|
impl CollabStorageProvider for ServerProvider {
|
||||||
fn storage_source(&self) -> CollabSource {
|
fn storage_source(&self) -> CollabSource {
|
||||||
self.get_server_type().into()
|
self.get_server_type().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_plugins(&self, context: CollabPluginContext) -> Vec<Arc<dyn CollabPlugin>> {
|
fn get_plugins(&self, context: CollabPluginContext) -> Fut<Vec<Arc<dyn CollabPlugin>>> {
|
||||||
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
|
|
||||||
match context {
|
match context {
|
||||||
CollabPluginContext::Local => {},
|
CollabPluginContext::Local => to_fut(async move { vec![] }),
|
||||||
CollabPluginContext::AppFlowyCloud {
|
CollabPluginContext::AppFlowyCloud {
|
||||||
uid: _,
|
uid: _,
|
||||||
collab_object,
|
collab_object,
|
||||||
local_collab,
|
local_collab,
|
||||||
} => {
|
} => {
|
||||||
if let Ok(server) = self.get_server(&ServerType::AFCloud) {
|
if let Ok(server) = self.get_server(&ServerType::AFCloud) {
|
||||||
match server.collab_ws_channel(&collab_object.object_id).await {
|
to_fut(async move {
|
||||||
Ok(Some((channel, ws_connect_state))) => {
|
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
|
||||||
let origin = CollabOrigin::Client(CollabClient::new(
|
match server.collab_ws_channel(&collab_object.object_id).await {
|
||||||
collab_object.uid,
|
Ok(Some((channel, ws_connect_state))) => {
|
||||||
collab_object.device_id.clone(),
|
let origin = CollabOrigin::Client(CollabClient::new(
|
||||||
));
|
collab_object.uid,
|
||||||
let sync_object = SyncObject::from(collab_object);
|
collab_object.device_id.clone(),
|
||||||
let (sink, stream) = (channel.sink(), channel.stream());
|
));
|
||||||
let sink_config = SinkConfig::new().with_timeout(6);
|
let sync_object = SyncObject::from(collab_object);
|
||||||
let sync_plugin = SyncPlugin::new(
|
let (sink, stream) = (channel.sink(), channel.stream());
|
||||||
origin,
|
let sink_config = SinkConfig::new().with_timeout(6);
|
||||||
sync_object,
|
let sync_plugin = SyncPlugin::new(
|
||||||
local_collab,
|
origin,
|
||||||
sink,
|
sync_object,
|
||||||
sink_config,
|
local_collab,
|
||||||
stream,
|
sink,
|
||||||
Some(channel),
|
sink_config,
|
||||||
ws_connect_state,
|
stream,
|
||||||
);
|
Some(channel),
|
||||||
plugins.push(Arc::new(sync_plugin));
|
ws_connect_state,
|
||||||
},
|
);
|
||||||
Ok(None) => {
|
plugins.push(Arc::new(sync_plugin));
|
||||||
tracing::error!("🔴Failed to get collab ws channel: channel is none");
|
},
|
||||||
},
|
Ok(None) => {
|
||||||
Err(err) => tracing::error!("🔴Failed to get collab ws channel: {:?}", err),
|
tracing::error!("🔴Failed to get collab ws channel: channel is none");
|
||||||
}
|
},
|
||||||
|
Err(err) => tracing::error!("🔴Failed to get collab ws channel: {:?}", err),
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
to_fut(async move { vec![] })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CollabPluginContext::Supabase {
|
CollabPluginContext::Supabase {
|
||||||
@ -308,6 +312,7 @@ impl CollabStorageProvider for ServerProvider {
|
|||||||
local_collab,
|
local_collab,
|
||||||
local_collab_db,
|
local_collab_db,
|
||||||
} => {
|
} => {
|
||||||
|
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
|
||||||
if let Some(remote_collab_storage) = self
|
if let Some(remote_collab_storage) = self
|
||||||
.get_server(&ServerType::Supabase)
|
.get_server(&ServerType::Supabase)
|
||||||
.ok()
|
.ok()
|
||||||
@ -322,9 +327,10 @@ impl CollabStorageProvider for ServerProvider {
|
|||||||
local_collab_db,
|
local_collab_db,
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
to_fut(async move { plugins })
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
plugins
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_sync_enabled(&self) -> bool {
|
fn is_sync_enabled(&self) -> bool {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use flowy_database2::DatabaseManager;
|
use flowy_database2::DatabaseManager;
|
||||||
use flowy_document2::manager::DocumentManager;
|
use flowy_document2::manager::DocumentManager;
|
||||||
@ -130,18 +132,22 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
},
|
},
|
||||||
&user_workspace.id,
|
&user_workspace.id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
|
.context("FolderManager error")?;
|
||||||
|
|
||||||
database_manager
|
database_manager
|
||||||
.initialize_with_new_user(
|
.initialize_with_new_user(
|
||||||
user_profile.uid,
|
user_profile.uid,
|
||||||
user_workspace.id.clone(),
|
user_workspace.id.clone(),
|
||||||
user_workspace.database_views_aggregate_id,
|
user_workspace.database_views_aggregate_id,
|
||||||
)
|
)
|
||||||
.await?;
|
.await
|
||||||
|
.context("DatabaseManager error")?;
|
||||||
|
|
||||||
document_manager
|
document_manager
|
||||||
.initialize_with_new_user(user_profile.uid, user_workspace.id)
|
.initialize_with_new_user(user_profile.uid, user_workspace.id)
|
||||||
.await?;
|
.await
|
||||||
|
.context("DocumentManager error")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,9 @@ use collab_database::user::{
|
|||||||
};
|
};
|
||||||
use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLayout};
|
use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLayout};
|
||||||
use collab_define::CollabType;
|
use collab_define::CollabType;
|
||||||
|
use futures::executor::block_on;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
|
use tracing::{instrument, trace};
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||||
@ -87,7 +89,7 @@ impl DatabaseManager {
|
|||||||
|
|
||||||
// If the workspace database not exist in disk, try to fetch from remote.
|
// If the workspace database not exist in disk, try to fetch from remote.
|
||||||
if !self.is_collab_exist(uid, &collab_db, &database_views_aggregate_id) {
|
if !self.is_collab_exist(uid, &collab_db, &database_views_aggregate_id) {
|
||||||
tracing::trace!("workspace database not exist, try to fetch from remote");
|
trace!("workspace database not exist, try to fetch from remote");
|
||||||
match self
|
match self
|
||||||
.cloud_service
|
.cloud_service
|
||||||
.get_collab_update(&database_views_aggregate_id, CollabType::WorkspaceDatabase)
|
.get_collab_update(&database_views_aggregate_id, CollabType::WorkspaceDatabase)
|
||||||
@ -106,7 +108,7 @@ impl DatabaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct the workspace database.
|
// Construct the workspace database.
|
||||||
tracing::trace!("open workspace database: {}", &database_views_aggregate_id);
|
trace!("open workspace database: {}", &database_views_aggregate_id);
|
||||||
let collab = collab_builder.build_collab_with_config(
|
let collab = collab_builder.build_collab_with_config(
|
||||||
uid,
|
uid,
|
||||||
&database_views_aggregate_id,
|
&database_views_aggregate_id,
|
||||||
@ -125,6 +127,7 @@ impl DatabaseManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn initialize_with_new_user(
|
pub async fn initialize_with_new_user(
|
||||||
&self,
|
&self,
|
||||||
user_id: i64,
|
user_id: i64,
|
||||||
@ -170,7 +173,7 @@ impl DatabaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn open_database(&self, database_id: &str) -> FlowyResult<Arc<DatabaseEditor>> {
|
pub async fn open_database(&self, database_id: &str) -> FlowyResult<Arc<DatabaseEditor>> {
|
||||||
tracing::trace!("create new editor for database {}", database_id);
|
trace!("create new editor for database {}", database_id);
|
||||||
let mut editors = self.editors.write().await;
|
let mut editors = self.editors.write().await;
|
||||||
|
|
||||||
let wdb = self.get_workspace_database().await?;
|
let wdb = self.get_workspace_database().await?;
|
||||||
@ -353,7 +356,7 @@ fn subscribe_block_event(workspace_database: &WorkspaceDatabase) {
|
|||||||
match event {
|
match event {
|
||||||
BlockEvent::DidFetchRow(row_details) => {
|
BlockEvent::DidFetchRow(row_details) => {
|
||||||
for row_detail in row_details {
|
for row_detail in row_details {
|
||||||
tracing::trace!("Did fetch row: {:?}", row_detail.row.id);
|
trace!("Did fetch row: {:?}", row_detail.row.id);
|
||||||
let row_id = row_detail.row.id.clone();
|
let row_id = row_detail.row.id.clone();
|
||||||
let pb = DidFetchRowPB::from(row_detail);
|
let pb = DidFetchRowPB::from(row_detail);
|
||||||
send_notification(&row_id, DatabaseNotification::DidFetchRow)
|
send_notification(&row_id, DatabaseNotification::DidFetchRow)
|
||||||
@ -426,16 +429,14 @@ impl DatabaseCollabService for UserDatabaseCollabServiceImpl {
|
|||||||
collab_raw_data: CollabRawData,
|
collab_raw_data: CollabRawData,
|
||||||
config: &CollabPersistenceConfig,
|
config: &CollabPersistenceConfig,
|
||||||
) -> Arc<MutexCollab> {
|
) -> Arc<MutexCollab> {
|
||||||
self
|
block_on(self.collab_builder.build_with_config(
|
||||||
.collab_builder
|
uid,
|
||||||
.build_with_config(
|
object_id,
|
||||||
uid,
|
object_type,
|
||||||
object_id,
|
collab_db,
|
||||||
object_type,
|
collab_raw_data,
|
||||||
collab_db,
|
config,
|
||||||
collab_raw_data,
|
))
|
||||||
config,
|
.unwrap()
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@ use collab_document::document::Document;
|
|||||||
use collab_document::document_data::default_document_data;
|
use collab_document::document_data::default_document_data;
|
||||||
use collab_document::YrsDocAction;
|
use collab_document::YrsDocAction;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use collab_integrate::RocksCollabDB;
|
use collab_integrate::RocksCollabDB;
|
||||||
@ -55,6 +56,7 @@ impl DocumentManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn initialize_with_new_user(&self, uid: i64, workspace_id: String) -> FlowyResult<()> {
|
pub async fn initialize_with_new_user(&self, uid: i64, workspace_id: String) -> FlowyResult<()> {
|
||||||
self.initialize(uid, workspace_id).await?;
|
self.initialize(uid, workspace_id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -175,8 +177,22 @@ impl DocumentManager {
|
|||||||
let db = self.user.collab_db(uid)?;
|
let db = self.user.collab_db(uid)?;
|
||||||
let collab = self
|
let collab = self
|
||||||
.collab_builder
|
.collab_builder
|
||||||
.build(uid, doc_id, CollabType::Document, updates, db)?;
|
.build(uid, doc_id, CollabType::Document, updates, db)
|
||||||
|
.await?;
|
||||||
Ok(collab)
|
Ok(collab)
|
||||||
|
|
||||||
|
// let doc_id = doc_id.to_string();
|
||||||
|
// let (tx, rx) = oneshot::channel();
|
||||||
|
// let collab_builder = self.collab_builder.clone();
|
||||||
|
// tokio::spawn(async move {
|
||||||
|
// let collab = collab_builder
|
||||||
|
// .build(uid, &doc_id, CollabType::Document, updates, db)
|
||||||
|
// .await
|
||||||
|
// .unwrap();
|
||||||
|
// let _ = tx.send(collab);
|
||||||
|
// });
|
||||||
|
//
|
||||||
|
// Ok(rx.await.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_doc_exist(&self, doc_id: &str) -> FlowyResult<bool> {
|
fn is_doc_exist(&self, doc_id: &str) -> FlowyResult<bool> {
|
||||||
|
@ -253,6 +253,9 @@ pub enum ErrorCode {
|
|||||||
|
|
||||||
#[error("Permission denied")]
|
#[error("Permission denied")]
|
||||||
NotEnoughPermissions = 83,
|
NotEnoughPermissions = 83,
|
||||||
|
|
||||||
|
#[error("Internal server error")]
|
||||||
|
InternalServerError = 84,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ErrorCode {
|
impl ErrorCode {
|
||||||
|
@ -99,6 +99,7 @@ impl FlowyError {
|
|||||||
ErrorCode::UnexpectedCalendarFieldType
|
ErrorCode::UnexpectedCalendarFieldType
|
||||||
);
|
);
|
||||||
static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync);
|
static_flowy_error!(collab_not_sync, ErrorCode::CollabDataNotSync);
|
||||||
|
static_flowy_error!(server_error, ErrorCode::InternalServerError);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::convert::From<ErrorCode> for FlowyError {
|
impl std::convert::From<ErrorCode> for FlowyError {
|
||||||
|
@ -8,12 +8,12 @@ impl From<AppError> for FlowyError {
|
|||||||
client_api::error::ErrorCode::Ok => ErrorCode::Internal,
|
client_api::error::ErrorCode::Ok => ErrorCode::Internal,
|
||||||
client_api::error::ErrorCode::Unhandled => ErrorCode::Internal,
|
client_api::error::ErrorCode::Unhandled => ErrorCode::Internal,
|
||||||
client_api::error::ErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
client_api::error::ErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
||||||
|
client_api::error::ErrorCode::FileNotFound => ErrorCode::RecordNotFound,
|
||||||
client_api::error::ErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
|
client_api::error::ErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
|
||||||
client_api::error::ErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
|
client_api::error::ErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
|
||||||
client_api::error::ErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
|
client_api::error::ErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
|
||||||
client_api::error::ErrorCode::OAuthError => ErrorCode::UserUnauthorized,
|
client_api::error::ErrorCode::OAuthError => ErrorCode::UserUnauthorized,
|
||||||
client_api::error::ErrorCode::MissingPayload => ErrorCode::MissingPayload,
|
client_api::error::ErrorCode::MissingPayload => ErrorCode::MissingPayload,
|
||||||
client_api::error::ErrorCode::StorageError => ErrorCode::Internal,
|
|
||||||
client_api::error::ErrorCode::OpenError => ErrorCode::Internal,
|
client_api::error::ErrorCode::OpenError => ErrorCode::Internal,
|
||||||
client_api::error::ErrorCode::InvalidUrl => ErrorCode::InvalidURL,
|
client_api::error::ErrorCode::InvalidUrl => ErrorCode::InvalidURL,
|
||||||
client_api::error::ErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
|
client_api::error::ErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
|
||||||
|
@ -12,7 +12,7 @@ use collab_folder::core::{
|
|||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
use tokio_stream::wrappers::WatchStream;
|
use tokio_stream::wrappers::WatchStream;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tracing::{event, Level};
|
use tracing::{event, info, instrument, Level};
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB, YrsDocAction};
|
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB, YrsDocAction};
|
||||||
@ -159,13 +159,17 @@ impl FolderManager {
|
|||||||
} => {
|
} => {
|
||||||
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
||||||
if is_exist {
|
if is_exist {
|
||||||
let collab = self.collab_for_folder(uid, &workspace_id, collab_db, vec![])?;
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
Folder::open(collab, Some(folder_notifier))
|
Folder::open(collab, Some(folder_notifier))
|
||||||
} else if create_if_not_exist {
|
} else if create_if_not_exist {
|
||||||
let folder_data =
|
let folder_data =
|
||||||
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers)
|
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers)
|
||||||
.await;
|
.await;
|
||||||
let collab = self.collab_for_folder(uid, &workspace_id, collab_db, vec![])?;
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
Folder::create(collab, Some(folder_notifier), Some(folder_data))
|
Folder::create(collab, Some(folder_notifier), Some(folder_data))
|
||||||
} else {
|
} else {
|
||||||
return Err(FlowyError::new(
|
return Err(FlowyError::new(
|
||||||
@ -178,11 +182,15 @@ impl FolderManager {
|
|||||||
if raw_data.is_empty() {
|
if raw_data.is_empty() {
|
||||||
return Err(workspace_data_not_sync_error(uid, &workspace_id));
|
return Err(workspace_data_not_sync_error(uid, &workspace_id));
|
||||||
}
|
}
|
||||||
let collab = self.collab_for_folder(uid, &workspace_id, collab_db, raw_data)?;
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, raw_data)
|
||||||
|
.await?;
|
||||||
Folder::open(collab, Some(folder_notifier))
|
Folder::open(collab, Some(folder_notifier))
|
||||||
},
|
},
|
||||||
FolderInitializeDataSource::FolderData(folder_data) => {
|
FolderInitializeDataSource::FolderData(folder_data) => {
|
||||||
let collab = self.collab_for_folder(uid, &workspace_id, collab_db, vec![])?;
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
Folder::create(collab, Some(folder_notifier), Some(folder_data))
|
Folder::create(collab, Some(folder_notifier), Some(folder_data))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -205,21 +213,24 @@ impl FolderManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collab_for_folder(
|
async fn collab_for_folder(
|
||||||
&self,
|
&self,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
collab_db: Weak<RocksCollabDB>,
|
collab_db: Weak<RocksCollabDB>,
|
||||||
raw_data: CollabRawData,
|
raw_data: CollabRawData,
|
||||||
) -> Result<Arc<MutexCollab>, FlowyError> {
|
) -> Result<Arc<MutexCollab>, FlowyError> {
|
||||||
let collab = self.collab_builder.build_with_config(
|
let collab = self
|
||||||
uid,
|
.collab_builder
|
||||||
workspace_id,
|
.build_with_config(
|
||||||
CollabType::Folder,
|
uid,
|
||||||
collab_db,
|
workspace_id,
|
||||||
raw_data,
|
CollabType::Folder,
|
||||||
&CollabPersistenceConfig::new().enable_snapshot(true),
|
collab_db,
|
||||||
)?;
|
raw_data,
|
||||||
|
&CollabPersistenceConfig::new().enable_snapshot(true),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
Ok(collab)
|
Ok(collab)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,7 +247,7 @@ impl FolderManager {
|
|||||||
.get_folder_updates(workspace_id, user_id)
|
.get_folder_updates(workspace_id, user_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
tracing::info!(
|
info!(
|
||||||
"Get folder updates via {}, number of updates: {}",
|
"Get folder updates via {}, number of updates: {}",
|
||||||
self.cloud_service.service_name(),
|
self.cloud_service.service_name(),
|
||||||
folder_updates.len()
|
folder_updates.len()
|
||||||
@ -254,6 +265,7 @@ impl FolderManager {
|
|||||||
|
|
||||||
/// Initialize the folder for the new user.
|
/// Initialize the folder for the new user.
|
||||||
/// Using the [DefaultFolderBuilder] to create the default workspace for the new user.
|
/// Using the [DefaultFolderBuilder] to create the default workspace for the new user.
|
||||||
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn initialize_with_new_user(
|
pub async fn initialize_with_new_user(
|
||||||
&self,
|
&self,
|
||||||
user_id: i64,
|
user_id: i64,
|
||||||
@ -263,29 +275,41 @@ impl FolderManager {
|
|||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
// Create the default workspace if the user is new
|
// Create the default workspace if the user is new
|
||||||
tracing::info!("initialize_when_sign_up: is_new: {}", is_new);
|
info!("initialize_when_sign_up: is_new: {}", is_new);
|
||||||
if is_new {
|
if is_new {
|
||||||
self.initialize(user_id, workspace_id, data_source).await?;
|
self.initialize(user_id, workspace_id, data_source).await?;
|
||||||
} else {
|
} else {
|
||||||
// The folder updates should not be empty, as the folder data is stored
|
// The folder updates should not be empty, as the folder data is stored
|
||||||
// when the user signs up for the first time.
|
// when the user signs up for the first time.
|
||||||
let folder_updates = self
|
let result = self
|
||||||
.cloud_service
|
.cloud_service
|
||||||
.get_folder_updates(workspace_id, user_id)
|
.get_folder_updates(workspace_id, user_id)
|
||||||
.await?;
|
.await
|
||||||
|
.map_err(FlowyError::from);
|
||||||
|
|
||||||
tracing::info!(
|
match result {
|
||||||
"Get folder updates via {}, number of updates: {}",
|
Ok(folder_updates) => {
|
||||||
self.cloud_service.service_name(),
|
info!(
|
||||||
folder_updates.len()
|
"Get folder updates via {}, number of updates: {}",
|
||||||
);
|
self.cloud_service.service_name(),
|
||||||
self
|
folder_updates.len()
|
||||||
.initialize(
|
);
|
||||||
user_id,
|
self
|
||||||
workspace_id,
|
.initialize(
|
||||||
FolderInitializeDataSource::Cloud(folder_updates),
|
user_id,
|
||||||
)
|
workspace_id,
|
||||||
.await?;
|
FolderInitializeDataSource::Cloud(folder_updates),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
if err.is_record_not_found() {
|
||||||
|
self.initialize(user_id, workspace_id, data_source).await?;
|
||||||
|
} else {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2,39 +2,47 @@ use serde::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use flowy_error::{ErrorCode, FlowyError};
|
use flowy_error::{ErrorCode, FlowyError};
|
||||||
|
|
||||||
pub const AF_CLOUD_BASE_URL: &str = "AF_CLOUD_BASE_URL";
|
pub const APPFLOWY_CLOUD_BASE_URL: &str = "APPFLOWY_CLOUD_BASE_URL";
|
||||||
pub const AF_CLOUD_WS_BASE_URL: &str = "AF_CLOUD_WS_BASE_URL";
|
pub const APPFLOWY_CLOUD_WS_BASE_URL: &str = "APPFLOWY_CLOUD_WS_BASE_URL";
|
||||||
pub const AF_CLOUD_GOTRUE_URL: &str = "AF_GOTRUE_URL";
|
pub const APPFLOWY_CLOUD_GOTRUE_URL: &str = "APPFLOWY_CLOUD_GOTRUE_URL";
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||||
pub struct AFCloudConfiguration {
|
pub struct AFCloudConfiguration {
|
||||||
pub base_url: String,
|
pub base_url: String,
|
||||||
pub base_ws_url: String,
|
pub ws_base_url: String,
|
||||||
pub gotrue_url: String,
|
pub gotrue_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AFCloudConfiguration {
|
impl AFCloudConfiguration {
|
||||||
pub fn from_env() -> Result<Self, FlowyError> {
|
pub fn from_env() -> Result<Self, FlowyError> {
|
||||||
let base_url = std::env::var(AF_CLOUD_BASE_URL)
|
let base_url = std::env::var(APPFLOWY_CLOUD_BASE_URL).map_err(|_| {
|
||||||
.map_err(|_| FlowyError::new(ErrorCode::InvalidAuthConfig, "Missing AF_CLOUD_BASE_URL"))?;
|
FlowyError::new(
|
||||||
|
ErrorCode::InvalidAuthConfig,
|
||||||
|
"Missing APPFLOWY_CLOUD_BASE_URL",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let base_ws_url = std::env::var(AF_CLOUD_WS_BASE_URL)
|
let ws_base_url = std::env::var(APPFLOWY_CLOUD_WS_BASE_URL).map_err(|_| {
|
||||||
.map_err(|_| FlowyError::new(ErrorCode::InvalidAuthConfig, "Missing AF_CLOUD_WS_BASE_URL"))?;
|
FlowyError::new(
|
||||||
|
ErrorCode::InvalidAuthConfig,
|
||||||
|
"Missing APPFLOWY_CLOUD_WS_BASE_URL",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
let gotrue_url = std::env::var(AF_CLOUD_GOTRUE_URL)
|
let gotrue_url = std::env::var(APPFLOWY_CLOUD_GOTRUE_URL)
|
||||||
.map_err(|_| FlowyError::new(ErrorCode::InvalidAuthConfig, "Missing AF_CLOUD_GOTRUE_URL"))?;
|
.map_err(|_| FlowyError::new(ErrorCode::InvalidAuthConfig, "Missing AF_CLOUD_GOTRUE_URL"))?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
base_url,
|
base_url,
|
||||||
base_ws_url,
|
ws_base_url,
|
||||||
gotrue_url,
|
gotrue_url,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the configuration to the environment variables.
|
/// Write the configuration to the environment variables.
|
||||||
pub fn write_env(&self) {
|
pub fn write_env(&self) {
|
||||||
std::env::set_var(AF_CLOUD_BASE_URL, &self.base_url);
|
std::env::set_var(APPFLOWY_CLOUD_BASE_URL, &self.base_url);
|
||||||
std::env::set_var(AF_CLOUD_WS_BASE_URL, &self.base_ws_url);
|
std::env::set_var(APPFLOWY_CLOUD_WS_BASE_URL, &self.ws_base_url);
|
||||||
std::env::set_var(AF_CLOUD_GOTRUE_URL, &self.gotrue_url);
|
std::env::set_var(APPFLOWY_CLOUD_GOTRUE_URL, &self.gotrue_url);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ use collab_define::CollabType;
|
|||||||
use collab_document::document::Document;
|
use collab_document::document::Document;
|
||||||
|
|
||||||
use flowy_document_deps::cloud::*;
|
use flowy_document_deps::cloud::*;
|
||||||
|
use flowy_error::FlowyError;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
use crate::af_cloud::AFServer;
|
use crate::af_cloud::AFServer;
|
||||||
@ -23,7 +24,10 @@ where
|
|||||||
object_id: document_id.to_string(),
|
object_id: document_id.to_string(),
|
||||||
collab_type: CollabType::Document,
|
collab_type: CollabType::Document,
|
||||||
};
|
};
|
||||||
let data = try_get_client?.get_collab(params).await?;
|
let data = try_get_client?
|
||||||
|
.get_collab(params)
|
||||||
|
.await
|
||||||
|
.map_err(FlowyError::from)?;
|
||||||
Ok(vec![data])
|
Ok(vec![data])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -44,7 +48,10 @@ where
|
|||||||
object_id: document_id.clone(),
|
object_id: document_id.clone(),
|
||||||
collab_type: CollabType::Document,
|
collab_type: CollabType::Document,
|
||||||
};
|
};
|
||||||
let updates = vec![try_get_client?.get_collab(params).await?];
|
let updates = vec![try_get_client?
|
||||||
|
.get_collab(params)
|
||||||
|
.await
|
||||||
|
.map_err(FlowyError::from)?];
|
||||||
let document = Document::from_updates(CollabOrigin::Empty, updates, &document_id, vec![])?;
|
let document = Document::from_updates(CollabOrigin::Empty, updates, &document_id, vec![])?;
|
||||||
Ok(document.get_document_data().ok())
|
Ok(document.get_document_data().ok())
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ use client_api::entity::QueryCollabParams;
|
|||||||
use collab::core::origin::CollabOrigin;
|
use collab::core::origin::CollabOrigin;
|
||||||
use collab_define::CollabType;
|
use collab_define::CollabType;
|
||||||
|
|
||||||
|
use flowy_error::FlowyError;
|
||||||
use flowy_folder_deps::cloud::{Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace};
|
use flowy_folder_deps::cloud::{Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace};
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
@ -26,7 +27,10 @@ where
|
|||||||
object_id: workspace_id.clone(),
|
object_id: workspace_id.clone(),
|
||||||
collab_type: CollabType::Folder,
|
collab_type: CollabType::Folder,
|
||||||
};
|
};
|
||||||
let updates = vec![try_get_client?.get_collab(params).await?];
|
let updates = vec![try_get_client?
|
||||||
|
.get_collab(params)
|
||||||
|
.await
|
||||||
|
.map_err(FlowyError::from)?];
|
||||||
let folder =
|
let folder =
|
||||||
Folder::from_collab_raw_data(CollabOrigin::Empty, updates, &workspace_id, vec![])?;
|
Folder::from_collab_raw_data(CollabOrigin::Empty, updates, &workspace_id, vec![])?;
|
||||||
Ok(folder.get_folder_data())
|
Ok(folder.get_folder_data())
|
||||||
@ -49,8 +53,11 @@ where
|
|||||||
object_id: workspace_id,
|
object_id: workspace_id,
|
||||||
collab_type: CollabType::Folder,
|
collab_type: CollabType::Folder,
|
||||||
};
|
};
|
||||||
let updates = vec![try_get_client?.get_collab(params).await?];
|
let update = try_get_client?
|
||||||
Ok(updates)
|
.get_collab(params)
|
||||||
|
.await
|
||||||
|
.map_err(FlowyError::from)?;
|
||||||
|
Ok(vec![update])
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,9 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use client_api::entity::dto::UserUpdateParams;
|
use client_api::entity::dto::UserUpdateParams;
|
||||||
use client_api::entity::{AFUserProfileView, AFWorkspace, AFWorkspaces, InsertCollabParams};
|
use client_api::entity::{
|
||||||
|
AFUserProfileView, AFWorkspace, AFWorkspaces, InsertCollabParams, OAuthProvider,
|
||||||
|
};
|
||||||
use collab_define::CollabObject;
|
use collab_define::CollabObject;
|
||||||
|
|
||||||
use flowy_error::{ErrorCode, FlowyError};
|
use flowy_error::{ErrorCode, FlowyError};
|
||||||
@ -54,7 +56,7 @@ where
|
|||||||
FutureResult::new(async move { Ok(try_get_client?.sign_out().await?) })
|
FutureResult::new(async move { Ok(try_get_client?.sign_out().await?) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_sign_in_callback_url(&self, email: &str) -> FutureResult<String, Error> {
|
fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, Error> {
|
||||||
let email = email.to_string();
|
let email = email.to_string();
|
||||||
let try_get_client = self.server.try_get_client();
|
let try_get_client = self.server.try_get_client();
|
||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
@ -62,7 +64,19 @@ where
|
|||||||
let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").unwrap();
|
let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").unwrap();
|
||||||
let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap();
|
let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap();
|
||||||
let url = try_get_client?
|
let url = try_get_client?
|
||||||
.generate_sign_in_callback_url(&admin_email, &admin_password, &email)
|
.generate_sign_in_url_with_email(&admin_email, &admin_password, &email)
|
||||||
|
.await?;
|
||||||
|
Ok(url)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, Error> {
|
||||||
|
let provider = OAuthProvider::from(provider);
|
||||||
|
let try_get_client = self.server.try_get_client();
|
||||||
|
FutureResult::new(async move {
|
||||||
|
let provider = provider.ok_or(anyhow!("invalid provider"))?;
|
||||||
|
let url = try_get_client?
|
||||||
|
.generate_oauth_url_with_provider(&provider)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(url)
|
Ok(url)
|
||||||
})
|
})
|
||||||
@ -196,7 +210,6 @@ where
|
|||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
let client = try_get_client?;
|
let client = try_get_client?;
|
||||||
let params = InsertCollabParams::new(
|
let params = InsertCollabParams::new(
|
||||||
collab_object.uid,
|
|
||||||
collab_object.object_id.clone(),
|
collab_object.object_id.clone(),
|
||||||
collab_object.collab_type.clone(),
|
collab_object.collab_type.clone(),
|
||||||
data,
|
data,
|
||||||
@ -219,7 +232,7 @@ pub async fn user_sign_in_with_url(
|
|||||||
client: Arc<AFCloudClient>,
|
client: Arc<AFCloudClient>,
|
||||||
params: AFCloudOAuthParams,
|
params: AFCloudOAuthParams,
|
||||||
) -> Result<AuthResponse, FlowyError> {
|
) -> Result<AuthResponse, FlowyError> {
|
||||||
let is_new_user = client.sign_in_url(¶ms.sign_in_url).await?;
|
let is_new_user = client.sign_in_with_url(¶ms.sign_in_url).await?;
|
||||||
let (profile, af_workspaces) = tokio::try_join!(client.profile(), client.workspaces())?;
|
let (profile, af_workspaces) = tokio::try_join!(client.profile(), client.workspaces())?;
|
||||||
|
|
||||||
let latest_workspace = to_user_workspace(
|
let latest_workspace = to_user_workspace(
|
||||||
|
@ -7,7 +7,7 @@ use client_api::ws::{
|
|||||||
BusinessID, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
|
BusinessID, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
|
||||||
};
|
};
|
||||||
use client_api::Client;
|
use client_api::Client;
|
||||||
use tokio::sync::RwLock;
|
use tracing::info;
|
||||||
|
|
||||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||||
use flowy_document_deps::cloud::DocumentCloudService;
|
use flowy_document_deps::cloud::DocumentCloudService;
|
||||||
@ -33,7 +33,7 @@ pub struct AFCloudServer {
|
|||||||
enable_sync: AtomicBool,
|
enable_sync: AtomicBool,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
device_id: Arc<parking_lot::RwLock<String>>,
|
device_id: Arc<parking_lot::RwLock<String>>,
|
||||||
ws_client: Arc<RwLock<WSClient>>,
|
ws_client: Arc<WSClient>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AFCloudServer {
|
impl AFCloudServer {
|
||||||
@ -42,13 +42,8 @@ impl AFCloudServer {
|
|||||||
enable_sync: bool,
|
enable_sync: bool,
|
||||||
device_id: Arc<parking_lot::RwLock<String>>,
|
device_id: Arc<parking_lot::RwLock<String>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let http_client = reqwest::Client::new();
|
let api_client =
|
||||||
let api_client = client_api::Client::from(
|
client_api::Client::new(&config.base_url, &config.ws_base_url, &config.gotrue_url);
|
||||||
http_client,
|
|
||||||
&config.base_url,
|
|
||||||
&config.base_ws_url,
|
|
||||||
&config.gotrue_url,
|
|
||||||
);
|
|
||||||
let token_state_rx = api_client.subscribe_token_state();
|
let token_state_rx = api_client.subscribe_token_state();
|
||||||
let enable_sync = AtomicBool::new(enable_sync);
|
let enable_sync = AtomicBool::new(enable_sync);
|
||||||
|
|
||||||
@ -57,7 +52,7 @@ impl AFCloudServer {
|
|||||||
ping_per_secs: 8,
|
ping_per_secs: 8,
|
||||||
retry_connect_per_pings: 5,
|
retry_connect_per_pings: 5,
|
||||||
});
|
});
|
||||||
let ws_client = Arc::new(RwLock::new(ws_client));
|
let ws_client = Arc::new(ws_client);
|
||||||
let api_client = Arc::new(api_client);
|
let api_client = Arc::new(api_client);
|
||||||
|
|
||||||
spawn_ws_conn(&device_id, token_state_rx, &ws_client, &api_client);
|
spawn_ws_conn(&device_id, token_state_rx, &ws_client, &api_client);
|
||||||
@ -81,7 +76,7 @@ impl AFCloudServer {
|
|||||||
|
|
||||||
impl AppFlowyServer for AFCloudServer {
|
impl AppFlowyServer for AFCloudServer {
|
||||||
fn set_enable_sync(&self, uid: i64, enable: bool) {
|
fn set_enable_sync(&self, uid: i64, enable: bool) {
|
||||||
tracing::info!("{} cloud sync: {}", uid, enable);
|
info!("{} cloud sync: {}", uid, enable);
|
||||||
self.enable_sync.store(enable, Ordering::SeqCst);
|
self.enable_sync.store(enable, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
fn user_service(&self) -> Arc<dyn UserCloudService> {
|
fn user_service(&self) -> Arc<dyn UserCloudService> {
|
||||||
@ -115,14 +110,8 @@ impl AppFlowyServer for AFCloudServer {
|
|||||||
match weak_ws_client.upgrade() {
|
match weak_ws_client.upgrade() {
|
||||||
None => Ok(None),
|
None => Ok(None),
|
||||||
Some(ws_client) => {
|
Some(ws_client) => {
|
||||||
let channel = ws_client
|
let channel = ws_client.subscribe(BusinessID::CollabId, object_id).ok();
|
||||||
.read()
|
let connect_state_recv = ws_client.subscribe_connect_state();
|
||||||
.await
|
|
||||||
.subscribe(BusinessID::CollabId, object_id)
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
let connect_state_recv = ws_client.read().await.subscribe_connect_state().await;
|
|
||||||
|
|
||||||
Ok(channel.map(|c| (c, connect_state_recv)))
|
Ok(channel.map(|c| (c, connect_state_recv)))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -145,7 +134,7 @@ impl AppFlowyServer for AFCloudServer {
|
|||||||
fn spawn_ws_conn(
|
fn spawn_ws_conn(
|
||||||
device_id: &Arc<parking_lot::RwLock<String>>,
|
device_id: &Arc<parking_lot::RwLock<String>>,
|
||||||
mut token_state_rx: TokenStateReceiver,
|
mut token_state_rx: TokenStateReceiver,
|
||||||
ws_client: &Arc<RwLock<WSClient>>,
|
ws_client: &Arc<WSClient>,
|
||||||
api_client: &Arc<Client>,
|
api_client: &Arc<Client>,
|
||||||
) {
|
) {
|
||||||
let weak_device_id = Arc::downgrade(device_id);
|
let weak_device_id = Arc::downgrade(device_id);
|
||||||
@ -154,7 +143,7 @@ fn spawn_ws_conn(
|
|||||||
|
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
if let Some(ws_client) = weak_ws_client.upgrade() {
|
if let Some(ws_client) = weak_ws_client.upgrade() {
|
||||||
let mut state_recv = ws_client.read().await.subscribe_connect_state().await;
|
let mut state_recv = ws_client.subscribe_connect_state();
|
||||||
while let Ok(state) = state_recv.recv().await {
|
while let Ok(state) = state_recv.recv().await {
|
||||||
if !state.is_timeout() {
|
if !state.is_timeout() {
|
||||||
continue;
|
continue;
|
||||||
@ -166,8 +155,8 @@ fn spawn_ws_conn(
|
|||||||
{
|
{
|
||||||
let device_id = device_id.read().clone();
|
let device_id = device_id.read().clone();
|
||||||
if let Ok(ws_addr) = api_client.ws_url(&device_id) {
|
if let Ok(ws_addr) = api_client.ws_url(&device_id) {
|
||||||
tracing::info!("🟢WebSocket Reconnecting");
|
info!("🟢WebSocket Reconnecting");
|
||||||
let _ = ws_client.write().await.connect(ws_addr).await;
|
let _ = ws_client.connect(ws_addr).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -179,7 +168,7 @@ fn spawn_ws_conn(
|
|||||||
let weak_api_client = Arc::downgrade(api_client);
|
let weak_api_client = Arc::downgrade(api_client);
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
while let Ok(token_state) = token_state_rx.recv().await {
|
while let Ok(token_state) = token_state_rx.recv().await {
|
||||||
tracing::info!("🟢Token state: {:?}", token_state);
|
info!("🟢Token state: {:?}", token_state);
|
||||||
match token_state {
|
match token_state {
|
||||||
TokenState::Refresh => {
|
TokenState::Refresh => {
|
||||||
if let (Some(api_client), Some(ws_client), Some(device_id)) = (
|
if let (Some(api_client), Some(ws_client), Some(device_id)) = (
|
||||||
@ -189,14 +178,14 @@ fn spawn_ws_conn(
|
|||||||
) {
|
) {
|
||||||
let device_id = device_id.read().clone();
|
let device_id = device_id.read().clone();
|
||||||
if let Ok(ws_addr) = api_client.ws_url(&device_id) {
|
if let Ok(ws_addr) = api_client.ws_url(&device_id) {
|
||||||
let _ = ws_client.write().await.connect(ws_addr).await;
|
let _ = ws_client.connect(ws_addr).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TokenState::Invalid => {
|
TokenState::Invalid => {
|
||||||
if let Some(ws_client) = weak_ws_client.upgrade() {
|
if let Some(ws_client) = weak_ws_client.upgrade() {
|
||||||
tracing::info!("🟡WebSocket Disconnecting");
|
info!("🟡WebSocket Disconnecting");
|
||||||
ws_client.write().await.disconnect().await;
|
ws_client.disconnect().await;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
|||||||
FutureResult::new(async { Ok(()) })
|
FutureResult::new(async { Ok(()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_sign_in_callback_url(&self, _email: &str) -> FutureResult<String, Error> {
|
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, Error> {
|
||||||
FutureResult::new(async {
|
FutureResult::new(async {
|
||||||
Err(anyhow::anyhow!(
|
Err(anyhow::anyhow!(
|
||||||
"Can't generate callback url when using offline mode"
|
"Can't generate callback url when using offline mode"
|
||||||
@ -84,6 +84,10 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, Error> {
|
||||||
|
FutureResult::new(async { Err(anyhow::anyhow!("Can't oauth url when using offline mode")) })
|
||||||
|
}
|
||||||
|
|
||||||
fn update_user(
|
fn update_user(
|
||||||
&self,
|
&self,
|
||||||
_credential: UserCredentials,
|
_credential: UserCredentials,
|
||||||
|
@ -165,7 +165,7 @@ where
|
|||||||
FutureResult::new(async { Ok(()) })
|
FutureResult::new(async { Ok(()) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_sign_in_callback_url(&self, _email: &str) -> FutureResult<String, Error> {
|
fn generate_sign_in_url_with_email(&self, _email: &str) -> FutureResult<String, Error> {
|
||||||
FutureResult::new(async {
|
FutureResult::new(async {
|
||||||
Err(anyhow::anyhow!(
|
Err(anyhow::anyhow!(
|
||||||
"Can't generate callback url when using supabase"
|
"Can't generate callback url when using supabase"
|
||||||
@ -173,6 +173,14 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_oauth_url_with_provider(&self, _provider: &str) -> FutureResult<String, Error> {
|
||||||
|
FutureResult::new(async {
|
||||||
|
Err(anyhow::anyhow!(
|
||||||
|
"Can't generate oauth url when using supabase"
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn update_user(
|
fn update_user(
|
||||||
&self,
|
&self,
|
||||||
_credential: UserCredentials,
|
_credential: UserCredentials,
|
||||||
|
@ -23,18 +23,13 @@ pub fn af_cloud_server(config: AFCloudConfiguration) -> Arc<AFCloudServer> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguration) -> String {
|
pub async fn generate_sign_in_url(user_email: &str, config: &AFCloudConfiguration) -> String {
|
||||||
let http_client = reqwest::Client::new();
|
let api_client =
|
||||||
let api_client = client_api::Client::from(
|
client_api::Client::new(&config.base_url, &config.ws_base_url, &config.gotrue_url);
|
||||||
http_client,
|
|
||||||
&config.base_url,
|
|
||||||
&config.base_ws_url,
|
|
||||||
&config.gotrue_url,
|
|
||||||
);
|
|
||||||
|
|
||||||
let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").unwrap();
|
let admin_email = std::env::var("GOTRUE_ADMIN_EMAIL").unwrap();
|
||||||
let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap();
|
let admin_password = std::env::var("GOTRUE_ADMIN_PASSWORD").unwrap();
|
||||||
api_client
|
api_client
|
||||||
.generate_sign_in_callback_url(&admin_email, &admin_password, user_email)
|
.generate_sign_in_url_with_email(&admin_email, &admin_password, user_email)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ use flowy_notification::entities::SubscribeObject;
|
|||||||
use flowy_notification::{register_notification_sender, NotificationSender};
|
use flowy_notification::{register_notification_sender, NotificationSender};
|
||||||
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
|
use flowy_server::supabase::define::{USER_DEVICE_ID, USER_EMAIL, USER_SIGN_IN_URL, USER_UUID};
|
||||||
use flowy_user::entities::{
|
use flowy_user::entities::{
|
||||||
AuthTypePB, OAuthCallbackRequestPB, OAuthCallbackResponsePB, OAuthPB, UpdateCloudConfigPB,
|
AuthTypePB, OauthSignInPB, SignInUrlPB, SignInUrlPayloadPB, UpdateCloudConfigPB,
|
||||||
UserCloudConfigPB, UserProfilePB,
|
UserCloudConfigPB, UserProfilePB,
|
||||||
};
|
};
|
||||||
use flowy_user::errors::{FlowyError, FlowyResult};
|
use flowy_user::errors::{FlowyError, FlowyResult};
|
||||||
@ -169,13 +169,13 @@ impl FlowyCoreTest {
|
|||||||
|
|
||||||
pub async fn supabase_party_sign_up(&self) -> UserProfilePB {
|
pub async fn supabase_party_sign_up(&self) -> UserProfilePB {
|
||||||
let map = third_party_sign_up_param(Uuid::new_v4().to_string());
|
let map = third_party_sign_up_param(Uuid::new_v4().to_string());
|
||||||
let payload = OAuthPB {
|
let payload = OauthSignInPB {
|
||||||
map,
|
map,
|
||||||
auth_type: AuthTypePB::Supabase,
|
auth_type: AuthTypePB::Supabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
EventBuilder::new(self.clone())
|
EventBuilder::new(self.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
@ -198,28 +198,28 @@ impl FlowyCoreTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
|
pub async fn af_cloud_sign_in_with_email(&self, email: &str) -> FlowyResult<UserProfilePB> {
|
||||||
let payload = OAuthCallbackRequestPB {
|
let payload = SignInUrlPayloadPB {
|
||||||
email: email.to_string(),
|
email: email.to_string(),
|
||||||
auth_type: AuthTypePB::AFCloud,
|
auth_type: AuthTypePB::AFCloud,
|
||||||
};
|
};
|
||||||
let sign_in_url = EventBuilder::new(self.clone())
|
let sign_in_url = EventBuilder::new(self.clone())
|
||||||
.event(OAuthCallbackURL)
|
.event(GetSignInURL)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
.try_parse::<OAuthCallbackResponsePB>()?
|
.try_parse::<SignInUrlPB>()?
|
||||||
.sign_in_url;
|
.sign_in_url;
|
||||||
|
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url);
|
map.insert(USER_SIGN_IN_URL.to_string(), sign_in_url);
|
||||||
map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string());
|
map.insert(USER_DEVICE_ID.to_string(), uuid::Uuid::new_v4().to_string());
|
||||||
let payload = OAuthPB {
|
let payload = OauthSignInPB {
|
||||||
map,
|
map,
|
||||||
auth_type: AuthTypePB::AFCloud,
|
auth_type: AuthTypePB::AFCloud,
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_profile = EventBuilder::new(self.clone())
|
let user_profile = EventBuilder::new(self.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
@ -240,13 +240,13 @@ impl FlowyCoreTest {
|
|||||||
USER_EMAIL.to_string(),
|
USER_EMAIL.to_string(),
|
||||||
email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))),
|
email.unwrap_or_else(|| format!("{}@appflowy.io", nanoid!(10))),
|
||||||
);
|
);
|
||||||
let payload = OAuthPB {
|
let payload = OauthSignInPB {
|
||||||
map,
|
map,
|
||||||
auth_type: AuthTypePB::Supabase,
|
auth_type: AuthTypePB::Supabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
let user_profile = EventBuilder::new(self.clone())
|
let user_profile = EventBuilder::new(self.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
|
@ -14,7 +14,7 @@ use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
|||||||
use flowy_test::document::document_event::DocumentEventTest;
|
use flowy_test::document::document_event::DocumentEventTest;
|
||||||
use flowy_test::event_builder::EventBuilder;
|
use flowy_test::event_builder::EventBuilder;
|
||||||
use flowy_test::FlowyCoreTest;
|
use flowy_test::FlowyCoreTest;
|
||||||
use flowy_user::entities::{AuthTypePB, OAuthPB, UpdateUserProfilePayloadPB, UserProfilePB};
|
use flowy_user::entities::{AuthTypePB, OauthSignInPB, UpdateUserProfilePayloadPB, UserProfilePB};
|
||||||
use flowy_user::errors::ErrorCode;
|
use flowy_user::errors::ErrorCode;
|
||||||
use flowy_user::event_map::UserEvent::*;
|
use flowy_user::event_map::UserEvent::*;
|
||||||
|
|
||||||
@ -30,13 +30,13 @@ async fn third_party_sign_up_test() {
|
|||||||
USER_EMAIL.to_string(),
|
USER_EMAIL.to_string(),
|
||||||
format!("{}@appflowy.io", nanoid!(6)),
|
format!("{}@appflowy.io", nanoid!(6)),
|
||||||
);
|
);
|
||||||
let payload = OAuthPB {
|
let payload = OauthSignInPB {
|
||||||
map,
|
map,
|
||||||
auth_type: AuthTypePB::Supabase,
|
auth_type: AuthTypePB::Supabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
let response = EventBuilder::new(test.clone())
|
let response = EventBuilder::new(test.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
@ -72,8 +72,8 @@ async fn third_party_sign_up_with_duplicated_uuid() {
|
|||||||
map.insert(USER_EMAIL.to_string(), email.clone());
|
map.insert(USER_EMAIL.to_string(), email.clone());
|
||||||
|
|
||||||
let response_1 = EventBuilder::new(test.clone())
|
let response_1 = EventBuilder::new(test.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(OAuthPB {
|
.payload(OauthSignInPB {
|
||||||
map: map.clone(),
|
map: map.clone(),
|
||||||
auth_type: AuthTypePB::Supabase,
|
auth_type: AuthTypePB::Supabase,
|
||||||
})
|
})
|
||||||
@ -83,8 +83,8 @@ async fn third_party_sign_up_with_duplicated_uuid() {
|
|||||||
dbg!(&response_1);
|
dbg!(&response_1);
|
||||||
|
|
||||||
let response_2 = EventBuilder::new(test.clone())
|
let response_2 = EventBuilder::new(test.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(OAuthPB {
|
.payload(OauthSignInPB {
|
||||||
map: map.clone(),
|
map: map.clone(),
|
||||||
auth_type: AuthTypePB::Supabase,
|
auth_type: AuthTypePB::Supabase,
|
||||||
})
|
})
|
||||||
|
@ -4,7 +4,7 @@ use flowy_folder2::entities::WorkspaceSettingPB;
|
|||||||
use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace;
|
use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace;
|
||||||
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
||||||
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
|
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
|
||||||
use flowy_user::entities::{AuthTypePB, OAuthPB, UserProfilePB};
|
use flowy_user::entities::{AuthTypePB, OauthSignInPB, UserProfilePB};
|
||||||
use flowy_user::event_map::UserEvent::*;
|
use flowy_user::event_map::UserEvent::*;
|
||||||
|
|
||||||
use crate::util::*;
|
use crate::util::*;
|
||||||
@ -19,13 +19,13 @@ async fn initial_workspace_test() {
|
|||||||
USER_EMAIL.to_string(),
|
USER_EMAIL.to_string(),
|
||||||
format!("{}@gmail.com", uuid::Uuid::new_v4()),
|
format!("{}@gmail.com", uuid::Uuid::new_v4()),
|
||||||
);
|
);
|
||||||
let payload = OAuthPB {
|
let payload = OauthSignInPB {
|
||||||
map,
|
map,
|
||||||
auth_type: AuthTypePB::Supabase,
|
auth_type: AuthTypePB::Supabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
let _ = EventBuilder::new(test.clone())
|
let _ = EventBuilder::new(test.clone())
|
||||||
.event(OAuth)
|
.event(OauthSignIn)
|
||||||
.payload(payload)
|
.payload(payload)
|
||||||
.async_send()
|
.async_send()
|
||||||
.await
|
.await
|
||||||
|
@ -70,8 +70,15 @@ pub trait UserCloudService: Send + Sync + 'static {
|
|||||||
/// Sign out an account
|
/// Sign out an account
|
||||||
fn sign_out(&self, token: Option<String>) -> FutureResult<(), Error>;
|
fn sign_out(&self, token: Option<String>) -> FutureResult<(), Error>;
|
||||||
|
|
||||||
/// Generate a sign in callback url for the user with the given email
|
/// Generate a sign in url for the user with the given email
|
||||||
fn generate_sign_in_callback_url(&self, email: &str) -> FutureResult<String, Error>;
|
fn generate_sign_in_url_with_email(&self, email: &str) -> FutureResult<String, Error>;
|
||||||
|
|
||||||
|
/// When the user opens the OAuth URL, it redirects to the corresponding provider's OAuth web page.
|
||||||
|
/// After the user is authenticated, the browser will open a deep link to the AppFlowy app (iOS, macOS, etc.),
|
||||||
|
/// which will call [Client::sign_in_with_url] to sign in.
|
||||||
|
///
|
||||||
|
/// For example, the OAuth URL on Google looks like `https://appflowy.io/authorize?provider=google`.
|
||||||
|
fn generate_oauth_url_with_provider(&self, provider: &str) -> FutureResult<String, Error>;
|
||||||
|
|
||||||
/// Using the user's token to update the user information
|
/// Using the user's token to update the user information
|
||||||
fn update_user(
|
fn update_user(
|
||||||
|
@ -79,7 +79,7 @@ impl TryInto<SignUpParams> for SignUpPayloadPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
#[derive(ProtoBuf, Default)]
|
||||||
pub struct OAuthPB {
|
pub struct OauthSignInPB {
|
||||||
/// Use this field to store the third party auth information.
|
/// Use this field to store the third party auth information.
|
||||||
/// Different auth type has different fields.
|
/// Different auth type has different fields.
|
||||||
/// Supabase:
|
/// Supabase:
|
||||||
@ -93,7 +93,7 @@ pub struct OAuthPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
#[derive(ProtoBuf, Default)]
|
||||||
pub struct OAuthCallbackRequestPB {
|
pub struct SignInUrlPayloadPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub email: String,
|
pub email: String,
|
||||||
|
|
||||||
@ -102,11 +102,77 @@ pub struct OAuthCallbackRequestPB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default)]
|
#[derive(ProtoBuf, Default)]
|
||||||
pub struct OAuthCallbackResponsePB {
|
pub struct SignInUrlPB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub sign_in_url: String,
|
pub sign_in_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(ProtoBuf, Default)]
|
||||||
|
pub struct OauthProviderPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub provider: ProviderTypePB,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone, Default)]
|
||||||
|
pub enum ProviderTypePB {
|
||||||
|
Apple = 0,
|
||||||
|
Azure = 1,
|
||||||
|
Bitbucket = 2,
|
||||||
|
Discord = 3,
|
||||||
|
Facebook = 4,
|
||||||
|
Figma = 5,
|
||||||
|
Github = 6,
|
||||||
|
Gitlab = 7,
|
||||||
|
#[default]
|
||||||
|
Google = 8,
|
||||||
|
Keycloak = 9,
|
||||||
|
Kakao = 10,
|
||||||
|
Linkedin = 11,
|
||||||
|
Notion = 12,
|
||||||
|
Spotify = 13,
|
||||||
|
Slack = 14,
|
||||||
|
Workos = 15,
|
||||||
|
Twitch = 16,
|
||||||
|
Twitter = 17,
|
||||||
|
Email = 18,
|
||||||
|
Phone = 19,
|
||||||
|
Zoom = 20,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProviderTypePB {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
ProviderTypePB::Apple => "apple",
|
||||||
|
ProviderTypePB::Azure => "azure",
|
||||||
|
ProviderTypePB::Bitbucket => "bitbucket",
|
||||||
|
ProviderTypePB::Discord => "discord",
|
||||||
|
ProviderTypePB::Facebook => "facebook",
|
||||||
|
ProviderTypePB::Figma => "figma",
|
||||||
|
ProviderTypePB::Github => "github",
|
||||||
|
ProviderTypePB::Gitlab => "gitlab",
|
||||||
|
ProviderTypePB::Google => "google",
|
||||||
|
ProviderTypePB::Keycloak => "keycloak",
|
||||||
|
ProviderTypePB::Kakao => "kakao",
|
||||||
|
ProviderTypePB::Linkedin => "linkedin",
|
||||||
|
ProviderTypePB::Notion => "notion",
|
||||||
|
ProviderTypePB::Spotify => "spotify",
|
||||||
|
ProviderTypePB::Slack => "slack",
|
||||||
|
ProviderTypePB::Workos => "workos",
|
||||||
|
ProviderTypePB::Twitch => "twitch",
|
||||||
|
ProviderTypePB::Twitter => "twitter",
|
||||||
|
ProviderTypePB::Email => "email",
|
||||||
|
ProviderTypePB::Phone => "phone",
|
||||||
|
ProviderTypePB::Zoom => "zoom",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(ProtoBuf, Default)]
|
||||||
|
pub struct OauthProviderDataPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
pub oauth_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
|
#[derive(ProtoBuf_Enum, Eq, PartialEq, Debug, Clone)]
|
||||||
pub enum AuthTypePB {
|
pub enum AuthTypePB {
|
||||||
Local = 0,
|
Local = 0,
|
||||||
|
@ -215,11 +215,9 @@ pub async fn get_user_setting(
|
|||||||
data_result_ok(user_setting)
|
data_result_ok(user_setting)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Only used for third party auth.
|
|
||||||
/// Use [UserEvent::SignIn] or [UserEvent::SignUp] If the [AuthType] is Local or SelfHosted
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||||
pub async fn oauth_handler(
|
pub async fn oauth_handler(
|
||||||
data: AFPluginData<OAuthPB>,
|
data: AFPluginData<OauthSignInPB>,
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
manager: AFPluginState<Weak<UserManager>>,
|
||||||
) -> DataResult<UserProfilePB, FlowyError> {
|
) -> DataResult<UserProfilePB, FlowyError> {
|
||||||
let manager = upgrade_manager(manager)?;
|
let manager = upgrade_manager(manager)?;
|
||||||
@ -230,20 +228,33 @@ pub async fn oauth_handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||||
pub async fn get_oauth_url_handler(
|
pub async fn get_sign_in_url_handler(
|
||||||
data: AFPluginData<OAuthCallbackRequestPB>,
|
data: AFPluginData<SignInUrlPayloadPB>,
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
manager: AFPluginState<Weak<UserManager>>,
|
||||||
) -> DataResult<OAuthCallbackResponsePB, FlowyError> {
|
) -> DataResult<SignInUrlPB, FlowyError> {
|
||||||
let manager = upgrade_manager(manager)?;
|
let manager = upgrade_manager(manager)?;
|
||||||
let params = data.into_inner();
|
let params = data.into_inner();
|
||||||
let auth_type: AuthType = params.auth_type.into();
|
let auth_type: AuthType = params.auth_type.into();
|
||||||
let sign_in_url = manager
|
let sign_in_url = manager
|
||||||
.generate_sign_in_callback_url(&auth_type, ¶ms.email)
|
.generate_sign_in_url_with_email(&auth_type, ¶ms.email)
|
||||||
.await?;
|
.await?;
|
||||||
let resp = OAuthCallbackResponsePB { sign_in_url };
|
let resp = SignInUrlPB { sign_in_url };
|
||||||
data_result_ok(resp)
|
data_result_ok(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
|
pub async fn sign_in_with_provider_handler(
|
||||||
|
data: AFPluginData<OauthProviderPB>,
|
||||||
|
manager: AFPluginState<Weak<UserManager>>,
|
||||||
|
) -> DataResult<OauthProviderDataPB, FlowyError> {
|
||||||
|
let manager = upgrade_manager(manager)?;
|
||||||
|
tracing::debug!("Sign in with provider: {:?}", data.provider.as_str());
|
||||||
|
let sign_in_url = manager.generate_oauth_url(data.provider.as_str()).await?;
|
||||||
|
data_result_ok(OauthProviderDataPB {
|
||||||
|
oauth_url: sign_in_url,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip_all, err)]
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn set_encrypt_secret_handler(
|
pub async fn set_encrypt_secret_handler(
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
manager: AFPluginState<Weak<UserManager>>,
|
||||||
|
@ -37,8 +37,12 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
|
|||||||
.event(UserEvent::GetCloudConfig, get_cloud_config_handler)
|
.event(UserEvent::GetCloudConfig, get_cloud_config_handler)
|
||||||
.event(UserEvent::SetEncryptionSecret, set_encrypt_secret_handler)
|
.event(UserEvent::SetEncryptionSecret, set_encrypt_secret_handler)
|
||||||
.event(UserEvent::CheckEncryptionSign, check_encrypt_secret_handler)
|
.event(UserEvent::CheckEncryptionSign, check_encrypt_secret_handler)
|
||||||
.event(UserEvent::OAuth, oauth_handler)
|
.event(UserEvent::OauthSignIn, oauth_handler)
|
||||||
.event(UserEvent::OAuthCallbackURL, get_oauth_url_handler)
|
.event(UserEvent::GetSignInURL, get_sign_in_url_handler)
|
||||||
|
.event(
|
||||||
|
UserEvent::GetOauthURLWithProvider,
|
||||||
|
sign_in_with_provider_handler,
|
||||||
|
)
|
||||||
.event(
|
.event(
|
||||||
UserEvent::GetAllUserWorkspaces,
|
UserEvent::GetAllUserWorkspaces,
|
||||||
get_all_user_workspace_handler,
|
get_all_user_workspace_handler,
|
||||||
@ -230,13 +234,16 @@ pub enum UserEvent {
|
|||||||
#[event(output = "UserSettingPB")]
|
#[event(output = "UserSettingPB")]
|
||||||
GetUserSetting = 9,
|
GetUserSetting = 9,
|
||||||
|
|
||||||
#[event(input = "OAuthPB", output = "UserProfilePB")]
|
#[event(input = "OauthSignInPB", output = "UserProfilePB")]
|
||||||
OAuth = 10,
|
OauthSignIn = 10,
|
||||||
|
|
||||||
/// Get the OAuth callback url
|
/// Get the OAuth callback url
|
||||||
/// Only use when the [AuthType] is AFCloud
|
/// Only use when the [AuthType] is AFCloud
|
||||||
#[event(input = "OAuthCallbackRequestPB", output = "OAuthCallbackResponsePB")]
|
#[event(input = "SignInUrlPayloadPB", output = "SignInUrlPB")]
|
||||||
OAuthCallbackURL = 11,
|
GetSignInURL = 11,
|
||||||
|
|
||||||
|
#[event(input = "OauthProviderPB", output = "OauthProviderDataPB")]
|
||||||
|
GetOauthURLWithProvider = 12,
|
||||||
|
|
||||||
#[event(input = "UpdateCloudConfigPB")]
|
#[event(input = "UpdateCloudConfigPB")]
|
||||||
SetCloudConfig = 13,
|
SetCloudConfig = 13,
|
||||||
|
@ -4,6 +4,7 @@ use std::sync::{Arc, Weak};
|
|||||||
use collab_user::core::MutexUserAwareness;
|
use collab_user::core::MutexUserAwareness;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tokio::sync::{Mutex, RwLock};
|
use tokio::sync::{Mutex, RwLock};
|
||||||
|
use tracing::{debug, info};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
@ -134,7 +135,7 @@ impl UserManager {
|
|||||||
{
|
{
|
||||||
Ok(applied_migrations) => {
|
Ok(applied_migrations) => {
|
||||||
if !applied_migrations.is_empty() {
|
if !applied_migrations.is_empty() {
|
||||||
tracing::info!("Did apply migrations: {:?}", applied_migrations);
|
info!("Did apply migrations: {:?}", applied_migrations);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => tracing::error!("User data migration failed: {:?}", e),
|
Err(e) => tracing::error!("User data migration failed: {:?}", e),
|
||||||
@ -312,16 +313,16 @@ impl UserManager {
|
|||||||
UserAwarenessDataSource::Remote
|
UserAwarenessDataSource::Remote
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("Sign up response: {:?}", response);
|
||||||
if response.is_new_user {
|
if response.is_new_user {
|
||||||
if let Some(old_user) = migration_user {
|
if let Some(old_user) = migration_user {
|
||||||
let new_user = MigrationUser {
|
let new_user = MigrationUser {
|
||||||
user_profile: user_profile.clone(),
|
user_profile: user_profile.clone(),
|
||||||
session: new_session.clone(),
|
session: new_session.clone(),
|
||||||
};
|
};
|
||||||
tracing::info!(
|
info!(
|
||||||
"Migrate old user data from {:?} to {:?}",
|
"Migrate old user data from {:?} to {:?}",
|
||||||
old_user.user_profile.uid,
|
old_user.user_profile.uid, new_user.user_profile.uid
|
||||||
new_user.user_profile.uid
|
|
||||||
);
|
);
|
||||||
self
|
self
|
||||||
.migrate_local_user_to_cloud(&old_user, &new_user)
|
.migrate_local_user_to_cloud(&old_user, &new_user)
|
||||||
@ -524,7 +525,7 @@ impl UserManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
|
pub(crate) fn set_session(&self, session: Option<Session>) -> Result<(), FlowyError> {
|
||||||
tracing::debug!("Set current user: {:?}", session);
|
debug!("Set current user: {:?}", session);
|
||||||
match &session {
|
match &session {
|
||||||
None => {
|
None => {
|
||||||
self.current_session.write().take();
|
self.current_session.write().take();
|
||||||
@ -543,7 +544,7 @@ impl UserManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn generate_sign_in_callback_url(
|
pub(crate) async fn generate_sign_in_url_with_email(
|
||||||
&self,
|
&self,
|
||||||
auth_type: &AuthType,
|
auth_type: &AuthType,
|
||||||
email: &str,
|
email: &str,
|
||||||
@ -551,7 +552,22 @@ impl UserManager {
|
|||||||
self.update_auth_type(auth_type).await;
|
self.update_auth_type(auth_type).await;
|
||||||
|
|
||||||
let auth_service = self.cloud_services.get_user_service()?;
|
let auth_service = self.cloud_services.get_user_service()?;
|
||||||
let url = auth_service.generate_sign_in_callback_url(email).await?;
|
let url = auth_service
|
||||||
|
.generate_sign_in_url_with_email(email)
|
||||||
|
.await
|
||||||
|
.map_err(|err| FlowyError::server_error().with_context(err))?;
|
||||||
|
Ok(url)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn generate_oauth_url(
|
||||||
|
&self,
|
||||||
|
oauth_provider: &str,
|
||||||
|
) -> Result<String, FlowyError> {
|
||||||
|
self.update_auth_type(&AuthType::AFCloud).await;
|
||||||
|
let auth_service = self.cloud_services.get_user_service()?;
|
||||||
|
let url = auth_service
|
||||||
|
.generate_oauth_url_with_provider(oauth_provider)
|
||||||
|
.await?;
|
||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,7 +604,7 @@ impl UserManager {
|
|||||||
async fn handler_user_update(&self, user_update: UserUpdate) -> FlowyResult<()> {
|
async fn handler_user_update(&self, user_update: UserUpdate) -> FlowyResult<()> {
|
||||||
let session = self.get_session()?;
|
let session = self.get_session()?;
|
||||||
if session.user_id == user_update.uid {
|
if session.user_id == user_update.uid {
|
||||||
tracing::debug!("Receive user update: {:?}", user_update);
|
debug!("Receive user update: {:?}", user_update);
|
||||||
let user_profile = self.get_user_profile(user_update.uid).await?;
|
let user_profile = self.get_user_profile(user_update.uid).await?;
|
||||||
|
|
||||||
if !is_user_encryption_sign_valid(&user_profile, &user_update.encryption_sign) {
|
if !is_user_encryption_sign_valid(&user_profile, &user_update.encryption_sign) {
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use collab::core::collab::{CollabRawData, MutexCollab};
|
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||||
use collab_define::reminder::Reminder;
|
use collab_define::reminder::Reminder;
|
||||||
use collab_define::CollabType;
|
use collab_define::CollabType;
|
||||||
use collab_user::core::{MutexUserAwareness, UserAwareness};
|
use collab_user::core::{MutexUserAwareness, UserAwareness};
|
||||||
|
use tracing::{error, trace};
|
||||||
|
|
||||||
use collab_integrate::RocksCollabDB;
|
use collab_integrate::RocksCollabDB;
|
||||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
@ -101,12 +103,8 @@ impl UserManager {
|
|||||||
source: UserAwarenessDataSource,
|
source: UserAwarenessDataSource,
|
||||||
) {
|
) {
|
||||||
match self.try_initial_user_awareness(session, source).await {
|
match self.try_initial_user_awareness(session, source).await {
|
||||||
Ok(_) => {
|
Ok(_) => trace!("User awareness initialized"),
|
||||||
tracing::trace!("User awareness initialized");
|
Err(e) => error!("Failed to initialize user awareness: {:?}", e),
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
tracing::error!("Failed to initialize user awareness: {:?}", e);
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,11 +126,13 @@ impl UserManager {
|
|||||||
session: &Session,
|
session: &Session,
|
||||||
source: UserAwarenessDataSource,
|
source: UserAwarenessDataSource,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
tracing::trace!("Initializing user awareness from {:?}", source);
|
trace!("Initializing user awareness from {:?}", source);
|
||||||
let collab_db = self.get_collab_db(session.user_id)?;
|
let collab_db = self.get_collab_db(session.user_id)?;
|
||||||
let user_awareness = match source {
|
let user_awareness = match source {
|
||||||
UserAwarenessDataSource::Local => {
|
UserAwarenessDataSource::Local => {
|
||||||
let collab = self.collab_for_user_awareness(session, collab_db, vec![])?;
|
let collab = self
|
||||||
|
.collab_for_user_awareness(session, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
MutexUserAwareness::new(UserAwareness::create(collab, None))
|
MutexUserAwareness::new(UserAwareness::create(collab, None))
|
||||||
},
|
},
|
||||||
UserAwarenessDataSource::Remote => {
|
UserAwarenessDataSource::Remote => {
|
||||||
@ -141,7 +141,10 @@ impl UserManager {
|
|||||||
.get_user_service()?
|
.get_user_service()?
|
||||||
.get_user_awareness_updates(session.user_id)
|
.get_user_awareness_updates(session.user_id)
|
||||||
.await?;
|
.await?;
|
||||||
let collab = self.collab_for_user_awareness(session, collab_db, data)?;
|
trace!("Get user awareness collab: {}", data.len());
|
||||||
|
let collab = self
|
||||||
|
.collab_for_user_awareness(session, collab_db, data)
|
||||||
|
.await?;
|
||||||
MutexUserAwareness::new(UserAwareness::create(collab, None))
|
MutexUserAwareness::new(UserAwareness::create(collab, None))
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -154,7 +157,7 @@ impl UserManager {
|
|||||||
/// This function constructs a collaboration instance based on the given session and raw data,
|
/// This function constructs a collaboration instance based on the given session and raw data,
|
||||||
/// using a collaboration builder. This instance is specifically geared towards handling
|
/// using a collaboration builder. This instance is specifically geared towards handling
|
||||||
/// user awareness.
|
/// user awareness.
|
||||||
fn collab_for_user_awareness(
|
async fn collab_for_user_awareness(
|
||||||
&self,
|
&self,
|
||||||
session: &Session,
|
session: &Session,
|
||||||
collab_db: Weak<RocksCollabDB>,
|
collab_db: Weak<RocksCollabDB>,
|
||||||
@ -164,13 +167,16 @@ impl UserManager {
|
|||||||
ErrorCode::Internal,
|
ErrorCode::Internal,
|
||||||
"Unexpected error: collab builder is not available",
|
"Unexpected error: collab builder is not available",
|
||||||
))?;
|
))?;
|
||||||
let collab = collab_builder.build(
|
let collab = collab_builder
|
||||||
session.user_id,
|
.build(
|
||||||
&session.user_id.to_string(),
|
session.user_id,
|
||||||
CollabType::UserAwareness,
|
&session.user_id.to_string(),
|
||||||
raw_data,
|
CollabType::UserAwareness,
|
||||||
collab_db,
|
raw_data,
|
||||||
)?;
|
collab_db,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("Build collab for user awareness failed")?;
|
||||||
Ok(collab)
|
Ok(collab)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
use crate::{
|
|
||||||
errors::{DispatchError, InternalError},
|
|
||||||
module::{container::AFPluginStateMap, AFPluginState},
|
|
||||||
request::{payload::Payload, AFPluginEventRequest, FromAFPluginRequest},
|
|
||||||
response::{AFPluginEventResponse, AFPluginResponder},
|
|
||||||
service::{
|
|
||||||
factory, AFPluginHandler, AFPluginHandlerService, AFPluginServiceFactory, BoxService,
|
|
||||||
BoxServiceFactory, Service, ServiceRequest, ServiceResponse,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
use futures_core::future::BoxFuture;
|
|
||||||
use futures_core::ready;
|
|
||||||
use nanoid::nanoid;
|
|
||||||
use pin_project::pin_project;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
@ -23,6 +9,23 @@ use std::{
|
|||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use futures_core::future::BoxFuture;
|
||||||
|
use futures_core::ready;
|
||||||
|
use nanoid::nanoid;
|
||||||
|
use pin_project::pin_project;
|
||||||
|
|
||||||
|
use crate::service::AFPluginHandler;
|
||||||
|
use crate::{
|
||||||
|
errors::{DispatchError, InternalError},
|
||||||
|
module::{container::AFPluginStateMap, AFPluginState},
|
||||||
|
request::{payload::Payload, AFPluginEventRequest, FromAFPluginRequest},
|
||||||
|
response::{AFPluginEventResponse, AFPluginResponder},
|
||||||
|
service::{
|
||||||
|
factory, AFPluginHandlerService, AFPluginServiceFactory, BoxService, BoxServiceFactory,
|
||||||
|
Service, ServiceRequest, ServiceResponse,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
pub type AFPluginMap = Arc<HashMap<AFPluginEvent, Arc<AFPlugin>>>;
|
pub type AFPluginMap = Arc<HashMap<AFPluginEvent, Arc<AFPlugin>>>;
|
||||||
pub(crate) fn as_plugin_map(plugins: Vec<AFPlugin>) -> AFPluginMap {
|
pub(crate) fn as_plugin_map(plugins: Vec<AFPlugin>) -> AFPluginMap {
|
||||||
let mut plugin_map = HashMap::new();
|
let mut plugin_map = HashMap::new();
|
||||||
@ -93,6 +96,7 @@ impl AFPlugin {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
pub fn event<E, H, T, R>(mut self, event: E, handler: H) -> Self
|
pub fn event<E, H, T, R>(mut self, event: E, handler: H) -> Self
|
||||||
where
|
where
|
||||||
H: AFPluginHandler<T, R>,
|
H: AFPluginHandler<T, R>,
|
||||||
|
Reference in New Issue
Block a user