mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
fix: af cloud sync auth (#3873)
* feat: open workspace * chore: update env docs * fix: invalid user callback * fix: token invalid * chore: update * chore: update * chore: update * chore: fix test * chore: fix tauri build
This commit is contained in:
parent
b35d6131d4
commit
1025b6d553
@ -1,23 +1,33 @@
|
|||||||
# Initial Setup
|
# Initial Setup
|
||||||
# 1. Copy the dev.env file to .env:
|
|
||||||
# cp dev.env .env
|
|
||||||
# 2. Alternatively, you can generate the .env file using the "Generate Env File" task in VSCode.
|
|
||||||
|
|
||||||
# Configuring Cloud Type
|
# 1. Copy the dev.env file to .env:
|
||||||
# This configuration file is used to specify the cloud type and the necessary configurations for each cloud type. The available options are:
|
# cp dev.env .env
|
||||||
|
# Update the environment parameters as needed.
|
||||||
|
|
||||||
|
# 2. Generate the env.dart from this .env file:
|
||||||
|
# You can use the "Generate Env File" task in VSCode.
|
||||||
|
# Alternatively, execute the following commands:
|
||||||
|
# cd appflowy_flutter
|
||||||
|
# dart run build_runner clean && dart run build_runner build --delete-conflicting-outputs
|
||||||
|
|
||||||
|
|
||||||
|
# Cloud Type Configuration
|
||||||
|
# Use this configuration file to specify the cloud type and its associated settings. The available cloud types are:
|
||||||
# Local: 0
|
# Local: 0
|
||||||
# Supabase: 1
|
# Supabase: 1
|
||||||
# AppFlowy Cloud: 2
|
# AppFlowy Cloud: 2
|
||||||
|
# By default, it's set to Local.
|
||||||
CLOUD_TYPE=0
|
CLOUD_TYPE=0
|
||||||
|
|
||||||
# Supabase Configuration
|
# Supabase Configuration
|
||||||
# If you're using Supabase (CLOUD_TYPE=1), you need to provide the following configurations:
|
# If using Supabase (CLOUD_TYPE=1), provide the following details:
|
||||||
SUPABASE_URL=replace-with-your-supabase-url
|
SUPABASE_URL=replace-with-your-supabase-url
|
||||||
SUPABASE_ANON_KEY=replace-with-your-supabase-key
|
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 using AppFlowy Cloud (CLOUD_TYPE=2), provide the following details:
|
||||||
APPFLOWY_CLOUD_BASE_URL=replace-with-your-appflowy-cloud-url
|
APPFLOWY_CLOUD_BASE_URL=replace-with-your-appflowy-cloud-url
|
||||||
APPFLOWY_CLOUD_WS_BASE_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
|
APPFLOWY_CLOUD_GOTRUE_URL=replace-with-your-appflowy-cloud-gotrue-url
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,10 +1,7 @@
|
|||||||
import 'package:json_annotation/json_annotation.dart';
|
// ignore_for_file: non_constant_identifier_names
|
||||||
|
|
||||||
// Run `dart run build_runner build` to generate the json serialization If the
|
import 'package:json_annotation/json_annotation.dart';
|
||||||
// file `env_serde.g.dart` is existed, delete it first.
|
part 'backend_env.g.dart';
|
||||||
//
|
|
||||||
// the file `env_serde.g.dart` will be generated in the same directory.
|
|
||||||
part 'env_serde.g.dart';
|
|
||||||
|
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class AppFlowyEnv {
|
class AppFlowyEnv {
|
@ -1,8 +1,9 @@
|
|||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:appflowy/env/backend_env.dart';
|
||||||
import 'package:appflowy/env/env.dart';
|
import 'package:appflowy/env/env.dart';
|
||||||
import 'package:appflowy_backend/appflowy_backend.dart';
|
import 'package:appflowy_backend/appflowy_backend.dart';
|
||||||
import 'package:appflowy_backend/env_serde.dart';
|
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
@ -23,8 +24,10 @@ class InitRustSDKTask extends LaunchTask {
|
|||||||
Future<void> initialize(LaunchContext context) async {
|
Future<void> initialize(LaunchContext context) async {
|
||||||
final dir = directory ?? await appFlowyApplicationDataDirectory();
|
final dir = directory ?? await appFlowyApplicationDataDirectory();
|
||||||
|
|
||||||
|
// Pass the environment variables to the Rust SDK
|
||||||
final env = getAppFlowyEnv();
|
final env = getAppFlowyEnv();
|
||||||
context.getIt<FlowySDK>().setEnv(env);
|
context.getIt<FlowySDK>().setEnv(jsonEncode(env.toJson()));
|
||||||
|
|
||||||
await context.getIt<FlowySDK>().init(dir);
|
await context.getIt<FlowySDK>().init(dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,14 +100,9 @@ class UserBackendService {
|
|||||||
return Future.value(left([]));
|
return Future.value(left([]));
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<WorkspacePB, FlowyError>> openWorkspace(String workspaceId) {
|
Future<Either<Unit, FlowyError>> openWorkspace(String workspaceId) {
|
||||||
final request = WorkspaceIdPB.create()..value = workspaceId;
|
final payload = UserWorkspaceIdPB.create()..workspaceId = workspaceId;
|
||||||
return FolderEventOpenWorkspace(request).send().then((result) {
|
return UserEventOpenWorkspace(payload).send();
|
||||||
return result.fold(
|
|
||||||
(workspace) => left(workspace),
|
|
||||||
(error) => right(error),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Either<WorkspacePB, FlowyError>> getCurrentWorkspace() {
|
Future<Either<WorkspacePB, FlowyError>> getCurrentWorkspace() {
|
||||||
|
@ -101,6 +101,5 @@ Widget _renderCreateButton(BuildContext context) {
|
|||||||
|
|
||||||
// same method as in mobile
|
// same method as in mobile
|
||||||
void _popToWorkspace(BuildContext context, WorkspacePB workspace) {
|
void _popToWorkspace(BuildContext context, WorkspacePB workspace) {
|
||||||
context.read<WorkspaceBloc>().add(WorkspaceEvent.openWorkspace(workspace));
|
|
||||||
context.pop(workspace.id);
|
context.pop(workspace.id);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import 'package:easy_localization/easy_localization.dart';
|
|||||||
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
|
||||||
import 'package:flowy_infra_ui/widget/error_page.dart';
|
import 'package:flowy_infra_ui/widget/error_page.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
// TODO(yijing): needs refactor when multiple workspaces are supported
|
// TODO(yijing): needs refactor when multiple workspaces are supported
|
||||||
@ -139,6 +138,5 @@ class _MobileWorkspaceStartScreenState
|
|||||||
|
|
||||||
// same method as in desktop
|
// same method as in desktop
|
||||||
void _popToWorkspace(BuildContext context, WorkspacePB workspace) {
|
void _popToWorkspace(BuildContext context, WorkspacePB workspace) {
|
||||||
context.read<WorkspaceBloc>().add(WorkspaceEvent.openWorkspace(workspace));
|
|
||||||
context.pop(workspace.id);
|
context.pop(workspace.id);
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,6 @@ class WorkspaceBloc extends Bloc<WorkspaceEvent, WorkspaceState> {
|
|||||||
initial: (e) async {
|
initial: (e) async {
|
||||||
await _fetchWorkspaces(emit);
|
await _fetchWorkspaces(emit);
|
||||||
},
|
},
|
||||||
openWorkspace: (e) async {
|
|
||||||
await _openWorkspace(e.workspace, emit);
|
|
||||||
},
|
|
||||||
createWorkspace: (e) async {
|
createWorkspace: (e) async {
|
||||||
await _createWorkspace(e.name, e.desc, emit);
|
await _createWorkspace(e.name, e.desc, emit);
|
||||||
},
|
},
|
||||||
@ -57,22 +54,6 @@ class WorkspaceBloc extends Bloc<WorkspaceEvent, WorkspaceState> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _openWorkspace(
|
|
||||||
WorkspacePB workspace,
|
|
||||||
Emitter<WorkspaceState> emit,
|
|
||||||
) async {
|
|
||||||
final result = await userService.openWorkspace(workspace.id);
|
|
||||||
emit(
|
|
||||||
result.fold(
|
|
||||||
(workspaces) => state.copyWith(successOrFailure: left(unit)),
|
|
||||||
(error) {
|
|
||||||
Log.error(error);
|
|
||||||
return state.copyWith(successOrFailure: right(error));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _createWorkspace(
|
Future<void> _createWorkspace(
|
||||||
String name,
|
String name,
|
||||||
String desc,
|
String desc,
|
||||||
@ -98,8 +79,6 @@ class WorkspaceEvent with _$WorkspaceEvent {
|
|||||||
const factory WorkspaceEvent.initial() = Initial;
|
const factory WorkspaceEvent.initial() = Initial;
|
||||||
const factory WorkspaceEvent.createWorkspace(String name, String desc) =
|
const factory WorkspaceEvent.createWorkspace(String name, String desc) =
|
||||||
CreateWorkspace;
|
CreateWorkspace;
|
||||||
const factory WorkspaceEvent.openWorkspace(WorkspacePB workspace) =
|
|
||||||
OpenWorkspace;
|
|
||||||
const factory WorkspaceEvent.workspacesReveived(
|
const factory WorkspaceEvent.workspacesReveived(
|
||||||
Either<List<WorkspacePB>, FlowyError> workspacesOrFail,
|
Either<List<WorkspacePB>, FlowyError> workspacesOrFail,
|
||||||
) = WorkspacesReceived;
|
) = WorkspacesReceived;
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
export 'package:async/async.dart';
|
export 'package:async/async.dart';
|
||||||
import 'dart:convert';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'package:appflowy_backend/rust_stream.dart';
|
import 'package:appflowy_backend/rust_stream.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'dart:ffi';
|
import 'dart:ffi';
|
||||||
import 'env_serde.dart';
|
|
||||||
import 'ffi.dart' as ffi;
|
import 'ffi.dart' as ffi;
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
|
|
||||||
@ -37,8 +35,7 @@ class FlowySDK {
|
|||||||
ffi.init_sdk(sdkDir.path.toNativeUtf8());
|
ffi.init_sdk(sdkDir.path.toNativeUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
void setEnv(AppFlowyEnv env) {
|
void setEnv(String envStr) {
|
||||||
final jsonStr = jsonEncode(env.toJson());
|
ffi.set_env(envStr.toNativeUtf8());
|
||||||
ffi.set_env(jsonStr.toNativeUtf8());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
76
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
76
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -135,6 +135,21 @@ version = "1.0.75"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "app-error"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
|
"thiserror",
|
||||||
|
"url",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "appflowy_tauri"
|
name = "appflowy_tauri"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
@ -445,7 +460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"borsh-derive",
|
"borsh-derive",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -753,9 +768,10 @@ 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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
@ -845,7 +861,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -864,7 +880,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -894,7 +910,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -906,7 +922,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -926,7 +942,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -940,7 +956,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -982,7 +998,7 @@ dependencies = [
|
|||||||
[[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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -1003,7 +1019,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1030,7 +1046,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1429,9 +1445,10 @@ 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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"chrono",
|
"chrono",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"serde",
|
"serde",
|
||||||
@ -2064,7 +2081,7 @@ dependencies = [
|
|||||||
"nanoid",
|
"nanoid",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"scraper 0.18.0",
|
"scraper 0.18.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"strum_macros 0.21.1",
|
"strum_macros 0.21.1",
|
||||||
@ -2778,7 +2795,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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2794,9 +2811,10 @@ dependencies = [
|
|||||||
[[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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -3229,7 +3247,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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -3479,7 +3497,6 @@ dependencies = [
|
|||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-bunyan-formatter",
|
"tracing-bunyan-formatter",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.2.0",
|
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4904,8 +4921,9 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
@ -5358,9 +5376,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scraper"
|
name = "scraper"
|
||||||
version = "0.18.0"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3693f9a0203d49a7ba8f38aa915316b3d535c1862d03dae7009cb71a3408b36a"
|
checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
"cssparser 0.31.2",
|
"cssparser 0.31.2",
|
||||||
@ -5642,9 +5660,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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"database-entity",
|
"database-entity",
|
||||||
"gotrue-entity",
|
"gotrue-entity",
|
||||||
@ -6689,7 +6708,7 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.1.3",
|
"tracing-log",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6714,17 +6733,6 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-log"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"once_cell",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-serde"
|
name = "tracing-serde"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -6752,7 +6760,7 @@ dependencies = [
|
|||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.1.3",
|
"tracing-log",
|
||||||
"tracing-serde",
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -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 = "c87d0f05e988a02e9272a42722b304289be320e4" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c739029d99517282d2ec1593523a47b51a85231b" }
|
||||||
# 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 = "c87
|
|||||||
# 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 = "01f92f7bb" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,13 +15,11 @@ import {
|
|||||||
SignInPayloadPB,
|
SignInPayloadPB,
|
||||||
SignUpPayloadPB,
|
SignUpPayloadPB,
|
||||||
UpdateUserProfilePayloadPB,
|
UpdateUserProfilePayloadPB,
|
||||||
WorkspaceIdPB,
|
|
||||||
WorkspacePB,
|
WorkspacePB,
|
||||||
WorkspaceSettingPB,
|
WorkspaceSettingPB,
|
||||||
} from '@/services/backend';
|
} from '@/services/backend';
|
||||||
import {
|
import {
|
||||||
FolderEventCreateWorkspace,
|
FolderEventCreateWorkspace,
|
||||||
FolderEventOpenWorkspace,
|
|
||||||
FolderEventGetCurrentWorkspaceSetting,
|
FolderEventGetCurrentWorkspaceSetting,
|
||||||
FolderEventReadCurrentWorkspace,
|
FolderEventReadCurrentWorkspace,
|
||||||
} from '@/services/backend/events/flowy-folder2';
|
} from '@/services/backend/events/flowy-folder2';
|
||||||
@ -67,12 +65,6 @@ export class UserBackendService {
|
|||||||
return FolderEventReadCurrentWorkspace();
|
return FolderEventReadCurrentWorkspace();
|
||||||
};
|
};
|
||||||
|
|
||||||
openWorkspace = (workspaceId: string) => {
|
|
||||||
const payload = WorkspaceIdPB.fromObject({ value: workspaceId });
|
|
||||||
|
|
||||||
return FolderEventOpenWorkspace(payload);
|
|
||||||
};
|
|
||||||
|
|
||||||
createWorkspace = async (params: { name: string; desc: string }): Promise<WorkspacePB> => {
|
createWorkspace = async (params: { name: string; desc: string }): Promise<WorkspacePB> => {
|
||||||
const payload = CreateWorkspacePayloadPB.fromObject({ name: params.name, desc: params.desc });
|
const payload = CreateWorkspacePayloadPB.fromObject({ name: params.name, desc: params.desc });
|
||||||
const result = await FolderEventCreateWorkspace(payload);
|
const result = await FolderEventCreateWorkspace(payload);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
FolderEventCreateWorkspace,
|
FolderEventCreateWorkspace,
|
||||||
CreateWorkspacePayloadPB,
|
CreateWorkspacePayloadPB,
|
||||||
FolderEventOpenWorkspace,
|
|
||||||
FolderEventDeleteWorkspace,
|
FolderEventDeleteWorkspace,
|
||||||
WorkspaceIdPB,
|
WorkspaceIdPB,
|
||||||
FolderEventReadWorkspaceViews,
|
FolderEventReadWorkspaceViews,
|
||||||
FolderEventReadCurrentWorkspace,
|
FolderEventReadCurrentWorkspace,
|
||||||
} from '@/services/backend/events/flowy-folder2';
|
} from '@/services/backend/events/flowy-folder2';
|
||||||
|
import { UserEventOpenWorkspace, UserWorkspaceIdPB } from '@/services/backend/events/flowy-user';
|
||||||
|
|
||||||
export class WorkspaceBackendService {
|
export class WorkspaceBackendService {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -24,11 +24,11 @@ export class WorkspaceBackendService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
openWorkspace = async (workspaceId: string) => {
|
openWorkspace = async (workspaceId: string) => {
|
||||||
const payload = new WorkspaceIdPB({
|
const payload = new UserWorkspaceIdPB({
|
||||||
value: workspaceId,
|
workspace_id: workspaceId,
|
||||||
});
|
});
|
||||||
|
|
||||||
return FolderEventOpenWorkspace(payload);
|
return UserEventOpenWorkspace(payload);
|
||||||
};
|
};
|
||||||
|
|
||||||
deleteWorkspace = async (workspaceId: string) => {
|
deleteWorkspace = async (workspaceId: string) => {
|
||||||
|
80
frontend/rust-lib/Cargo.lock
generated
80
frontend/rust-lib/Cargo.lock
generated
@ -121,6 +121,21 @@ version = "1.0.75"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "app-error"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"serde_repr",
|
||||||
|
"thiserror",
|
||||||
|
"url",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayvec"
|
name = "arrayvec"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -452,7 +467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"borsh-derive",
|
"borsh-derive",
|
||||||
"hashbrown 0.12.3",
|
"hashbrown 0.13.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -651,9 +666,10 @@ 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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
@ -712,7 +728,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -731,7 +747,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -761,7 +777,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -773,7 +789,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -793,7 +809,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "collab-entity"
|
name = "collab-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@ -807,7 +823,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -849,7 +865,7 @@ dependencies = [
|
|||||||
[[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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"bincode",
|
"bincode",
|
||||||
@ -870,7 +886,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -897,7 +913,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=01f92f7bb#01f92f7bb204a3aeed24b345d504dfd36d3d9fcb"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=bab20052#bab200529ef0306194fa8618cc8708878b01ce04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"collab",
|
"collab",
|
||||||
@ -1256,9 +1272,10 @@ 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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"chrono",
|
"chrono",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"serde",
|
"serde",
|
||||||
@ -1885,7 +1902,7 @@ dependencies = [
|
|||||||
"nanoid",
|
"nanoid",
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"protobuf",
|
"protobuf",
|
||||||
"scraper 0.18.0",
|
"scraper 0.18.1",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"strum_macros 0.21.1",
|
"strum_macros 0.21.1",
|
||||||
@ -2437,7 +2454,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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -2453,9 +2470,10 @@ dependencies = [
|
|||||||
[[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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"jsonwebtoken",
|
"jsonwebtoken",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -2813,7 +2831,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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
@ -2979,7 +2997,6 @@ dependencies = [
|
|||||||
"tracing-appender",
|
"tracing-appender",
|
||||||
"tracing-bunyan-formatter",
|
"tracing-bunyan-formatter",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.2.0",
|
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -4254,8 +4271,9 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "realtime-entity"
|
name = "realtime-entity"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
"collab",
|
"collab",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
@ -4705,9 +4723,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scraper"
|
name = "scraper"
|
||||||
version = "0.18.0"
|
version = "0.18.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3693f9a0203d49a7ba8f38aa915316b3d535c1862d03dae7009cb71a3408b36a"
|
checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.3",
|
"ahash 0.8.3",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
@ -4810,9 +4828,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.105"
|
version = "1.0.108"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
|
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
@ -4891,9 +4909,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=c87d0f05e988a02e9272a42722b304289be320e4#c87d0f05e988a02e9272a42722b304289be320e4"
|
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Cloud?rev=c739029d99517282d2ec1593523a47b51a85231b#c739029d99517282d2ec1593523a47b51a85231b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
"app-error",
|
||||||
"collab-entity",
|
"collab-entity",
|
||||||
"database-entity",
|
"database-entity",
|
||||||
"gotrue-entity",
|
"gotrue-entity",
|
||||||
@ -5654,7 +5673,7 @@ dependencies = [
|
|||||||
"time",
|
"time",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.1.3",
|
"tracing-log",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -5679,17 +5698,6 @@ dependencies = [
|
|||||||
"tracing-core",
|
"tracing-core",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-log"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"once_cell",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing-serde"
|
name = "tracing-serde"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -5717,7 +5725,7 @@ dependencies = [
|
|||||||
"thread_local",
|
"thread_local",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-core",
|
"tracing-core",
|
||||||
"tracing-log 0.1.3",
|
"tracing-log",
|
||||||
"tracing-serde",
|
"tracing-serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -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 = "c87d0f05e988a02e9272a42722b304289be320e4" }
|
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c739029d99517282d2ec1593523a47b51a85231b" }
|
||||||
# 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 = "c87
|
|||||||
# 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 = "01f92f7bb" }
|
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
|
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
|
||||||
|
@ -144,19 +144,10 @@ pub struct ViewTest {
|
|||||||
pub workspace: WorkspacePB,
|
pub workspace: WorkspacePB,
|
||||||
pub child_view: ViewPB,
|
pub child_view: ViewPB,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ViewTest {
|
impl ViewTest {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn new(sdk: &EventIntegrationTest, layout: ViewLayoutPB, data: Vec<u8>) -> Self {
|
pub async fn new(sdk: &EventIntegrationTest, layout: ViewLayoutPB, data: Vec<u8>) -> Self {
|
||||||
let workspace = sdk.folder_manager.get_current_workspace().await.unwrap();
|
let workspace = sdk.folder_manager.get_current_workspace().await.unwrap();
|
||||||
let payload = WorkspaceIdPB {
|
|
||||||
value: workspace.id.clone(),
|
|
||||||
};
|
|
||||||
let _ = EventBuilder::new(sdk.clone())
|
|
||||||
.event(OpenWorkspace)
|
|
||||||
.payload(payload)
|
|
||||||
.async_send()
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let payload = CreateViewPayloadPB {
|
let payload = CreateViewPayloadPB {
|
||||||
parent_view_id: workspace.id.clone(),
|
parent_view_id: workspace.id.clone(),
|
||||||
|
@ -22,7 +22,7 @@ async fn af_cloud_edit_document_test() {
|
|||||||
let rx = test
|
let rx = test
|
||||||
.notification_sender
|
.notification_sender
|
||||||
.subscribe_with_condition::<DocumentSyncStatePB, _>(&document_id, |pb| pb.is_finish);
|
.subscribe_with_condition::<DocumentSyncStatePB, _>(&document_id, |pb| pb.is_finish);
|
||||||
receive_with_timeout(rx, Duration::from_secs(15))
|
receive_with_timeout(rx, Duration::from_secs(25))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ use std::sync::{Arc, Weak};
|
|||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use serde_repr::*;
|
use serde_repr::*;
|
||||||
|
|
||||||
use collab_integrate::YrsDocAction;
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_server::af_cloud::AFCloudServer;
|
use flowy_server::af_cloud::AFCloudServer;
|
||||||
use flowy_server::local_server::{LocalServer, LocalServerDB};
|
use flowy_server::local_server::{LocalServer, LocalServerDB};
|
||||||
@ -14,9 +13,7 @@ use flowy_server::{AppFlowyEncryption, AppFlowyServer, EncryptionImpl};
|
|||||||
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
use flowy_server_config::af_cloud_config::AFCloudConfiguration;
|
||||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||||
use flowy_sqlite::kv::StorePreferences;
|
use flowy_sqlite::kv::StorePreferences;
|
||||||
use flowy_user::services::database::{
|
use flowy_user::services::database::{get_user_profile, get_user_workspace, open_user_db};
|
||||||
get_user_profile, get_user_workspace, open_collab_db, open_user_db,
|
|
||||||
};
|
|
||||||
use flowy_user_deps::cloud::UserCloudService;
|
use flowy_user_deps::cloud::UserCloudService;
|
||||||
use flowy_user_deps::entities::*;
|
use flowy_user_deps::entities::*;
|
||||||
|
|
||||||
@ -195,14 +192,4 @@ impl LocalServerDB for LocalServerDBImpl {
|
|||||||
let user_workspace = get_user_workspace(&sqlite_db, uid)?;
|
let user_workspace = get_user_workspace(&sqlite_db, uid)?;
|
||||||
Ok(user_workspace)
|
Ok(user_workspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_collab_updates(&self, uid: i64, object_id: &str) -> Result<Vec<Vec<u8>>, FlowyError> {
|
|
||||||
let collab_db = open_collab_db(&self.storage_path, uid)?;
|
|
||||||
let read_txn = collab_db.read_txn();
|
|
||||||
let updates = read_txn.get_all_updates(uid, object_id).map_err(|e| {
|
|
||||||
FlowyError::internal().with_context(format!("Failed to open collab db: {:?}", e))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
Ok(updates)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,9 @@ use flowy_database_deps::cloud::{
|
|||||||
use flowy_document2::deps::DocumentData;
|
use flowy_document2::deps::DocumentData;
|
||||||
use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
|
use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
|
||||||
use flowy_error::FlowyError;
|
use flowy_error::FlowyError;
|
||||||
use flowy_folder_deps::cloud::{FolderCloudService, FolderData, FolderSnapshot, Workspace};
|
use flowy_folder_deps::cloud::{
|
||||||
|
FolderCloudService, FolderData, FolderSnapshot, Workspace, WorkspaceRecord,
|
||||||
|
};
|
||||||
use flowy_storage::{FileStorageService, StorageObject};
|
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;
|
||||||
@ -140,6 +142,17 @@ impl FolderCloudService for ServerProvider {
|
|||||||
FutureResult::new(async move { server?.folder_service().create_workspace(uid, &name).await })
|
FutureResult::new(async move { server?.folder_service().create_workspace(uid, &name).await })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_workspace(&self, workspace_id: &str) -> FutureResult<(), Error> {
|
||||||
|
let workspace_id = workspace_id.to_string();
|
||||||
|
let server = self.get_server(&self.get_server_type());
|
||||||
|
FutureResult::new(async move { server?.folder_service().open_workspace(&workspace_id).await })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> {
|
||||||
|
let server = self.get_server(&self.get_server_type());
|
||||||
|
FutureResult::new(async move { server?.folder_service().get_all_workspace().await })
|
||||||
|
}
|
||||||
|
|
||||||
fn get_folder_data(
|
fn get_folder_data(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
@ -245,7 +258,7 @@ impl DocumentCloudService for ServerProvider {
|
|||||||
&self,
|
&self,
|
||||||
document_id: &str,
|
document_id: &str,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
) -> FutureResult<Vec<Vec<u8>>, Error> {
|
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
|
||||||
let workspace_id = workspace_id.to_string();
|
let workspace_id = workspace_id.to_string();
|
||||||
let document_id = document_id.to_string();
|
let document_id = document_id.to_string();
|
||||||
let server = self.get_server(&self.get_server_type());
|
let server = self.get_server(&self.get_server_type());
|
||||||
@ -308,7 +321,7 @@ impl CollabStorageProvider for ServerProvider {
|
|||||||
to_fut(async move {
|
to_fut(async move {
|
||||||
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
|
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
|
||||||
match server.collab_ws_channel(&collab_object.object_id).await {
|
match server.collab_ws_channel(&collab_object.object_id).await {
|
||||||
Ok(Some((channel, ws_connect_state))) => {
|
Ok(Some((channel, ws_connect_state, is_connected))) => {
|
||||||
let origin = CollabOrigin::Client(CollabClient::new(
|
let origin = CollabOrigin::Client(CollabClient::new(
|
||||||
collab_object.uid,
|
collab_object.uid,
|
||||||
collab_object.device_id.clone(),
|
collab_object.device_id.clone(),
|
||||||
@ -316,7 +329,7 @@ impl CollabStorageProvider for ServerProvider {
|
|||||||
let sync_object = SyncObject::from(collab_object);
|
let sync_object = SyncObject::from(collab_object);
|
||||||
let (sink, stream) = (channel.sink(), channel.stream());
|
let (sink, stream) = (channel.sink(), channel.stream());
|
||||||
let sink_config = SinkConfig::new()
|
let sink_config = SinkConfig::new()
|
||||||
.send_timeout(6)
|
.send_timeout(8)
|
||||||
.with_strategy(SinkStrategy::FixInterval(Duration::from_secs(2)));
|
.with_strategy(SinkStrategy::FixInterval(Duration::from_secs(2)));
|
||||||
let sync_plugin = SyncPlugin::new(
|
let sync_plugin = SyncPlugin::new(
|
||||||
origin,
|
origin,
|
||||||
@ -326,6 +339,7 @@ impl CollabStorageProvider for ServerProvider {
|
|||||||
sink_config,
|
sink_config,
|
||||||
stream,
|
stream,
|
||||||
Some(channel),
|
Some(channel),
|
||||||
|
!is_connected,
|
||||||
ws_connect_state,
|
ws_connect_state,
|
||||||
);
|
);
|
||||||
plugins.push(Arc::new(sync_plugin));
|
plugins.push(Arc::new(sync_plugin));
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
use tracing::event;
|
||||||
|
|
||||||
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;
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
use flowy_folder2::manager::{FolderInitializeDataSource, FolderManager};
|
use flowy_folder2::manager::{FolderInitDataSource, FolderManager};
|
||||||
use flowy_user::event_map::{UserCloudServiceProvider, UserStatusCallback};
|
use flowy_user::event_map::{UserCloudServiceProvider, UserStatusCallback};
|
||||||
use flowy_user_deps::cloud::UserCloudConfig;
|
use flowy_user_deps::cloud::UserCloudConfig;
|
||||||
use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace};
|
use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace};
|
||||||
@ -59,7 +60,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
.initialize(
|
.initialize(
|
||||||
user_id,
|
user_id,
|
||||||
&user_workspace.id,
|
&user_workspace.id,
|
||||||
FolderInitializeDataSource::LocalDisk {
|
FolderInitDataSource::LocalDisk {
|
||||||
create_if_not_exist: false,
|
create_if_not_exist: false,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@ -82,8 +83,9 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
&self,
|
&self,
|
||||||
user_id: i64,
|
user_id: i64,
|
||||||
user_workspace: &UserWorkspace,
|
user_workspace: &UserWorkspace,
|
||||||
_device_id: &str,
|
device_id: &str,
|
||||||
) -> Fut<FlowyResult<()>> {
|
) -> Fut<FlowyResult<()>> {
|
||||||
|
let device_id = device_id.to_owned();
|
||||||
let user_id = user_id.to_owned();
|
let user_id = user_id.to_owned();
|
||||||
let user_workspace = user_workspace.clone();
|
let user_workspace = user_workspace.clone();
|
||||||
let folder_manager = self.folder_manager.clone();
|
let folder_manager = self.folder_manager.clone();
|
||||||
@ -91,6 +93,13 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
let document_manager = self.document_manager.clone();
|
let document_manager = self.document_manager.clone();
|
||||||
|
|
||||||
to_fut(async move {
|
to_fut(async move {
|
||||||
|
event!(
|
||||||
|
tracing::Level::TRACE,
|
||||||
|
"Notify did sign in: latest_workspace: {:?}, device_id: {}",
|
||||||
|
user_workspace,
|
||||||
|
device_id
|
||||||
|
);
|
||||||
|
|
||||||
folder_manager
|
folder_manager
|
||||||
.initialize_with_workspace_id(user_id, &user_workspace.id)
|
.initialize_with_workspace_id(user_id, &user_workspace.id)
|
||||||
.await?;
|
.await?;
|
||||||
@ -113,8 +122,9 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
is_new_user: bool,
|
is_new_user: bool,
|
||||||
user_profile: &UserProfile,
|
user_profile: &UserProfile,
|
||||||
user_workspace: &UserWorkspace,
|
user_workspace: &UserWorkspace,
|
||||||
_device_id: &str,
|
device_id: &str,
|
||||||
) -> Fut<FlowyResult<()>> {
|
) -> Fut<FlowyResult<()>> {
|
||||||
|
let device_id = device_id.to_owned();
|
||||||
let user_profile = user_profile.clone();
|
let user_profile = user_profile.clone();
|
||||||
let folder_manager = self.folder_manager.clone();
|
let folder_manager = self.folder_manager.clone();
|
||||||
let database_manager = self.database_manager.clone();
|
let database_manager = self.database_manager.clone();
|
||||||
@ -122,12 +132,20 @@ impl UserStatusCallback for UserStatusCallbackImpl {
|
|||||||
let document_manager = self.document_manager.clone();
|
let document_manager = self.document_manager.clone();
|
||||||
|
|
||||||
to_fut(async move {
|
to_fut(async move {
|
||||||
|
event!(
|
||||||
|
tracing::Level::TRACE,
|
||||||
|
"Notify did sign up: is new: {} user_workspace: {:?}, device_id: {}",
|
||||||
|
is_new_user,
|
||||||
|
user_workspace,
|
||||||
|
device_id
|
||||||
|
);
|
||||||
|
|
||||||
folder_manager
|
folder_manager
|
||||||
.initialize_with_new_user(
|
.initialize_with_new_user(
|
||||||
user_profile.uid,
|
user_profile.uid,
|
||||||
&user_profile.token,
|
&user_profile.token,
|
||||||
is_new_user,
|
is_new_user,
|
||||||
FolderInitializeDataSource::LocalDisk {
|
FolderInitDataSource::LocalDisk {
|
||||||
create_if_not_exist: true,
|
create_if_not_exist: true,
|
||||||
},
|
},
|
||||||
&user_workspace.id,
|
&user_workspace.id,
|
||||||
|
@ -196,7 +196,6 @@ impl AppFlowyCore {
|
|||||||
|
|
||||||
let cloned_user_session = Arc::downgrade(&user_manager);
|
let cloned_user_session = Arc::downgrade(&user_manager);
|
||||||
if let Some(user_session) = cloned_user_session.upgrade() {
|
if let Some(user_session) = cloned_user_session.upgrade() {
|
||||||
event!(tracing::Level::DEBUG, "init user session",);
|
|
||||||
if let Err(err) = user_session
|
if let Err(err) = user_session
|
||||||
.init(user_status_callback, collab_interact_impl)
|
.init(user_status_callback, collab_interact_impl)
|
||||||
.await
|
.await
|
||||||
|
@ -13,7 +13,7 @@ use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLay
|
|||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tracing::{instrument, trace};
|
use tracing::{event, instrument, trace};
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
|
||||||
@ -80,6 +80,11 @@ impl DatabaseManager {
|
|||||||
workspace_id: String,
|
workspace_id: String,
|
||||||
database_views_aggregate_id: String,
|
database_views_aggregate_id: String,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
|
// Clear all existing tasks
|
||||||
|
self.task_scheduler.write().await.clear_task();
|
||||||
|
// Release all existing editors
|
||||||
|
self.editors.write().await.clear();
|
||||||
|
|
||||||
let collab_db = self.user.collab_db(uid)?;
|
let collab_db = self.user.collab_db(uid)?;
|
||||||
let collab_builder = UserDatabaseCollabServiceImpl {
|
let collab_builder = UserDatabaseCollabServiceImpl {
|
||||||
workspace_id: workspace_id.clone(),
|
workspace_id: workspace_id.clone(),
|
||||||
@ -114,7 +119,11 @@ impl DatabaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct the workspace database.
|
// Construct the workspace database.
|
||||||
trace!("open workspace database: {}", &database_views_aggregate_id);
|
event!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"open aggregate database views object: {}",
|
||||||
|
&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,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
pub use collab_document::blocks::DocumentData;
|
pub use collab_document::blocks::DocumentData;
|
||||||
|
|
||||||
|
use flowy_error::FlowyError;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
/// A trait for document cloud service.
|
/// A trait for document cloud service.
|
||||||
@ -11,7 +12,7 @@ pub trait DocumentCloudService: Send + Sync + 'static {
|
|||||||
&self,
|
&self,
|
||||||
document_id: &str,
|
document_id: &str,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
) -> FutureResult<Vec<Vec<u8>>, Error>;
|
) -> FutureResult<Vec<Vec<u8>>, FlowyError>;
|
||||||
|
|
||||||
fn get_document_snapshots(
|
fn get_document_snapshots(
|
||||||
&self,
|
&self,
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use std::sync::Weak;
|
use std::sync::Weak;
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
use collab::core::collab::MutexCollab;
|
use collab::core::collab::{CollabRawData, MutexCollab};
|
||||||
use collab_document::blocks::DocumentData;
|
use collab_document::blocks::DocumentData;
|
||||||
use collab_document::document::Document;
|
use collab_document::document::Document;
|
||||||
use collab_document::document_data::default_document_data;
|
use collab_document::document_data::{default_document_collab_data, default_document_data};
|
||||||
use collab_document::YrsDocAction;
|
use collab_document::YrsDocAction;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use parking_lot::RwLock;
|
use parking_lot::RwLock;
|
||||||
use tracing::instrument;
|
use tracing::{event, instrument};
|
||||||
|
|
||||||
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
|
||||||
use collab_integrate::RocksCollabDB;
|
use collab_integrate::RocksCollabDB;
|
||||||
@ -109,10 +109,29 @@ impl DocumentManager {
|
|||||||
let mut updates = vec![];
|
let mut updates = vec![];
|
||||||
if !self.is_doc_exist(doc_id)? {
|
if !self.is_doc_exist(doc_id)? {
|
||||||
// Try to get the document from the cloud service
|
// Try to get the document from the cloud service
|
||||||
updates = self
|
let result: Result<CollabRawData, FlowyError> = self
|
||||||
.cloud_service
|
.cloud_service
|
||||||
.get_document_updates(&self.user.workspace_id()?, doc_id)
|
.get_document_updates(doc_id, &self.user.workspace_id()?)
|
||||||
.await?;
|
.await;
|
||||||
|
|
||||||
|
updates = match result {
|
||||||
|
Ok(data) => data,
|
||||||
|
Err(err) => {
|
||||||
|
if err.is_record_not_found() {
|
||||||
|
// The document's ID exists in the cloud, but its content does not.
|
||||||
|
// This occurs when user A's document hasn't finished syncing and user B tries to open it.
|
||||||
|
// As a result, a blank document is created for user B.
|
||||||
|
event!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"can't find the document in the cloud, doc_id: {}",
|
||||||
|
doc_id
|
||||||
|
);
|
||||||
|
default_document_collab_data(doc_id)
|
||||||
|
} else {
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let uid = self.user.user_id()?;
|
let uid = self.user.user_id()?;
|
||||||
|
@ -129,7 +129,7 @@ impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
|
|||||||
&self,
|
&self,
|
||||||
_document_id: &str,
|
_document_id: &str,
|
||||||
_workspace_id: &str,
|
_workspace_id: &str,
|
||||||
) -> FutureResult<Vec<Vec<u8>>, Error> {
|
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
|
||||||
FutureResult::new(async move { Ok(vec![]) })
|
FutureResult::new(async move { Ok(vec![]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,9 +98,6 @@ pub enum ErrorCode {
|
|||||||
#[error("user id is empty or whitespace")]
|
#[error("user id is empty or whitespace")]
|
||||||
UserIdInvalid = 30,
|
UserIdInvalid = 30,
|
||||||
|
|
||||||
#[error("User not exist")]
|
|
||||||
UserNotExist = 31,
|
|
||||||
|
|
||||||
#[error("Text is too long")]
|
#[error("Text is too long")]
|
||||||
TextTooLong = 32,
|
TextTooLong = 32,
|
||||||
|
|
||||||
|
@ -55,6 +55,10 @@ impl FlowyError {
|
|||||||
self.code == ErrorCode::RecordNotFound
|
self.code == ErrorCode::RecordNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_unauthorized(&self) -> bool {
|
||||||
|
self.code == ErrorCode::UserUnauthorized || self.code == ErrorCode::RecordNotFound
|
||||||
|
}
|
||||||
|
|
||||||
static_flowy_error!(internal, ErrorCode::Internal);
|
static_flowy_error!(internal, ErrorCode::Internal);
|
||||||
static_flowy_error!(record_not_found, ErrorCode::RecordNotFound);
|
static_flowy_error!(record_not_found, ErrorCode::RecordNotFound);
|
||||||
static_flowy_error!(workspace_name, ErrorCode::WorkspaceNameInvalid);
|
static_flowy_error!(workspace_name, ErrorCode::WorkspaceNameInvalid);
|
||||||
@ -87,7 +91,6 @@ impl FlowyError {
|
|||||||
);
|
);
|
||||||
static_flowy_error!(name_empty, ErrorCode::UserNameIsEmpty);
|
static_flowy_error!(name_empty, ErrorCode::UserNameIsEmpty);
|
||||||
static_flowy_error!(user_id, ErrorCode::UserIdInvalid);
|
static_flowy_error!(user_id, ErrorCode::UserIdInvalid);
|
||||||
static_flowy_error!(user_not_exist, ErrorCode::UserNotExist);
|
|
||||||
static_flowy_error!(text_too_long, ErrorCode::TextTooLong);
|
static_flowy_error!(text_too_long, ErrorCode::TextTooLong);
|
||||||
static_flowy_error!(invalid_data, ErrorCode::InvalidParams);
|
static_flowy_error!(invalid_data, ErrorCode::InvalidParams);
|
||||||
static_flowy_error!(out_of_bounds, ErrorCode::OutOfBounds);
|
static_flowy_error!(out_of_bounds, ErrorCode::OutOfBounds);
|
||||||
|
@ -1,26 +1,25 @@
|
|||||||
use client_api::error::AppError;
|
use client_api::error::{AppResponseError, ErrorCode as AppErrorCode};
|
||||||
|
|
||||||
use crate::{ErrorCode, FlowyError};
|
use crate::{ErrorCode, FlowyError};
|
||||||
|
|
||||||
impl From<AppError> for FlowyError {
|
impl From<AppResponseError> for FlowyError {
|
||||||
fn from(error: AppError) -> Self {
|
fn from(error: AppResponseError) -> Self {
|
||||||
let code = match error.code {
|
let code = match error.code {
|
||||||
client_api::error::ErrorCode::Ok => ErrorCode::Internal,
|
AppErrorCode::Ok => ErrorCode::Internal,
|
||||||
client_api::error::ErrorCode::Unhandled => ErrorCode::Internal,
|
AppErrorCode::Unhandled => ErrorCode::Internal,
|
||||||
client_api::error::ErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
AppErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
|
||||||
client_api::error::ErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
|
AppErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
|
||||||
client_api::error::ErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
|
AppErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
|
||||||
client_api::error::ErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
|
AppErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
|
||||||
client_api::error::ErrorCode::OAuthError => ErrorCode::UserUnauthorized,
|
AppErrorCode::OAuthError => ErrorCode::UserUnauthorized,
|
||||||
client_api::error::ErrorCode::MissingPayload => ErrorCode::MissingPayload,
|
AppErrorCode::MissingPayload => ErrorCode::MissingPayload,
|
||||||
client_api::error::ErrorCode::OpenError => ErrorCode::Internal,
|
AppErrorCode::OpenError => ErrorCode::Internal,
|
||||||
client_api::error::ErrorCode::InvalidUrl => ErrorCode::InvalidURL,
|
AppErrorCode::InvalidUrl => ErrorCode::InvalidURL,
|
||||||
client_api::error::ErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
|
AppErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
|
||||||
client_api::error::ErrorCode::UrlMissingParameter => ErrorCode::InvalidParams,
|
AppErrorCode::UrlMissingParameter => ErrorCode::InvalidParams,
|
||||||
client_api::error::ErrorCode::InvalidOAuthProvider => ErrorCode::InvalidAuthConfig,
|
AppErrorCode::InvalidOAuthProvider => ErrorCode::InvalidAuthConfig,
|
||||||
client_api::error::ErrorCode::NotLoggedIn => ErrorCode::UserUnauthorized,
|
AppErrorCode::NotLoggedIn => ErrorCode::UserUnauthorized,
|
||||||
client_api::error::ErrorCode::NotEnoughPermissions => ErrorCode::NotEnoughPermissions,
|
AppErrorCode::NotEnoughPermissions => ErrorCode::NotEnoughPermissions,
|
||||||
client_api::error::ErrorCode::UserNameIsEmpty => ErrorCode::UserNameIsEmpty,
|
|
||||||
_ => ErrorCode::Internal,
|
_ => ErrorCode::Internal,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,8 +6,16 @@ use lib_infra::future::FutureResult;
|
|||||||
|
|
||||||
/// [FolderCloudService] represents the cloud service for folder.
|
/// [FolderCloudService] represents the cloud service for folder.
|
||||||
pub trait FolderCloudService: Send + Sync + 'static {
|
pub trait FolderCloudService: Send + Sync + 'static {
|
||||||
|
/// Creates a new workspace for the user.
|
||||||
|
/// Returns error if the cloud service doesn't support multiple workspaces
|
||||||
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, Error>;
|
fn create_workspace(&self, uid: i64, name: &str) -> FutureResult<Workspace, Error>;
|
||||||
|
|
||||||
|
fn open_workspace(&self, workspace_id: &str) -> FutureResult<(), Error>;
|
||||||
|
|
||||||
|
/// Returns all workspaces of the user.
|
||||||
|
/// Returns vec![] if the cloud service doesn't support multiple workspaces
|
||||||
|
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error>;
|
||||||
|
|
||||||
fn get_folder_data(
|
fn get_folder_data(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
@ -39,3 +47,10 @@ pub fn gen_workspace_id() -> Uuid {
|
|||||||
pub fn gen_view_id() -> Uuid {
|
pub fn gen_view_id() -> Uuid {
|
||||||
uuid::Uuid::new_v4()
|
uuid::Uuid::new_v4()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WorkspaceRecord {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub created_at: i64,
|
||||||
|
}
|
||||||
|
@ -38,6 +38,14 @@ pub(crate) async fn create_workspace_handler(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(level = "debug", skip_all, err)]
|
||||||
|
pub(crate) async fn get_all_workspace_handler(
|
||||||
|
_data: AFPluginData<CreateWorkspacePayloadPB>,
|
||||||
|
_folder: AFPluginState<Weak<FolderManager>>,
|
||||||
|
) -> DataResult<RepeatedWorkspacePB, FlowyError> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder), err)]
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
pub(crate) async fn get_workspace_views_handler(
|
pub(crate) async fn get_workspace_views_handler(
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
@ -48,32 +56,12 @@ pub(crate) async fn get_workspace_views_handler(
|
|||||||
data_result_ok(repeated_view)
|
data_result_ok(repeated_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, folder), err)]
|
|
||||||
pub(crate) async fn open_workspace_handler(
|
|
||||||
data: AFPluginData<WorkspaceIdPB>,
|
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
|
||||||
) -> DataResult<WorkspacePB, FlowyError> {
|
|
||||||
let folder = upgrade_folder(folder)?;
|
|
||||||
let workspace_id = data.into_inner().value;
|
|
||||||
if workspace_id.is_empty() {
|
|
||||||
Err(FlowyError::workspace_id().with_context("workspace id should not be empty"))
|
|
||||||
} else {
|
|
||||||
let workspace = folder.open_workspace(&workspace_id).await?;
|
|
||||||
let views = folder.get_workspace_views(&workspace_id).await?;
|
|
||||||
let workspace_pb: WorkspacePB = (workspace, views).into();
|
|
||||||
data_result_ok(workspace_pb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(folder), err)]
|
#[tracing::instrument(level = "debug", skip(folder), err)]
|
||||||
pub(crate) async fn read_current_workspace_setting_handler(
|
pub(crate) async fn read_current_workspace_setting_handler(
|
||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
) -> DataResult<WorkspaceSettingPB, FlowyError> {
|
) -> DataResult<WorkspaceSettingPB, FlowyError> {
|
||||||
let folder = upgrade_folder(folder)?;
|
let folder = upgrade_folder(folder)?;
|
||||||
let setting = folder
|
let setting = folder.get_workspace_setting_pb().await?;
|
||||||
.get_workspace_setting_pb()
|
|
||||||
.await
|
|
||||||
.ok_or(FlowyError::record_not_found())?;
|
|
||||||
data_result_ok(setting)
|
data_result_ok(setting)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -82,10 +70,7 @@ pub(crate) async fn read_current_workspace_handler(
|
|||||||
folder: AFPluginState<Weak<FolderManager>>,
|
folder: AFPluginState<Weak<FolderManager>>,
|
||||||
) -> DataResult<WorkspacePB, FlowyError> {
|
) -> DataResult<WorkspacePB, FlowyError> {
|
||||||
let folder = upgrade_folder(folder)?;
|
let folder = upgrade_folder(folder)?;
|
||||||
let workspace = folder
|
let workspace = folder.get_workspace_pb().await?;
|
||||||
.get_workspace_pb()
|
|
||||||
.await
|
|
||||||
.ok_or(FlowyError::record_not_found())?;
|
|
||||||
data_result_ok(workspace)
|
data_result_ok(workspace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,9 +12,8 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
|
|||||||
AFPlugin::new().name("Flowy-Folder").state(folder)
|
AFPlugin::new().name("Flowy-Folder").state(folder)
|
||||||
// Workspace
|
// Workspace
|
||||||
.event(FolderEvent::CreateWorkspace, create_workspace_handler)
|
.event(FolderEvent::CreateWorkspace, create_workspace_handler)
|
||||||
.event(FolderEvent::GetCurrentWorkspaceSetting, read_current_workspace_setting_handler)
|
.event(FolderEvent::GetCurrentWorkspaceSetting, read_current_workspace_setting_handler)
|
||||||
.event(FolderEvent::ReadCurrentWorkspace, read_current_workspace_handler)
|
.event(FolderEvent::ReadCurrentWorkspace, read_current_workspace_handler)
|
||||||
.event(FolderEvent::OpenWorkspace, open_workspace_handler)
|
|
||||||
.event(FolderEvent::ReadWorkspaceViews, get_workspace_views_handler)
|
.event(FolderEvent::ReadWorkspaceViews, get_workspace_views_handler)
|
||||||
// View
|
// View
|
||||||
.event(FolderEvent::CreateView, create_view_handler)
|
.event(FolderEvent::CreateView, create_view_handler)
|
||||||
@ -59,10 +58,6 @@ pub enum FolderEvent {
|
|||||||
#[event(input = "WorkspaceIdPB")]
|
#[event(input = "WorkspaceIdPB")]
|
||||||
DeleteWorkspace = 3,
|
DeleteWorkspace = 3,
|
||||||
|
|
||||||
/// Open the workspace and mark it as the current workspace
|
|
||||||
#[event(input = "WorkspaceIdPB", output = "WorkspacePB")]
|
|
||||||
OpenWorkspace = 4,
|
|
||||||
|
|
||||||
/// Return a list of views of the current workspace.
|
/// Return a list of views of the current workspace.
|
||||||
/// Only the first level of child views are included.
|
/// Only the first level of child views are included.
|
||||||
#[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")]
|
#[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
@ -124,68 +125,50 @@ impl FolderManager {
|
|||||||
Ok(views)
|
Ok(views)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called immediately after the application launched fi the user already sign in/sign up.
|
/// Called immediately after the application launched if the user already sign in/sign up.
|
||||||
#[tracing::instrument(level = "info", skip(self, initial_data), err)]
|
#[tracing::instrument(level = "info", skip(self, initial_data), err)]
|
||||||
pub async fn initialize(
|
pub async fn initialize(
|
||||||
&self,
|
&self,
|
||||||
uid: i64,
|
uid: i64,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
initial_data: FolderInitializeDataSource,
|
initial_data: FolderInitDataSource,
|
||||||
) -> FlowyResult<()> {
|
) -> FlowyResult<()> {
|
||||||
|
// Update the workspace id
|
||||||
|
event!(
|
||||||
|
Level::INFO,
|
||||||
|
"Init current workspace: {} from: {}",
|
||||||
|
workspace_id,
|
||||||
|
initial_data
|
||||||
|
);
|
||||||
*self.workspace_id.write() = Some(workspace_id.to_string());
|
*self.workspace_id.write() = Some(workspace_id.to_string());
|
||||||
let workspace_id = workspace_id.to_string();
|
let workspace_id = workspace_id.to_string();
|
||||||
if let Ok(collab_db) = self.user.collab_db(uid) {
|
|
||||||
let (view_tx, view_rx) = tokio::sync::broadcast::channel(100);
|
|
||||||
let (trash_tx, trash_rx) = tokio::sync::broadcast::channel(100);
|
|
||||||
let folder_notifier = FolderNotify {
|
|
||||||
view_change_tx: view_tx,
|
|
||||||
trash_change_tx: trash_tx,
|
|
||||||
};
|
|
||||||
|
|
||||||
let folder = match initial_data {
|
// Get the collab db for the user with given user id.
|
||||||
FolderInitializeDataSource::LocalDisk {
|
let collab_db = self.user.collab_db(uid)?;
|
||||||
create_if_not_exist,
|
|
||||||
} => {
|
let (view_tx, view_rx) = tokio::sync::broadcast::channel(100);
|
||||||
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
let (trash_tx, trash_rx) = tokio::sync::broadcast::channel(100);
|
||||||
if is_exist {
|
let folder_notifier = FolderNotify {
|
||||||
event!(Level::INFO, "Restore folder from local disk");
|
view_change_tx: view_tx,
|
||||||
let collab = self
|
trash_change_tx: trash_tx,
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
};
|
||||||
.await?;
|
|
||||||
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
|
let folder = match initial_data {
|
||||||
} else if create_if_not_exist {
|
FolderInitDataSource::LocalDisk {
|
||||||
event!(Level::INFO, "Create folder with default folder builder");
|
create_if_not_exist,
|
||||||
let folder_data =
|
} => {
|
||||||
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers)
|
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
|
||||||
.await;
|
if is_exist {
|
||||||
let collab = self
|
event!(Level::INFO, "Restore folder from local disk");
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
|
||||||
.await?;
|
|
||||||
Folder::create(
|
|
||||||
UserId::from(uid),
|
|
||||||
collab,
|
|
||||||
Some(folder_notifier),
|
|
||||||
folder_data,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
return Err(FlowyError::new(
|
|
||||||
ErrorCode::RecordNotFound,
|
|
||||||
"Can't find any workspace data",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
FolderInitializeDataSource::Cloud(raw_data) => {
|
|
||||||
event!(Level::INFO, "Restore folder from cloud service");
|
|
||||||
if raw_data.is_empty() {
|
|
||||||
return Err(workspace_data_not_sync_error(uid, &workspace_id));
|
|
||||||
}
|
|
||||||
let collab = self
|
let collab = self
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, raw_data)
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
.await?;
|
.await?;
|
||||||
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
|
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
|
||||||
},
|
} else if create_if_not_exist {
|
||||||
FolderInitializeDataSource::FolderData(folder_data) => {
|
event!(Level::INFO, "Create folder with default folder builder");
|
||||||
event!(Level::INFO, "Restore folder with passed-in folder data");
|
let folder_data =
|
||||||
|
DefaultFolderBuilder::build(uid, workspace_id.to_string(), &self.operation_handlers)
|
||||||
|
.await;
|
||||||
let collab = self
|
let collab = self
|
||||||
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
.await?;
|
.await?;
|
||||||
@ -195,24 +178,45 @@ impl FolderManager {
|
|||||||
Some(folder_notifier),
|
Some(folder_notifier),
|
||||||
folder_data,
|
folder_data,
|
||||||
)
|
)
|
||||||
},
|
} else {
|
||||||
};
|
return Err(FlowyError::new(
|
||||||
|
ErrorCode::RecordNotFound,
|
||||||
|
"Can't find any workspace data",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
FolderInitDataSource::Cloud(raw_data) => {
|
||||||
|
event!(Level::INFO, "Restore folder from cloud service");
|
||||||
|
if raw_data.is_empty() {
|
||||||
|
return Err(workspace_data_not_sync_error(uid, &workspace_id));
|
||||||
|
}
|
||||||
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, raw_data)
|
||||||
|
.await?;
|
||||||
|
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
|
||||||
|
},
|
||||||
|
FolderInitDataSource::FolderData(folder_data) => {
|
||||||
|
event!(Level::INFO, "Restore folder with passed-in folder data");
|
||||||
|
let collab = self
|
||||||
|
.collab_for_folder(uid, &workspace_id, collab_db, vec![])
|
||||||
|
.await?;
|
||||||
|
Folder::create(
|
||||||
|
UserId::from(uid),
|
||||||
|
collab,
|
||||||
|
Some(folder_notifier),
|
||||||
|
folder_data,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
tracing::debug!("Current workspace_id: {}", workspace_id);
|
let folder_state_rx = folder.subscribe_sync_state();
|
||||||
let folder_state_rx = folder.subscribe_sync_state();
|
*self.mutex_folder.lock() = Some(folder);
|
||||||
*self.mutex_folder.lock() = Some(folder);
|
|
||||||
|
|
||||||
let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
|
|
||||||
subscribe_folder_sync_state_changed(
|
|
||||||
workspace_id.clone(),
|
|
||||||
folder_state_rx,
|
|
||||||
&weak_mutex_folder,
|
|
||||||
);
|
|
||||||
subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder);
|
|
||||||
subscribe_folder_trash_changed(trash_rx, &weak_mutex_folder);
|
|
||||||
subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let weak_mutex_folder = Arc::downgrade(&self.mutex_folder);
|
||||||
|
subscribe_folder_sync_state_changed(workspace_id.clone(), folder_state_rx, &weak_mutex_folder);
|
||||||
|
subscribe_folder_snapshot_state_changed(workspace_id, &weak_mutex_folder);
|
||||||
|
subscribe_folder_trash_changed(trash_rx, &weak_mutex_folder);
|
||||||
|
subscribe_folder_view_changed(view_rx, &weak_mutex_folder);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -239,7 +243,7 @@ impl FolderManager {
|
|||||||
|
|
||||||
/// Initialize the folder with the given workspace id.
|
/// Initialize the folder with the given workspace id.
|
||||||
/// Fetch the folder updates from the cloud service and initialize the folder.
|
/// Fetch the folder updates from the cloud service and initialize the folder.
|
||||||
#[tracing::instrument(level = "debug", skip(self, user_id), err)]
|
#[tracing::instrument(skip(self, user_id), err)]
|
||||||
pub async fn initialize_with_workspace_id(
|
pub async fn initialize_with_workspace_id(
|
||||||
&self,
|
&self,
|
||||||
user_id: i64,
|
user_id: i64,
|
||||||
@ -250,7 +254,8 @@ impl FolderManager {
|
|||||||
.get_folder_updates(workspace_id, user_id)
|
.get_folder_updates(workspace_id, user_id)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
info!(
|
event!(
|
||||||
|
Level::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()
|
||||||
@ -260,7 +265,7 @@ impl FolderManager {
|
|||||||
.initialize(
|
.initialize(
|
||||||
user_id,
|
user_id,
|
||||||
workspace_id,
|
workspace_id,
|
||||||
FolderInitializeDataSource::Cloud(folder_updates),
|
FolderInitDataSource::Cloud(folder_updates),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -268,18 +273,13 @@ 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(
|
#[instrument(level = "info", skip_all, err)]
|
||||||
name = "folder_initialize_with_new_user",
|
|
||||||
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,
|
||||||
_token: &str,
|
_token: &str,
|
||||||
is_new: bool,
|
is_new: bool,
|
||||||
data_source: FolderInitializeDataSource,
|
data_source: FolderInitDataSource,
|
||||||
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
|
||||||
@ -306,7 +306,7 @@ impl FolderManager {
|
|||||||
.initialize(
|
.initialize(
|
||||||
user_id,
|
user_id,
|
||||||
workspace_id,
|
workspace_id,
|
||||||
FolderInitializeDataSource::Cloud(folder_updates),
|
FolderInitDataSource::Cloud(folder_updates),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
},
|
},
|
||||||
@ -348,20 +348,24 @@ impl FolderManager {
|
|||||||
self.with_folder(|| None, |folder| folder.get_current_workspace())
|
self.with_folder(|| None, |folder| folder.get_current_workspace())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_workspace_setting_pb(&self) -> Option<WorkspaceSettingPB> {
|
pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceSettingPB> {
|
||||||
let workspace_id = self.get_current_workspace_id().await.ok()?;
|
let workspace_id = self.get_current_workspace_id().await?;
|
||||||
let latest_view = self.get_current_view().await;
|
let latest_view = self.get_current_view().await;
|
||||||
Some(WorkspaceSettingPB {
|
Ok(WorkspaceSettingPB {
|
||||||
workspace_id,
|
workspace_id,
|
||||||
latest_view,
|
latest_view,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_workspace_pb(&self) -> Option<WorkspacePB> {
|
pub async fn get_workspace_pb(&self) -> FlowyResult<WorkspacePB> {
|
||||||
let workspace_pb = {
|
let workspace_pb = {
|
||||||
let guard = self.mutex_folder.lock();
|
let guard = self.mutex_folder.lock();
|
||||||
let folder = guard.as_ref()?;
|
let folder = guard
|
||||||
let workspace = folder.get_current_workspace()?;
|
.as_ref()
|
||||||
|
.ok_or(FlowyError::internal().with_context("folder is not initialized"))?;
|
||||||
|
let workspace = folder.get_current_workspace().ok_or(
|
||||||
|
FlowyError::record_not_found().with_context("Can't find the current workspace id "),
|
||||||
|
)?;
|
||||||
|
|
||||||
let views = folder
|
let views = folder
|
||||||
.views
|
.views
|
||||||
@ -378,7 +382,7 @@ impl FolderManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(workspace_pb)
|
Ok(workspace_pb)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_current_workspace_id(&self) -> FlowyResult<String> {
|
async fn get_current_workspace_id(&self) -> FlowyResult<String> {
|
||||||
@ -1274,7 +1278,7 @@ impl Deref for MutexFolder {
|
|||||||
unsafe impl Sync for MutexFolder {}
|
unsafe impl Sync for MutexFolder {}
|
||||||
unsafe impl Send for MutexFolder {}
|
unsafe impl Send for MutexFolder {}
|
||||||
|
|
||||||
pub enum FolderInitializeDataSource {
|
pub enum FolderInitDataSource {
|
||||||
/// It means using the data stored on local disk to initialize the folder
|
/// It means using the data stored on local disk to initialize the folder
|
||||||
LocalDisk { create_if_not_exist: bool },
|
LocalDisk { create_if_not_exist: bool },
|
||||||
/// If there is no data stored on local disk, we will use the data from the server to initialize the folder
|
/// If there is no data stored on local disk, we will use the data from the server to initialize the folder
|
||||||
@ -1283,6 +1287,16 @@ pub enum FolderInitializeDataSource {
|
|||||||
FolderData(FolderData),
|
FolderData(FolderData),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Display for FolderInitDataSource {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
FolderInitDataSource::LocalDisk { .. } => f.write_fmt(format_args!("LocalDisk")),
|
||||||
|
FolderInitDataSource::Cloud(_) => f.write_fmt(format_args!("Cloud")),
|
||||||
|
FolderInitDataSource::FolderData(_) => f.write_fmt(format_args!("Custom FolderData")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn is_exist_in_local_disk(user: &Arc<dyn FolderUser>, doc_id: &str) -> FlowyResult<bool> {
|
fn is_exist_in_local_disk(user: &Arc<dyn FolderUser>, doc_id: &str) -> FlowyResult<bool> {
|
||||||
let uid = user.user_id()?;
|
let uid = user.user_id()?;
|
||||||
if let Some(collab_db) = user.collab_db(uid)?.upgrade() {
|
if let Some(collab_db) = user.collab_db(uid)?.upgrade() {
|
||||||
|
@ -20,7 +20,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
document_id: &str,
|
document_id: &str,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
) -> FutureResult<Vec<Vec<u8>>, Error> {
|
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
|
||||||
let workspace_id = workspace_id.to_string();
|
let workspace_id = workspace_id.to_string();
|
||||||
let try_get_client = self.0.try_get_client();
|
let try_get_client = self.0.try_get_client();
|
||||||
let document_id = document_id.to_string();
|
let document_id = document_id.to_string();
|
||||||
|
@ -4,7 +4,9 @@ use collab::core::origin::CollabOrigin;
|
|||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
|
|
||||||
use flowy_error::FlowyError;
|
use flowy_error::FlowyError;
|
||||||
use flowy_folder_deps::cloud::{Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace};
|
use flowy_folder_deps::cloud::{
|
||||||
|
Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace, WorkspaceRecord,
|
||||||
|
};
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
use crate::af_cloud::AFServer;
|
use crate::af_cloud::AFServer;
|
||||||
@ -19,6 +21,35 @@ where
|
|||||||
FutureResult::new(async move { Err(anyhow!("Not support yet")) })
|
FutureResult::new(async move { Err(anyhow!("Not support yet")) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_workspace(&self, workspace_id: &str) -> FutureResult<(), Error> {
|
||||||
|
let workspace_id = workspace_id.to_string();
|
||||||
|
let try_get_client = self.0.try_get_client();
|
||||||
|
FutureResult::new(async move {
|
||||||
|
let client = try_get_client?;
|
||||||
|
let _ = client.open_workspace(&workspace_id).await?;
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> {
|
||||||
|
let try_get_client = self.0.try_get_client();
|
||||||
|
FutureResult::new(async move {
|
||||||
|
let client = try_get_client?;
|
||||||
|
let records = client
|
||||||
|
.get_user_workspace_info()
|
||||||
|
.await?
|
||||||
|
.workspaces
|
||||||
|
.into_iter()
|
||||||
|
.map(|af_workspace| WorkspaceRecord {
|
||||||
|
id: af_workspace.workspace_id.to_string(),
|
||||||
|
name: af_workspace.workspace_name,
|
||||||
|
created_at: af_workspace.created_at.timestamp(),
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
Ok(records)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn get_folder_data(
|
fn get_folder_data(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
|
@ -113,7 +113,17 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
fn open_workspace(&self, workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError> {
|
||||||
|
let try_get_client = self.server.try_get_client();
|
||||||
|
let workspace_id = workspace_id.to_string();
|
||||||
|
FutureResult::new(async move {
|
||||||
|
let client = try_get_client?;
|
||||||
|
let af_workspace = client.open_workspace(&workspace_id).await?;
|
||||||
|
Ok(to_user_workspace(af_workspace))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||||
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 {
|
||||||
let workspaces = try_get_client?.get_workspaces().await?;
|
let workspaces = try_get_client?.get_workspaces().await?;
|
||||||
|
@ -4,12 +4,12 @@ use std::sync::Arc;
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use client_api::notify::{TokenState, TokenStateReceiver};
|
use client_api::notify::{TokenState, TokenStateReceiver};
|
||||||
use client_api::ws::{
|
use client_api::ws::{
|
||||||
BusinessID, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
|
BusinessID, ConnectState, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
|
||||||
};
|
};
|
||||||
use client_api::Client;
|
use client_api::Client;
|
||||||
use tokio::sync::watch;
|
use tokio::sync::watch;
|
||||||
use tokio_stream::wrappers::WatchStream;
|
use tokio_stream::wrappers::WatchStream;
|
||||||
use tracing::{error, info};
|
use tracing::{error, event, 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;
|
||||||
@ -145,7 +145,8 @@ impl AppFlowyServer for AFCloudServer {
|
|||||||
fn collab_ws_channel(
|
fn collab_ws_channel(
|
||||||
&self,
|
&self,
|
||||||
object_id: &str,
|
object_id: &str,
|
||||||
) -> FutureResult<Option<(Arc<WebSocketChannel>, WSConnectStateReceiver)>, anyhow::Error> {
|
) -> FutureResult<Option<(Arc<WebSocketChannel>, WSConnectStateReceiver, bool)>, anyhow::Error>
|
||||||
|
{
|
||||||
if self.enable_sync.load(Ordering::SeqCst) {
|
if self.enable_sync.load(Ordering::SeqCst) {
|
||||||
let object_id = object_id.to_string();
|
let object_id = object_id.to_string();
|
||||||
let weak_ws_client = Arc::downgrade(&self.ws_client);
|
let weak_ws_client = Arc::downgrade(&self.ws_client);
|
||||||
@ -155,7 +156,7 @@ impl AppFlowyServer for AFCloudServer {
|
|||||||
Some(ws_client) => {
|
Some(ws_client) => {
|
||||||
let channel = ws_client.subscribe(BusinessID::CollabId, object_id).ok();
|
let channel = ws_client.subscribe(BusinessID::CollabId, object_id).ok();
|
||||||
let connect_state_recv = ws_client.subscribe_connect_state();
|
let connect_state_recv = ws_client.subscribe_connect_state();
|
||||||
Ok(channel.map(|c| (c, connect_state_recv)))
|
Ok(channel.map(|c| (c, connect_state_recv, ws_client.is_connected())))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -190,24 +191,33 @@ fn spawn_ws_conn(
|
|||||||
if let Some(ws_client) = weak_ws_client.upgrade() {
|
if let Some(ws_client) = weak_ws_client.upgrade() {
|
||||||
let mut state_recv = ws_client.subscribe_connect_state();
|
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() {
|
info!("[websocket] state: {:?}", state);
|
||||||
continue;
|
match state {
|
||||||
}
|
ConnectState::PingTimeout => {
|
||||||
|
// Try to reconnect if the connection is timed out.
|
||||||
// Try to reconnect if the connection is timed out.
|
if let (Some(api_client), Some(device_id)) =
|
||||||
if let (Some(api_client), Some(device_id)) =
|
(weak_api_client.upgrade(), weak_device_id.upgrade())
|
||||||
(weak_api_client.upgrade(), weak_device_id.upgrade())
|
{
|
||||||
{
|
if enable_sync.load(Ordering::SeqCst) {
|
||||||
if enable_sync.load(Ordering::SeqCst) {
|
let device_id = device_id.read().clone();
|
||||||
info!("🟢websocket state: {:?}, reconnecting", state);
|
match api_client.ws_url(&device_id) {
|
||||||
let device_id = device_id.read().clone();
|
Ok(ws_addr) => {
|
||||||
match api_client.ws_url(&device_id) {
|
event!(tracing::Level::INFO, "🟢reconnecting websocket");
|
||||||
Ok(ws_addr) => {
|
let _ = ws_client.connect(ws_addr).await;
|
||||||
let _ = ws_client.connect(ws_addr).await;
|
},
|
||||||
},
|
Err(err) => error!("Failed to get ws url: {}", err),
|
||||||
Err(err) => error!("Failed to get ws url: {}", err),
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
ConnectState::Unauthorized => {
|
||||||
|
if let Some(api_client) = weak_api_client.upgrade() {
|
||||||
|
if enable_sync.load(Ordering::SeqCst) {
|
||||||
|
let _ = api_client.refresh().await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_ => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
|
||||||
use flowy_document_deps::cloud::*;
|
use flowy_document_deps::cloud::*;
|
||||||
|
use flowy_error::FlowyError;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
|
|
||||||
pub(crate) struct LocalServerDocumentCloudServiceImpl();
|
pub(crate) struct LocalServerDocumentCloudServiceImpl();
|
||||||
@ -10,7 +11,7 @@ impl DocumentCloudService for LocalServerDocumentCloudServiceImpl {
|
|||||||
&self,
|
&self,
|
||||||
_document_id: &str,
|
_document_id: &str,
|
||||||
_workspace_id: &str,
|
_workspace_id: &str,
|
||||||
) -> FutureResult<Vec<Vec<u8>>, Error> {
|
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
|
||||||
FutureResult::new(async move { Ok(vec![]) })
|
FutureResult::new(async move { Ok(vec![]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ use std::sync::Arc;
|
|||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
|
|
||||||
use flowy_folder_deps::cloud::{
|
use flowy_folder_deps::cloud::{
|
||||||
gen_workspace_id, FolderCloudService, FolderData, FolderSnapshot, Workspace,
|
gen_workspace_id, FolderCloudService, FolderData, FolderSnapshot, Workspace, WorkspaceRecord,
|
||||||
};
|
};
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
use lib_infra::util::timestamp;
|
use lib_infra::util::timestamp;
|
||||||
@ -11,6 +11,7 @@ use lib_infra::util::timestamp;
|
|||||||
use crate::local_server::LocalServerDB;
|
use crate::local_server::LocalServerDB;
|
||||||
|
|
||||||
pub(crate) struct LocalServerFolderCloudServiceImpl {
|
pub(crate) struct LocalServerFolderCloudServiceImpl {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub db: Arc<dyn LocalServerDB>,
|
pub db: Arc<dyn LocalServerDB>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -27,6 +28,14 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_workspace(&self, _workspace_id: &str) -> FutureResult<(), Error> {
|
||||||
|
FutureResult::new(async { Ok(()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> {
|
||||||
|
FutureResult::new(async { Ok(vec![]) })
|
||||||
|
}
|
||||||
|
|
||||||
fn get_folder_data(
|
fn get_folder_data(
|
||||||
&self,
|
&self,
|
||||||
_workspace_id: &str,
|
_workspace_id: &str,
|
||||||
@ -43,18 +52,12 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
|
|||||||
FutureResult::new(async move { Ok(vec![]) })
|
FutureResult::new(async move { Ok(vec![]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_folder_updates(&self, workspace_id: &str, uid: i64) -> FutureResult<Vec<Vec<u8>>, Error> {
|
fn get_folder_updates(
|
||||||
let weak_db = Arc::downgrade(&self.db);
|
&self,
|
||||||
let workspace_id = workspace_id.to_string();
|
_workspace_id: &str,
|
||||||
FutureResult::new(async move {
|
_uid: i64,
|
||||||
match weak_db.upgrade() {
|
) -> FutureResult<Vec<Vec<u8>>, Error> {
|
||||||
None => Ok(vec![]),
|
FutureResult::new(async move { Ok(vec![]) })
|
||||||
Some(db) => {
|
|
||||||
let updates = db.get_collab_updates(uid, &workspace_id)?;
|
|
||||||
Ok(updates)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn service_name(&self) -> String {
|
fn service_name(&self) -> String {
|
||||||
|
@ -116,7 +116,13 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
|
|||||||
FutureResult::new(async { result })
|
FutureResult::new(async { result })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_user_workspaces(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
fn open_workspace(&self, _workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError> {
|
||||||
|
FutureResult::new(async {
|
||||||
|
Err(FlowyError::not_support().with_context("local server doesn't support open workspace"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self, _uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||||
FutureResult::new(async { Ok(vec![]) })
|
FutureResult::new(async { Ok(vec![]) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ use crate::AppFlowyServer;
|
|||||||
pub trait LocalServerDB: Send + Sync + 'static {
|
pub trait LocalServerDB: Send + Sync + 'static {
|
||||||
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError>;
|
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError>;
|
||||||
fn get_user_workspace(&self, uid: i64) -> Result<Option<UserWorkspace>, FlowyError>;
|
fn get_user_workspace(&self, uid: i64) -> Result<Option<UserWorkspace>, FlowyError>;
|
||||||
fn get_collab_updates(&self, uid: i64, object_id: &str) -> Result<Vec<Vec<u8>>, FlowyError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LocalServer {
|
pub struct LocalServer {
|
||||||
|
@ -104,7 +104,8 @@ pub trait AppFlowyServer: Send + Sync + 'static {
|
|||||||
fn collab_ws_channel(
|
fn collab_ws_channel(
|
||||||
&self,
|
&self,
|
||||||
_object_id: &str,
|
_object_id: &str,
|
||||||
) -> FutureResult<Option<(Arc<WebSocketChannel>, WSConnectStateReceiver)>, anyhow::Error> {
|
) -> FutureResult<Option<(Arc<WebSocketChannel>, WSConnectStateReceiver, bool)>, anyhow::Error>
|
||||||
|
{
|
||||||
FutureResult::new(async { Ok(None) })
|
FutureResult::new(async { Ok(None) })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ where
|
|||||||
&self,
|
&self,
|
||||||
document_id: &str,
|
document_id: &str,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
) -> FutureResult<Vec<Vec<u8>>, Error> {
|
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
|
||||||
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
let try_get_postgrest = self.server.try_get_weak_postgrest();
|
||||||
let document_id = document_id.to_string();
|
let document_id = document_id.to_string();
|
||||||
let (tx, rx) = channel();
|
let (tx, rx) = channel();
|
||||||
@ -43,7 +43,7 @@ where
|
|||||||
let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest);
|
let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest);
|
||||||
let updates = action.run_with_fix_interval(5, 10).await?;
|
let updates = action.run_with_fix_interval(5, 10).await?;
|
||||||
if updates.is_empty() {
|
if updates.is_empty() {
|
||||||
return Err(FlowyError::collab_not_sync().into());
|
return Err(FlowyError::collab_not_sync());
|
||||||
}
|
}
|
||||||
Ok(updates)
|
Ok(updates)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ use tokio::sync::oneshot::channel;
|
|||||||
|
|
||||||
use flowy_folder_deps::cloud::{
|
use flowy_folder_deps::cloud::{
|
||||||
gen_workspace_id, Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace,
|
gen_workspace_id, Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace,
|
||||||
|
WorkspaceRecord,
|
||||||
};
|
};
|
||||||
use lib_dispatch::prelude::af_spawn;
|
use lib_dispatch::prelude::af_spawn;
|
||||||
use lib_infra::future::FutureResult;
|
use lib_infra::future::FutureResult;
|
||||||
@ -69,6 +70,14 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn open_workspace(&self, _workspace_id: &str) -> FutureResult<(), Error> {
|
||||||
|
FutureResult::new(async { Ok(()) })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self) -> FutureResult<Vec<WorkspaceRecord>, Error> {
|
||||||
|
FutureResult::new(async { Ok(vec![]) })
|
||||||
|
}
|
||||||
|
|
||||||
fn get_folder_data(
|
fn get_folder_data(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
|
@ -226,7 +226,13 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
fn open_workspace(&self, _workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError> {
|
||||||
|
FutureResult::new(async {
|
||||||
|
Err(FlowyError::not_support().with_context("supabase server doesn't support open workspace"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_all_workspace(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error> {
|
||||||
let try_get_postgrest = self.server.try_get_postgrest();
|
let try_get_postgrest = self.server.try_get_postgrest();
|
||||||
FutureResult::new(async move {
|
FutureResult::new(async move {
|
||||||
let postgrest = try_get_postgrest?;
|
let postgrest = try_get_postgrest?;
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
use crate::queue::TaskQueue;
|
|
||||||
use crate::store::TaskStore;
|
|
||||||
use crate::{Task, TaskContent, TaskId, TaskState};
|
|
||||||
use anyhow::Error;
|
|
||||||
|
|
||||||
use lib_infra::future::BoxResultFuture;
|
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
use tokio::sync::{watch, RwLock};
|
use tokio::sync::{watch, RwLock};
|
||||||
use tokio::time::interval;
|
use tokio::time::interval;
|
||||||
|
|
||||||
|
use lib_infra::future::BoxResultFuture;
|
||||||
|
|
||||||
|
use crate::queue::TaskQueue;
|
||||||
|
use crate::store::TaskStore;
|
||||||
|
use crate::{Task, TaskContent, TaskId, TaskState};
|
||||||
|
|
||||||
pub struct TaskDispatcher {
|
pub struct TaskDispatcher {
|
||||||
queue: TaskQueue,
|
queue: TaskQueue,
|
||||||
store: TaskStore,
|
store: TaskStore,
|
||||||
@ -122,6 +122,9 @@ impl TaskDispatcher {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn clear_task(&mut self) {
|
||||||
|
self.store.clear();
|
||||||
|
}
|
||||||
pub fn next_task_id(&self) -> TaskId {
|
pub fn next_task_id(&self) -> TaskId {
|
||||||
self.store.next_task_id()
|
self.store.next_task_id()
|
||||||
}
|
}
|
||||||
|
@ -93,8 +93,10 @@ pub trait UserCloudService: Send + Sync + 'static {
|
|||||||
/// return None if the user is not found
|
/// return None if the user is not found
|
||||||
fn get_user_profile(&self, credential: UserCredentials) -> FutureResult<UserProfile, FlowyError>;
|
fn get_user_profile(&self, credential: UserCredentials) -> FutureResult<UserProfile, FlowyError>;
|
||||||
|
|
||||||
|
fn open_workspace(&self, workspace_id: &str) -> FutureResult<UserWorkspace, FlowyError>;
|
||||||
|
|
||||||
/// Return the all the workspaces of the user
|
/// Return the all the workspaces of the user
|
||||||
fn get_all_user_workspaces(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error>;
|
fn get_all_workspace(&self, uid: i64) -> FutureResult<Vec<UserWorkspace>, Error>;
|
||||||
|
|
||||||
fn add_workspace_member(
|
fn add_workspace_member(
|
||||||
&self,
|
&self,
|
||||||
|
@ -138,7 +138,7 @@ pub struct UserWorkspace {
|
|||||||
pub id: String,
|
pub id: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub created_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
/// The database storage id is used indexing all the database in current workspace.
|
/// The database storage id is used indexing all the database views in current workspace.
|
||||||
#[serde(rename = "database_storage_id")]
|
#[serde(rename = "database_storage_id")]
|
||||||
pub database_views_aggregate_id: String,
|
pub database_views_aggregate_id: String,
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
use std::convert::TryInto;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
|
||||||
use flowy_user_deps::entities::*;
|
use flowy_user_deps::entities::*;
|
||||||
|
|
||||||
use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword};
|
use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword};
|
||||||
|
use crate::entities::required_not_empty_str;
|
||||||
use crate::entities::AuthTypePB;
|
use crate::entities::AuthTypePB;
|
||||||
use crate::errors::ErrorCode;
|
use crate::errors::ErrorCode;
|
||||||
use crate::services::entities::HistoricalUser;
|
use crate::services::entities::HistoricalUser;
|
||||||
@ -217,10 +220,11 @@ impl From<Vec<UserWorkspace>> for RepeatedUserWorkspacePB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(ProtoBuf, Default, Debug, Clone)]
|
#[derive(ProtoBuf, Default, Debug, Clone, Validate)]
|
||||||
pub struct UserWorkspacePB {
|
pub struct UserWorkspacePB {
|
||||||
#[pb(index = 1)]
|
#[pb(index = 1)]
|
||||||
pub id: String,
|
#[validate(custom = "required_not_empty_str")]
|
||||||
|
pub workspace_id: String,
|
||||||
|
|
||||||
#[pb(index = 2)]
|
#[pb(index = 2)]
|
||||||
pub name: String,
|
pub name: String,
|
||||||
@ -229,7 +233,7 @@ pub struct UserWorkspacePB {
|
|||||||
impl From<UserWorkspace> for UserWorkspacePB {
|
impl From<UserWorkspace> for UserWorkspacePB {
|
||||||
fn from(value: UserWorkspace) -> Self {
|
fn from(value: UserWorkspace) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: value.id,
|
workspace_id: value.id,
|
||||||
name: value.name,
|
name: value.name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,3 +103,10 @@ impl From<Role> for AFRolePB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(ProtoBuf, Default, Clone, Validate)]
|
||||||
|
pub struct UserWorkspaceIdPB {
|
||||||
|
#[pb(index = 1)]
|
||||||
|
#[validate(custom = "required_not_empty_str")]
|
||||||
|
pub workspace_id: String,
|
||||||
|
}
|
||||||
|
@ -2,7 +2,6 @@ use std::sync::Weak;
|
|||||||
use std::{convert::TryInto, sync::Arc};
|
use std::{convert::TryInto, sync::Arc};
|
||||||
|
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tracing::event;
|
|
||||||
|
|
||||||
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
|
||||||
use flowy_sqlite::kv::StorePreferences;
|
use flowy_sqlite::kv::StorePreferences;
|
||||||
@ -105,17 +104,11 @@ pub async fn get_user_profile_handler(
|
|||||||
user_profile.email = "".to_string();
|
user_profile.email = "".to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
event!(
|
|
||||||
tracing::Level::DEBUG,
|
|
||||||
"Get user profile: {:?}",
|
|
||||||
user_profile
|
|
||||||
);
|
|
||||||
|
|
||||||
data_result_ok(user_profile.into())
|
data_result_ok(user_profile.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(manager))]
|
#[tracing::instrument(level = "debug", skip(manager))]
|
||||||
pub async fn sign_out(manager: AFPluginState<Weak<UserManager>>) -> Result<(), FlowyError> {
|
pub async fn sign_out_handler(manager: AFPluginState<Weak<UserManager>>) -> Result<(), FlowyError> {
|
||||||
let manager = upgrade_manager(manager)?;
|
let manager = upgrade_manager(manager)?;
|
||||||
manager.sign_out().await?;
|
manager.sign_out().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -425,7 +418,7 @@ pub async fn get_cloud_config_handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(manager), err)]
|
#[tracing::instrument(level = "debug", skip(manager), err)]
|
||||||
pub async fn get_all_user_workspace_handler(
|
pub async fn get_all_workspace_handler(
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
manager: AFPluginState<Weak<UserManager>>,
|
||||||
) -> DataResult<RepeatedUserWorkspacePB, FlowyError> {
|
) -> DataResult<RepeatedUserWorkspacePB, FlowyError> {
|
||||||
let manager = upgrade_manager(manager)?;
|
let manager = upgrade_manager(manager)?;
|
||||||
@ -436,12 +429,12 @@ pub async fn get_all_user_workspace_handler(
|
|||||||
|
|
||||||
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
#[tracing::instrument(level = "debug", skip(data, manager), err)]
|
||||||
pub async fn open_workspace_handler(
|
pub async fn open_workspace_handler(
|
||||||
data: AFPluginData<UserWorkspacePB>,
|
data: AFPluginData<UserWorkspaceIdPB>,
|
||||||
manager: AFPluginState<Weak<UserManager>>,
|
manager: AFPluginState<Weak<UserManager>>,
|
||||||
) -> Result<(), FlowyError> {
|
) -> Result<(), FlowyError> {
|
||||||
let manager = upgrade_manager(manager)?;
|
let manager = upgrade_manager(manager)?;
|
||||||
let params = data.into_inner();
|
let params = data.validate()?.into_inner();
|
||||||
manager.open_workspace(¶ms.id).await?;
|
manager.open_workspace(¶ms.workspace_id).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
|
|||||||
.event(UserEvent::SignUp, sign_up)
|
.event(UserEvent::SignUp, sign_up)
|
||||||
.event(UserEvent::InitUser, init_user_handler)
|
.event(UserEvent::InitUser, init_user_handler)
|
||||||
.event(UserEvent::GetUserProfile, get_user_profile_handler)
|
.event(UserEvent::GetUserProfile, get_user_profile_handler)
|
||||||
.event(UserEvent::SignOut, sign_out)
|
.event(UserEvent::SignOut, sign_out_handler)
|
||||||
.event(UserEvent::UpdateUserProfile, update_user_profile_handler)
|
.event(UserEvent::UpdateUserProfile, update_user_profile_handler)
|
||||||
.event(UserEvent::SetAppearanceSetting, set_appearance_setting)
|
.event(UserEvent::SetAppearanceSetting, set_appearance_setting)
|
||||||
.event(UserEvent::GetAppearanceSetting, get_appearance_setting)
|
.event(UserEvent::GetAppearanceSetting, get_appearance_setting)
|
||||||
@ -41,7 +41,7 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
|
|||||||
.event(UserEvent::OauthSignIn, oauth_handler)
|
.event(UserEvent::OauthSignIn, oauth_handler)
|
||||||
.event(UserEvent::GetSignInURL, get_sign_in_url_handler)
|
.event(UserEvent::GetSignInURL, get_sign_in_url_handler)
|
||||||
.event(UserEvent::GetOauthURLWithProvider, sign_in_with_provider_handler)
|
.event(UserEvent::GetOauthURLWithProvider, sign_in_with_provider_handler)
|
||||||
.event(UserEvent::GetAllUserWorkspaces, get_all_user_workspace_handler)
|
.event(UserEvent::GetAllWorkspace, get_all_workspace_handler)
|
||||||
.event(UserEvent::OpenWorkspace, open_workspace_handler)
|
.event(UserEvent::OpenWorkspace, open_workspace_handler)
|
||||||
.event(UserEvent::UpdateNetworkState, update_network_state_handler)
|
.event(UserEvent::UpdateNetworkState, update_network_state_handler)
|
||||||
.event(UserEvent::GetHistoricalUsers, get_historical_users_handler)
|
.event(UserEvent::GetHistoricalUsers, get_historical_users_handler)
|
||||||
@ -60,7 +60,7 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
|
|||||||
.event(UserEvent::AddWorkspaceMember, add_workspace_member_handler)
|
.event(UserEvent::AddWorkspaceMember, add_workspace_member_handler)
|
||||||
.event(UserEvent::RemoveWorkspaceMember, delete_workspace_member_handler)
|
.event(UserEvent::RemoveWorkspaceMember, delete_workspace_member_handler)
|
||||||
.event(UserEvent::GetWorkspaceMember, get_workspace_member_handler)
|
.event(UserEvent::GetWorkspaceMember, get_workspace_member_handler)
|
||||||
.event(UserEvent::UpdateWorkspaceMember, update_workspace_member_handler,)
|
.event(UserEvent::UpdateWorkspaceMember, update_workspace_member_handler)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
|
||||||
@ -129,10 +129,10 @@ pub enum UserEvent {
|
|||||||
CheckEncryptionSign = 16,
|
CheckEncryptionSign = 16,
|
||||||
|
|
||||||
/// Return the all the workspaces of the user
|
/// Return the all the workspaces of the user
|
||||||
#[event()]
|
#[event(output = "RepeatedUserWorkspacePB")]
|
||||||
GetAllUserWorkspaces = 20,
|
GetAllWorkspace = 17,
|
||||||
|
|
||||||
#[event(input = "UserWorkspacePB")]
|
#[event(input = "UserWorkspaceIdPB")]
|
||||||
OpenWorkspace = 21,
|
OpenWorkspace = 21,
|
||||||
|
|
||||||
#[event(input = "NetworkStatePB")]
|
#[event(input = "NetworkStatePB")]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
use std::sync::atomic::{AtomicI64, Ordering};
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
use collab_user::core::MutexUserAwareness;
|
use collab_user::core::MutexUserAwareness;
|
||||||
@ -55,7 +56,7 @@ impl UserSessionConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct UserManager {
|
pub struct UserManager {
|
||||||
database: UserDB,
|
database: Arc<UserDB>,
|
||||||
session_config: UserSessionConfig,
|
session_config: UserSessionConfig,
|
||||||
pub(crate) cloud_services: Arc<dyn UserCloudServiceProvider>,
|
pub(crate) cloud_services: Arc<dyn UserCloudServiceProvider>,
|
||||||
pub(crate) store_preferences: Arc<StorePreferences>,
|
pub(crate) store_preferences: Arc<StorePreferences>,
|
||||||
@ -64,7 +65,8 @@ pub struct UserManager {
|
|||||||
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
|
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
|
||||||
pub(crate) collab_interact: RwLock<Arc<dyn CollabInteract>>,
|
pub(crate) collab_interact: RwLock<Arc<dyn CollabInteract>>,
|
||||||
resumable_sign_up: Mutex<Option<ResumableSignUp>>,
|
resumable_sign_up: Mutex<Option<ResumableSignUp>>,
|
||||||
current_session: parking_lot::RwLock<Option<Session>>,
|
current_session: Arc<parking_lot::RwLock<Option<Session>>>,
|
||||||
|
refresh_user_profile_since: AtomicI64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserManager {
|
impl UserManager {
|
||||||
@ -74,10 +76,11 @@ impl UserManager {
|
|||||||
store_preferences: Arc<StorePreferences>,
|
store_preferences: Arc<StorePreferences>,
|
||||||
collab_builder: Weak<AppFlowyCollabBuilder>,
|
collab_builder: Weak<AppFlowyCollabBuilder>,
|
||||||
) -> Arc<Self> {
|
) -> Arc<Self> {
|
||||||
let database = UserDB::new(&session_config.root_dir);
|
let database = Arc::new(UserDB::new(&session_config.root_dir));
|
||||||
let user_status_callback: RwLock<Arc<dyn UserStatusCallback>> =
|
let user_status_callback: RwLock<Arc<dyn UserStatusCallback>> =
|
||||||
RwLock::new(Arc::new(DefaultUserStatusCallback));
|
RwLock::new(Arc::new(DefaultUserStatusCallback));
|
||||||
|
|
||||||
|
let refresh_user_profile_since = AtomicI64::new(0);
|
||||||
let user_manager = Arc::new(Self {
|
let user_manager = Arc::new(Self {
|
||||||
database,
|
database,
|
||||||
session_config,
|
session_config,
|
||||||
@ -89,6 +92,7 @@ impl UserManager {
|
|||||||
collab_interact: RwLock::new(Arc::new(DefaultCollabInteract)),
|
collab_interact: RwLock::new(Arc::new(DefaultCollabInteract)),
|
||||||
resumable_sign_up: Default::default(),
|
resumable_sign_up: Default::default(),
|
||||||
current_session: Default::default(),
|
current_session: Default::default(),
|
||||||
|
refresh_user_profile_since,
|
||||||
});
|
});
|
||||||
|
|
||||||
let weak_user_manager = Arc::downgrade(&user_manager);
|
let weak_user_manager = Arc::downgrade(&user_manager);
|
||||||
@ -120,13 +124,28 @@ impl UserManager {
|
|||||||
/// a local data migration for the user. After ensuring the user's data is migrated and up-to-date,
|
/// a local data migration for the user. After ensuring the user's data is migrated and up-to-date,
|
||||||
/// the function will set up the collaboration configuration and initialize the user's awareness. Upon successful
|
/// the function will set up the collaboration configuration and initialize the user's awareness. Upon successful
|
||||||
/// completion, a user status callback is invoked to signify that the initialization process is complete.
|
/// completion, a user status callback is invoked to signify that the initialization process is complete.
|
||||||
|
#[instrument(level = "debug", skip_all, err)]
|
||||||
pub async fn init<C: UserStatusCallback + 'static, I: CollabInteract>(
|
pub async fn init<C: UserStatusCallback + 'static, I: CollabInteract>(
|
||||||
&self,
|
&self,
|
||||||
user_status_callback: C,
|
user_status_callback: C,
|
||||||
collab_interact: I,
|
collab_interact: I,
|
||||||
) -> Result<(), FlowyError> {
|
) -> Result<(), FlowyError> {
|
||||||
|
let user_status_callback = Arc::new(user_status_callback);
|
||||||
|
*self.user_status_callback.write().await = user_status_callback.clone();
|
||||||
|
*self.collab_interact.write().await = Arc::new(collab_interact);
|
||||||
|
|
||||||
if let Ok(session) = self.get_session() {
|
if let Ok(session) = self.get_session() {
|
||||||
let user = self.get_user_profile(session.user_id).await?;
|
let user = self.get_user_profile(session.user_id).await?;
|
||||||
|
|
||||||
|
event!(
|
||||||
|
tracing::Level::INFO,
|
||||||
|
"init user session: {}:{}",
|
||||||
|
user.uid,
|
||||||
|
user.email
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set the token if the current cloud service using token to authenticate
|
||||||
|
// Currently, only the AppFlowy cloud using token to init the client api.
|
||||||
if let Err(err) = self.cloud_services.set_token(&user.token) {
|
if let Err(err) = self.cloud_services.set_token(&user.token) {
|
||||||
error!("Set token failed: {}", err);
|
error!("Set token failed: {}", err);
|
||||||
}
|
}
|
||||||
@ -134,10 +153,13 @@ impl UserManager {
|
|||||||
// Subscribe the token state
|
// Subscribe the token state
|
||||||
let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?);
|
let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?);
|
||||||
if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() {
|
if let Some(mut token_state_rx) = self.cloud_services.subscribe_token_state() {
|
||||||
|
event!(tracing::Level::DEBUG, "Listen token state change");
|
||||||
af_spawn(async move {
|
af_spawn(async move {
|
||||||
while let Some(token_state) = token_state_rx.next().await {
|
while let Some(token_state) = token_state_rx.next().await {
|
||||||
|
debug!("Token state changed: {:?}", token_state);
|
||||||
match token_state {
|
match token_state {
|
||||||
UserTokenState::Refresh { token } => {
|
UserTokenState::Refresh { token } => {
|
||||||
|
// Only save the token if the token is different from the current token
|
||||||
if token != user.token {
|
if token != user.token {
|
||||||
if let Some(pool) = weak_pool.upgrade() {
|
if let Some(pool) = weak_pool.upgrade() {
|
||||||
// Save the new token
|
// Save the new token
|
||||||
@ -147,19 +169,14 @@ impl UserManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
UserTokenState::Invalid => {
|
UserTokenState::Invalid => {},
|
||||||
send_auth_state_notification(AuthStateChangedPB {
|
|
||||||
state: AuthStatePB::InvalidAuth,
|
|
||||||
message: "Token is invalid".to_string(),
|
|
||||||
})
|
|
||||||
.send();
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do the user data migration if needed
|
// Do the user data migration if needed
|
||||||
|
event!(tracing::Level::INFO, "Prepare user data migration");
|
||||||
match (
|
match (
|
||||||
self.database.get_collab_db(session.user_id),
|
self.database.get_collab_db(session.user_id),
|
||||||
self.database.get_pool(session.user_id),
|
self.database.get_pool(session.user_id),
|
||||||
@ -202,8 +219,6 @@ impl UserManager {
|
|||||||
error!("Failed to call did_init callback: {:?}", e);
|
error!("Failed to call did_init callback: {:?}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*self.user_status_callback.write().await = Arc::new(user_status_callback);
|
|
||||||
*self.collab_interact.write().await = Arc::new(collab_interact);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,6 +395,7 @@ impl UserManager {
|
|||||||
self
|
self
|
||||||
.save_auth_data(&response, auth_type, &new_session)
|
.save_auth_data(&response, auth_type, &new_session)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
self
|
self
|
||||||
.user_status_callback
|
.user_status_callback
|
||||||
.read()
|
.read()
|
||||||
@ -445,14 +461,28 @@ impl UserManager {
|
|||||||
pub async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
|
pub async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
|
||||||
let user: UserProfile = user_table::dsl::user_table
|
let user: UserProfile = user_table::dsl::user_table
|
||||||
.filter(user_table::id.eq(&uid.to_string()))
|
.filter(user_table::id.eq(&uid.to_string()))
|
||||||
.first::<UserTable>(&*(self.db_connection(uid)?))?
|
.first::<UserTable>(&*(self.db_connection(uid)?))
|
||||||
|
.map_err(|err| {
|
||||||
|
FlowyError::record_not_found().with_context(format!(
|
||||||
|
"Can't find the user profile for user id: {}, error: {:?}",
|
||||||
|
uid, err
|
||||||
|
))
|
||||||
|
})?
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
Ok(user)
|
Ok(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(level = "info", skip_all)]
|
#[tracing::instrument(level = "info", skip_all, err)]
|
||||||
pub async fn refresh_user_profile(&self, old_user_profile: &UserProfile) -> FlowyResult<()> {
|
pub async fn refresh_user_profile(&self, old_user_profile: &UserProfile) -> FlowyResult<()> {
|
||||||
|
let now = chrono::Utc::now().timestamp();
|
||||||
|
|
||||||
|
// Add debounce to avoid too many requests
|
||||||
|
if now - self.refresh_user_profile_since.load(Ordering::SeqCst) < 5 {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
self.refresh_user_profile_since.store(now, Ordering::SeqCst);
|
||||||
let uid = old_user_profile.uid;
|
let uid = old_user_profile.uid;
|
||||||
let result: Result<UserProfile, FlowyError> = self
|
let result: Result<UserProfile, FlowyError> = self
|
||||||
.cloud_services
|
.cloud_services
|
||||||
@ -494,12 +524,12 @@ impl UserManager {
|
|||||||
},
|
},
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
// If the user is not found, notify the frontend to logout
|
// If the user is not found, notify the frontend to logout
|
||||||
if err.is_record_not_found() {
|
if err.is_unauthorized() {
|
||||||
event!(
|
event!(
|
||||||
tracing::Level::INFO,
|
tracing::Level::ERROR,
|
||||||
"User is not found on the server when refreshing profile"
|
"User is unauthorized, sign out the user"
|
||||||
);
|
);
|
||||||
|
self.sign_out().await?;
|
||||||
send_auth_state_notification(AuthStateChangedPB {
|
send_auth_state_notification(AuthStateChangedPB {
|
||||||
state: AuthStatePB::InvalidAuth,
|
state: AuthStatePB::InvalidAuth,
|
||||||
message: "User is not found on the server".to_string(),
|
message: "User is not found on the server".to_string(),
|
||||||
@ -641,6 +671,7 @@ impl UserManager {
|
|||||||
Ok(url)
|
Ok(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "info", skip_all, err)]
|
||||||
async fn save_auth_data(
|
async fn save_auth_data(
|
||||||
&self,
|
&self,
|
||||||
response: &impl UserAuthResponse,
|
response: &impl UserAuthResponse,
|
||||||
|
@ -5,11 +5,13 @@ use collab::core::origin::{CollabClient, CollabOrigin};
|
|||||||
use collab_document::document::Document;
|
use collab_document::document::Document;
|
||||||
use collab_document::document_data::default_document_data;
|
use collab_document::document_data::default_document_data;
|
||||||
use collab_folder::Folder;
|
use collab_folder::Folder;
|
||||||
|
use tracing::{event, instrument};
|
||||||
|
|
||||||
use collab_integrate::{RocksCollabDB, YrsDocAction};
|
use collab_integrate::{RocksCollabDB, YrsDocAction};
|
||||||
use flowy_error::{internal_error, FlowyResult};
|
use flowy_error::{internal_error, FlowyResult};
|
||||||
|
|
||||||
use crate::migrations::migration::UserDataMigration;
|
use crate::migrations::migration::UserDataMigration;
|
||||||
|
use crate::migrations::util::load_collab;
|
||||||
use crate::services::entities::Session;
|
use crate::services::entities::Session;
|
||||||
|
|
||||||
/// Migrate the first level documents of the workspace by inserting documents
|
/// Migrate the first level documents of the workspace by inserting documents
|
||||||
@ -20,39 +22,42 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
|
|||||||
"historical_empty_document"
|
"historical_empty_document"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(name = "HistoricalEmptyDocumentMigration", skip_all, err)]
|
||||||
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> {
|
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> {
|
||||||
let write_txn = collab_db.write_txn();
|
let write_txn = collab_db.write_txn();
|
||||||
if let Ok(updates) = write_txn.get_all_updates(session.user_id, &session.user_workspace.id) {
|
let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom"));
|
||||||
let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom"));
|
// Deserialize the folder from the raw data
|
||||||
// Deserialize the folder from the raw data
|
if let Ok(folder_collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id)
|
||||||
let folder = Folder::from_collab_raw_data(
|
{
|
||||||
session.user_id,
|
let folder = Folder::open(session.user_id, folder_collab, None)?;
|
||||||
origin.clone(),
|
|
||||||
updates,
|
|
||||||
&session.user_workspace.id,
|
|
||||||
vec![],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Migration the first level documents of the workspace
|
// Migration the first level documents of the workspace. The first level documents do not have
|
||||||
|
// any updates. So when calling load_collab, it will return error.
|
||||||
let migration_views = folder.get_workspace_views(&session.user_workspace.id);
|
let migration_views = folder.get_workspace_views(&session.user_workspace.id);
|
||||||
for view in migration_views {
|
for view in migration_views {
|
||||||
// Read all updates of the view
|
if load_collab(session.user_id, &write_txn, &view.id).is_err() {
|
||||||
if let Ok(view_updates) = write_txn.get_all_updates(session.user_id, &view.id) {
|
// Create a document with default data
|
||||||
if Document::from_updates(origin.clone(), view_updates, &view.id, vec![]).is_err() {
|
let document_data = default_document_data();
|
||||||
// Create a document with default data
|
let collab = Arc::new(MutexCollab::new(origin.clone(), &view.id, vec![]));
|
||||||
let document_data = default_document_data();
|
if let Ok(document) = Document::create_with_data(collab.clone(), document_data) {
|
||||||
let collab = Arc::new(MutexCollab::new(origin.clone(), &view.id, vec![]));
|
// Remove all old updates and then insert the new update
|
||||||
if let Ok(document) = Document::create_with_data(collab.clone(), document_data) {
|
let (doc_state, sv) = document.get_collab().encode_as_update_v1();
|
||||||
// Remove all old updates and then insert the new update
|
if let Err(err) = write_txn.flush_doc_with(session.user_id, &view.id, &doc_state, &sv) {
|
||||||
let (doc_state, sv) = document.get_collab().encode_as_update_v1();
|
event!(
|
||||||
write_txn
|
tracing::Level::ERROR,
|
||||||
.flush_doc_with(session.user_id, &view.id, &doc_state, &sv)
|
"Failed to migrate document {}, error: {}",
|
||||||
.map_err(internal_error)?;
|
view.id,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
event!(tracing::Level::INFO, "Did migrate document {}", view.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
event!(tracing::Level::INFO, "Save all migrated documents");
|
||||||
write_txn.commit_transaction().map_err(internal_error)?;
|
write_txn.commit_transaction().map_err(internal_error)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ use std::sync::Arc;
|
|||||||
|
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use diesel::{RunQueryDsl, SqliteConnection};
|
use diesel::{RunQueryDsl, SqliteConnection};
|
||||||
use tracing::event;
|
|
||||||
|
|
||||||
use collab_integrate::RocksCollabDB;
|
use collab_integrate::RocksCollabDB;
|
||||||
use flowy_error::FlowyResult;
|
use flowy_error::FlowyResult;
|
||||||
@ -55,12 +54,6 @@ impl UserLocalDataMigration {
|
|||||||
{
|
{
|
||||||
let migration_name = migration.name().to_string();
|
let migration_name = migration.name().to_string();
|
||||||
if !duplicated_names.contains(&migration_name) {
|
if !duplicated_names.contains(&migration_name) {
|
||||||
event!(
|
|
||||||
tracing::Level::INFO,
|
|
||||||
"Running migration {}",
|
|
||||||
migration.name()
|
|
||||||
);
|
|
||||||
|
|
||||||
migration.run(&self.session, &self.collab_db)?;
|
migration.run(&self.session, &self.collab_db)?;
|
||||||
applied_migrations.push(migration.name().to_string());
|
applied_migrations.push(migration.name().to_string());
|
||||||
save_record(&conn, &migration_name);
|
save_record(&conn, &migration_name);
|
||||||
|
@ -3,4 +3,5 @@ pub use define::*;
|
|||||||
mod define;
|
mod define;
|
||||||
pub mod document_empty_content;
|
pub mod document_empty_content;
|
||||||
pub mod migration;
|
pub mod migration;
|
||||||
|
mod util;
|
||||||
pub mod workspace_and_favorite_v1;
|
pub mod workspace_and_favorite_v1;
|
||||||
|
23
frontend/rust-lib/flowy-user/src/migrations/util.rs
Normal file
23
frontend/rust-lib/flowy-user/src/migrations/util.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use collab::core::collab::MutexCollab;
|
||||||
|
use collab::preclude::Collab;
|
||||||
|
|
||||||
|
use collab_integrate::{PersistenceError, YrsDocAction};
|
||||||
|
use flowy_error::{internal_error, FlowyResult};
|
||||||
|
|
||||||
|
pub fn load_collab<'a, R>(
|
||||||
|
uid: i64,
|
||||||
|
collab_r_txn: &R,
|
||||||
|
object_id: &str,
|
||||||
|
) -> FlowyResult<Arc<MutexCollab>>
|
||||||
|
where
|
||||||
|
R: YrsDocAction<'a>,
|
||||||
|
PersistenceError: From<R::Error>,
|
||||||
|
{
|
||||||
|
let collab = Collab::new(uid, object_id, "phantom", vec![]);
|
||||||
|
collab
|
||||||
|
.with_origin_transact_mut(|txn| collab_r_txn.load_doc_with_txn(uid, &object_id, txn))
|
||||||
|
.map_err(internal_error)?;
|
||||||
|
Ok(Arc::new(MutexCollab::from_collab(collab)))
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collab::core::origin::{CollabClient, CollabOrigin};
|
|
||||||
use collab_folder::Folder;
|
use collab_folder::Folder;
|
||||||
|
use tracing::instrument;
|
||||||
|
|
||||||
use collab_integrate::{RocksCollabDB, YrsDocAction};
|
use collab_integrate::{RocksCollabDB, YrsDocAction};
|
||||||
use flowy_error::{internal_error, FlowyResult};
|
use flowy_error::{internal_error, FlowyResult};
|
||||||
|
|
||||||
use crate::migrations::migration::UserDataMigration;
|
use crate::migrations::migration::UserDataMigration;
|
||||||
|
use crate::migrations::util::load_collab;
|
||||||
use crate::services::entities::Session;
|
use crate::services::entities::Session;
|
||||||
|
|
||||||
/// 1. Migrate the workspace: { favorite: [view_id] } to { favorite: { uid: [view_id] } }
|
/// 1. Migrate the workspace: { favorite: [view_id] } to { favorite: { uid: [view_id] } }
|
||||||
@ -19,19 +20,11 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration {
|
|||||||
"workspace_favorite_v1_and_workspace_array_migration"
|
"workspace_favorite_v1_and_workspace_array_migration"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(name = "FavoriteV1AndWorkspaceArrayMigration", skip_all, err)]
|
||||||
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> {
|
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> {
|
||||||
let write_txn = collab_db.write_txn();
|
let write_txn = collab_db.write_txn();
|
||||||
if let Ok(updates) = write_txn.get_all_updates(session.user_id, &session.user_workspace.id) {
|
if let Ok(collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id) {
|
||||||
let origin = CollabOrigin::Client(CollabClient::new(session.user_id, "phantom"));
|
let folder = Folder::open(session.user_id, collab, None)?;
|
||||||
// Deserialize the folder from the raw data
|
|
||||||
let folder = Folder::from_collab_raw_data(
|
|
||||||
session.user_id,
|
|
||||||
origin,
|
|
||||||
updates,
|
|
||||||
&session.user_workspace.id,
|
|
||||||
vec![],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
folder.migrate_workspace_to_view();
|
folder.migrate_workspace_to_view();
|
||||||
|
|
||||||
let favorite_view_ids = folder
|
let favorite_view_ids = folder
|
||||||
@ -48,8 +41,9 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration {
|
|||||||
write_txn
|
write_txn
|
||||||
.flush_doc_with(session.user_id, &session.user_workspace.id, &doc_state, &sv)
|
.flush_doc_with(session.user_id, &session.user_workspace.id, &doc_state, &sv)
|
||||||
.map_err(internal_error)?;
|
.map_err(internal_error)?;
|
||||||
write_txn.commit_transaction().map_err(internal_error)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
write_txn.commit_transaction().map_err(internal_error)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use std::convert::TryFrom;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use collab_entity::{CollabObject, CollabType};
|
use collab_entity::{CollabObject, CollabType};
|
||||||
|
use tracing::{error, instrument};
|
||||||
|
|
||||||
use flowy_error::{FlowyError, FlowyResult};
|
use flowy_error::{FlowyError, FlowyResult};
|
||||||
use flowy_sqlite::schema::user_workspace_table;
|
use flowy_sqlite::schema::user_workspace_table;
|
||||||
@ -15,8 +16,14 @@ use crate::notification::{send_notification, UserNotification};
|
|||||||
use crate::services::user_workspace_sql::UserWorkspaceTable;
|
use crate::services::user_workspace_sql::UserWorkspaceTable;
|
||||||
|
|
||||||
impl UserManager {
|
impl UserManager {
|
||||||
|
#[instrument(skip(self), err)]
|
||||||
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
|
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
|
||||||
let uid = self.user_id()?;
|
let uid = self.user_id()?;
|
||||||
|
let _ = self
|
||||||
|
.cloud_services
|
||||||
|
.get_user_service()?
|
||||||
|
.open_workspace(workspace_id)
|
||||||
|
.await;
|
||||||
if let Some(user_workspace) = self.get_user_workspace(uid, workspace_id) {
|
if let Some(user_workspace) = self.get_user_workspace(uid, workspace_id) {
|
||||||
if let Err(err) = self
|
if let Err(err) = self
|
||||||
.user_status_callback
|
.user_status_callback
|
||||||
@ -25,7 +32,7 @@ impl UserManager {
|
|||||||
.open_workspace(uid, &user_workspace)
|
.open_workspace(uid, &user_workspace)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
tracing::error!("Open workspace failed: {:?}", err);
|
error!("Open workspace failed: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -101,7 +108,7 @@ impl UserManager {
|
|||||||
if let Ok(service) = self.cloud_services.get_user_service() {
|
if let Ok(service) = self.cloud_services.get_user_service() {
|
||||||
if let Ok(pool) = self.db_pool(uid) {
|
if let Ok(pool) = self.db_pool(uid) {
|
||||||
af_spawn(async move {
|
af_spawn(async move {
|
||||||
if let Ok(new_user_workspaces) = service.get_all_user_workspaces(uid).await {
|
if let Ok(new_user_workspaces) = service.get_all_workspace(uid).await {
|
||||||
let _ = save_user_workspaces(uid, pool, &new_user_workspaces);
|
let _ = save_user_workspaces(uid, pool, &new_user_workspaces);
|
||||||
let repeated_workspace_pbs = RepeatedUserWorkspacePB::from(new_user_workspaces);
|
let repeated_workspace_pbs = RepeatedUserWorkspacePB::from(new_user_workspaces);
|
||||||
send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces)
|
send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces)
|
||||||
|
@ -7,7 +7,6 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
tracing-log = { version = "0.2"}
|
|
||||||
tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter", "ansi", "json"] }
|
tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter", "ansi", "json"] }
|
||||||
tracing-bunyan-formatter = "0.3.9"
|
tracing-bunyan-formatter = "0.3.9"
|
||||||
tracing-appender = "0.2.2"
|
tracing-appender = "0.2.2"
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use std::sync::RwLock;
|
use std::sync::RwLock;
|
||||||
|
|
||||||
|
use chrono::Local;
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use tracing::subscriber::set_global_default;
|
use tracing::subscriber::set_global_default;
|
||||||
use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender};
|
use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender};
|
||||||
use tracing_bunyan_formatter::JsonStorageLayer;
|
use tracing_bunyan_formatter::JsonStorageLayer;
|
||||||
|
use tracing_subscriber::fmt::format::Writer;
|
||||||
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
|
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
|
||||||
|
|
||||||
use crate::layer::FlowyFormattingLayer;
|
use crate::layer::FlowyFormattingLayer;
|
||||||
@ -43,12 +45,12 @@ impl Builder {
|
|||||||
|
|
||||||
let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
|
let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
|
||||||
let subscriber = tracing_subscriber::fmt()
|
let subscriber = tracing_subscriber::fmt()
|
||||||
|
.with_timer(CustomTime)
|
||||||
.with_ansi(true)
|
.with_ansi(true)
|
||||||
.with_target(true)
|
.with_target(false)
|
||||||
.with_max_level(tracing::Level::TRACE)
|
.with_max_level(tracing::Level::TRACE)
|
||||||
.with_thread_ids(false)
|
.with_thread_ids(false)
|
||||||
.with_file(false)
|
.with_writer(std::io::stdout)
|
||||||
.with_writer(std::io::stderr)
|
|
||||||
.pretty()
|
.pretty()
|
||||||
.with_env_filter(env_filter)
|
.with_env_filter(env_filter)
|
||||||
.finish()
|
.finish()
|
||||||
@ -56,7 +58,15 @@ impl Builder {
|
|||||||
.with(FlowyFormattingLayer::new(non_blocking));
|
.with(FlowyFormattingLayer::new(non_blocking));
|
||||||
|
|
||||||
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
|
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
|
||||||
|
|
||||||
*LOG_GUARD.write().unwrap() = Some(guard);
|
*LOG_GUARD.write().unwrap() = Some(guard);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct CustomTime;
|
||||||
|
impl tracing_subscriber::fmt::time::FormatTime for CustomTime {
|
||||||
|
fn format_time(&self, w: &mut Writer<'_>) -> std::fmt::Result {
|
||||||
|
write!(w, "{}", Local::now().format("%Y-%m-%d %H:%M:%S"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user