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:
Nathan.fooo 2023-11-05 14:00:24 +08:00 committed by GitHub
parent b35d6131d4
commit 1025b6d553
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 658 additions and 478 deletions

View File

@ -1,23 +1,33 @@
# 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.
# Update the environment parameters as needed.
# Configuring Cloud Type
# This configuration file is used to specify the cloud type and the necessary configurations for each cloud type. The available options are:
# 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
# Supabase: 1
# AppFlowy Cloud: 2
# By default, it's set to Local.
CLOUD_TYPE=0
# 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_ANON_KEY=replace-with-your-supabase-key
# 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_WS_BASE_URL=replace-with-your-appflowy-cloud-ws-url
APPFLOWY_CLOUD_GOTRUE_URL=replace-with-your-appflowy-cloud-gotrue-url

View File

@ -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
// file `env_serde.g.dart` is existed, delete it first.
//
// the file `env_serde.g.dart` will be generated in the same directory.
part 'env_serde.g.dart';
import 'package:json_annotation/json_annotation.dart';
part 'backend_env.g.dart';
@JsonSerializable()
class AppFlowyEnv {

View File

@ -1,8 +1,9 @@
import 'dart:convert';
import 'dart:io';
import 'package:appflowy/env/backend_env.dart';
import 'package:appflowy/env/env.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/path.dart' as path;
@ -23,8 +24,10 @@ class InitRustSDKTask extends LaunchTask {
Future<void> initialize(LaunchContext context) async {
final dir = directory ?? await appFlowyApplicationDataDirectory();
// Pass the environment variables to the Rust SDK
final env = getAppFlowyEnv();
context.getIt<FlowySDK>().setEnv(env);
context.getIt<FlowySDK>().setEnv(jsonEncode(env.toJson()));
await context.getIt<FlowySDK>().init(dir);
}

View File

@ -100,14 +100,9 @@ class UserBackendService {
return Future.value(left([]));
}
Future<Either<WorkspacePB, FlowyError>> openWorkspace(String workspaceId) {
final request = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventOpenWorkspace(request).send().then((result) {
return result.fold(
(workspace) => left(workspace),
(error) => right(error),
);
});
Future<Either<Unit, FlowyError>> openWorkspace(String workspaceId) {
final payload = UserWorkspaceIdPB.create()..workspaceId = workspaceId;
return UserEventOpenWorkspace(payload).send();
}
Future<Either<WorkspacePB, FlowyError>> getCurrentWorkspace() {

View File

@ -101,6 +101,5 @@ Widget _renderCreateButton(BuildContext context) {
// same method as in mobile
void _popToWorkspace(BuildContext context, WorkspacePB workspace) {
context.read<WorkspaceBloc>().add(WorkspaceEvent.openWorkspace(workspace));
context.pop(workspace.id);
}

View File

@ -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/widget/error_page.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart';
// TODO(yijing): needs refactor when multiple workspaces are supported
@ -139,6 +138,5 @@ class _MobileWorkspaceStartScreenState
// same method as in desktop
void _popToWorkspace(BuildContext context, WorkspacePB workspace) {
context.read<WorkspaceBloc>().add(WorkspaceEvent.openWorkspace(workspace));
context.pop(workspace.id);
}

View File

@ -19,9 +19,6 @@ class WorkspaceBloc extends Bloc<WorkspaceEvent, WorkspaceState> {
initial: (e) async {
await _fetchWorkspaces(emit);
},
openWorkspace: (e) async {
await _openWorkspace(e.workspace, emit);
},
createWorkspace: (e) async {
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(
String name,
String desc,
@ -98,8 +79,6 @@ class WorkspaceEvent with _$WorkspaceEvent {
const factory WorkspaceEvent.initial() = Initial;
const factory WorkspaceEvent.createWorkspace(String name, String desc) =
CreateWorkspace;
const factory WorkspaceEvent.openWorkspace(WorkspacePB workspace) =
OpenWorkspace;
const factory WorkspaceEvent.workspacesReveived(
Either<List<WorkspacePB>, FlowyError> workspacesOrFail,
) = WorkspacesReceived;

View File

@ -1,11 +1,9 @@
export 'package:async/async.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:async';
import 'package:appflowy_backend/rust_stream.dart';
import 'package:flutter/services.dart';
import 'dart:ffi';
import 'env_serde.dart';
import 'ffi.dart' as ffi;
import 'package:ffi/ffi.dart';
@ -37,8 +35,7 @@ class FlowySDK {
ffi.init_sdk(sdkDir.path.toNativeUtf8());
}
void setEnv(AppFlowyEnv env) {
final jsonStr = jsonEncode(env.toJson());
ffi.set_env(jsonStr.toNativeUtf8());
void setEnv(String envStr) {
ffi.set_env(envStr.toNativeUtf8());
}
}

View File

@ -135,6 +135,21 @@ version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "appflowy_tauri"
version = "0.0.0"
@ -445,7 +460,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
dependencies = [
"borsh-derive",
"hashbrown 0.12.3",
"hashbrown 0.13.2",
]
[[package]]
@ -753,9 +768,10 @@ dependencies = [
[[package]]
name = "client-api"
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 = [
"anyhow",
"app-error",
"bytes",
"collab",
"collab-entity",
@ -845,7 +861,7 @@ dependencies = [
[[package]]
name = "collab"
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 = [
"anyhow",
"async-trait",
@ -864,7 +880,7 @@ dependencies = [
[[package]]
name = "collab-database"
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 = [
"anyhow",
"async-trait",
@ -894,7 +910,7 @@ dependencies = [
[[package]]
name = "collab-derive"
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 = [
"proc-macro2",
"quote",
@ -906,7 +922,7 @@ dependencies = [
[[package]]
name = "collab-document"
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 = [
"anyhow",
"collab",
@ -926,7 +942,7 @@ dependencies = [
[[package]]
name = "collab-entity"
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 = [
"anyhow",
"bytes",
@ -940,7 +956,7 @@ dependencies = [
[[package]]
name = "collab-folder"
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 = [
"anyhow",
"chrono",
@ -982,7 +998,7 @@ dependencies = [
[[package]]
name = "collab-persistence"
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 = [
"async-trait",
"bincode",
@ -1003,7 +1019,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
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 = [
"anyhow",
"async-trait",
@ -1030,7 +1046,7 @@ dependencies = [
[[package]]
name = "collab-user"
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 = [
"anyhow",
"collab",
@ -1429,9 +1445,10 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
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 = [
"anyhow",
"app-error",
"chrono",
"collab-entity",
"serde",
@ -2064,7 +2081,7 @@ dependencies = [
"nanoid",
"parking_lot",
"protobuf",
"scraper 0.18.0",
"scraper 0.18.1",
"serde",
"serde_json",
"strum_macros 0.21.1",
@ -2778,7 +2795,7 @@ dependencies = [
[[package]]
name = "gotrue"
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 = [
"anyhow",
"futures-util",
@ -2794,9 +2811,10 @@ dependencies = [
[[package]]
name = "gotrue-entity"
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 = [
"anyhow",
"app-error",
"jsonwebtoken",
"lazy_static",
"reqwest",
@ -3229,7 +3247,7 @@ dependencies = [
[[package]]
name = "infra"
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 = [
"anyhow",
"reqwest",
@ -3479,7 +3497,6 @@ dependencies = [
"tracing-appender",
"tracing-bunyan-formatter",
"tracing-core",
"tracing-log 0.2.0",
"tracing-subscriber",
]
@ -4904,8 +4921,9 @@ dependencies = [
[[package]]
name = "realtime-entity"
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 = [
"anyhow",
"bytes",
"collab",
"collab-entity",
@ -5358,9 +5376,9 @@ dependencies = [
[[package]]
name = "scraper"
version = "0.18.0"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3693f9a0203d49a7ba8f38aa915316b3d535c1862d03dae7009cb71a3408b36a"
checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1"
dependencies = [
"ahash 0.8.3",
"cssparser 0.31.2",
@ -5642,9 +5660,10 @@ dependencies = [
[[package]]
name = "shared_entity"
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 = [
"anyhow",
"app-error",
"collab-entity",
"database-entity",
"gotrue-entity",
@ -6689,7 +6708,7 @@ dependencies = [
"time",
"tracing",
"tracing-core",
"tracing-log 0.1.3",
"tracing-log",
"tracing-subscriber",
]
@ -6714,17 +6733,6 @@ dependencies = [
"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]]
name = "tracing-serde"
version = "0.1.3"
@ -6752,7 +6760,7 @@ dependencies = [
"thread_local",
"tracing",
"tracing-core",
"tracing-log 0.1.3",
"tracing-log",
"tracing-serde",
]

View File

@ -38,7 +38,7 @@ custom-protocol = ["tauri/custom-protocol"]
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c87d0f05e988a02e9272a42722b304289be320e4" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c739029d99517282d2ec1593523a47b51a85231b" }
# Please use the following script to update collab.
# 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:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-persistence = { 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 = "bab20052" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }

View File

@ -15,13 +15,11 @@ import {
SignInPayloadPB,
SignUpPayloadPB,
UpdateUserProfilePayloadPB,
WorkspaceIdPB,
WorkspacePB,
WorkspaceSettingPB,
} from '@/services/backend';
import {
FolderEventCreateWorkspace,
FolderEventOpenWorkspace,
FolderEventGetCurrentWorkspaceSetting,
FolderEventReadCurrentWorkspace,
} from '@/services/backend/events/flowy-folder2';
@ -67,12 +65,6 @@ export class UserBackendService {
return FolderEventReadCurrentWorkspace();
};
openWorkspace = (workspaceId: string) => {
const payload = WorkspaceIdPB.fromObject({ value: workspaceId });
return FolderEventOpenWorkspace(payload);
};
createWorkspace = async (params: { name: string; desc: string }): Promise<WorkspacePB> => {
const payload = CreateWorkspacePayloadPB.fromObject({ name: params.name, desc: params.desc });
const result = await FolderEventCreateWorkspace(payload);

View File

@ -1,12 +1,12 @@
import {
FolderEventCreateWorkspace,
CreateWorkspacePayloadPB,
FolderEventOpenWorkspace,
FolderEventDeleteWorkspace,
WorkspaceIdPB,
FolderEventReadWorkspaceViews,
FolderEventReadCurrentWorkspace,
} from '@/services/backend/events/flowy-folder2';
import { UserEventOpenWorkspace, UserWorkspaceIdPB } from '@/services/backend/events/flowy-user';
export class WorkspaceBackendService {
constructor() {
@ -24,11 +24,11 @@ export class WorkspaceBackendService {
};
openWorkspace = async (workspaceId: string) => {
const payload = new WorkspaceIdPB({
value: workspaceId,
const payload = new UserWorkspaceIdPB({
workspace_id: workspaceId,
});
return FolderEventOpenWorkspace(payload);
return UserEventOpenWorkspace(payload);
};
deleteWorkspace = async (workspaceId: string) => {

View File

@ -121,6 +121,21 @@ version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "arrayvec"
version = "0.5.2"
@ -452,7 +467,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b"
dependencies = [
"borsh-derive",
"hashbrown 0.12.3",
"hashbrown 0.13.2",
]
[[package]]
@ -651,9 +666,10 @@ dependencies = [
[[package]]
name = "client-api"
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 = [
"anyhow",
"app-error",
"bytes",
"collab",
"collab-entity",
@ -712,7 +728,7 @@ dependencies = [
[[package]]
name = "collab"
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 = [
"anyhow",
"async-trait",
@ -731,7 +747,7 @@ dependencies = [
[[package]]
name = "collab-database"
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 = [
"anyhow",
"async-trait",
@ -761,7 +777,7 @@ dependencies = [
[[package]]
name = "collab-derive"
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 = [
"proc-macro2",
"quote",
@ -773,7 +789,7 @@ dependencies = [
[[package]]
name = "collab-document"
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 = [
"anyhow",
"collab",
@ -793,7 +809,7 @@ dependencies = [
[[package]]
name = "collab-entity"
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 = [
"anyhow",
"bytes",
@ -807,7 +823,7 @@ dependencies = [
[[package]]
name = "collab-folder"
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 = [
"anyhow",
"chrono",
@ -849,7 +865,7 @@ dependencies = [
[[package]]
name = "collab-persistence"
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 = [
"async-trait",
"bincode",
@ -870,7 +886,7 @@ dependencies = [
[[package]]
name = "collab-plugins"
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 = [
"anyhow",
"async-trait",
@ -897,7 +913,7 @@ dependencies = [
[[package]]
name = "collab-user"
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 = [
"anyhow",
"collab",
@ -1256,9 +1272,10 @@ checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308"
[[package]]
name = "database-entity"
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 = [
"anyhow",
"app-error",
"chrono",
"collab-entity",
"serde",
@ -1885,7 +1902,7 @@ dependencies = [
"nanoid",
"parking_lot",
"protobuf",
"scraper 0.18.0",
"scraper 0.18.1",
"serde",
"serde_json",
"strum_macros 0.21.1",
@ -2437,7 +2454,7 @@ dependencies = [
[[package]]
name = "gotrue"
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 = [
"anyhow",
"futures-util",
@ -2453,9 +2470,10 @@ dependencies = [
[[package]]
name = "gotrue-entity"
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 = [
"anyhow",
"app-error",
"jsonwebtoken",
"lazy_static",
"reqwest",
@ -2813,7 +2831,7 @@ dependencies = [
[[package]]
name = "infra"
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 = [
"anyhow",
"reqwest",
@ -2979,7 +2997,6 @@ dependencies = [
"tracing-appender",
"tracing-bunyan-formatter",
"tracing-core",
"tracing-log 0.2.0",
"tracing-subscriber",
]
@ -4254,8 +4271,9 @@ dependencies = [
[[package]]
name = "realtime-entity"
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 = [
"anyhow",
"bytes",
"collab",
"collab-entity",
@ -4705,9 +4723,9 @@ dependencies = [
[[package]]
name = "scraper"
version = "0.18.0"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3693f9a0203d49a7ba8f38aa915316b3d535c1862d03dae7009cb71a3408b36a"
checksum = "585480e3719b311b78a573db1c9d9c4c1f8010c2dee4cc59c2efe58ea4dbc3e1"
dependencies = [
"ahash 0.8.3",
"cssparser",
@ -4810,9 +4828,9 @@ dependencies = [
[[package]]
name = "serde_json"
version = "1.0.105"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
dependencies = [
"itoa",
"ryu",
@ -4891,9 +4909,10 @@ dependencies = [
[[package]]
name = "shared_entity"
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 = [
"anyhow",
"app-error",
"collab-entity",
"database-entity",
"gotrue-entity",
@ -5654,7 +5673,7 @@ dependencies = [
"time",
"tracing",
"tracing-core",
"tracing-log 0.1.3",
"tracing-log",
"tracing-subscriber",
]
@ -5679,17 +5698,6 @@ dependencies = [
"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]]
name = "tracing-serde"
version = "0.1.3"
@ -5717,7 +5725,7 @@ dependencies = [
"thread_local",
"tracing",
"tracing-core",
"tracing-log 0.1.3",
"tracing-log",
"tracing-serde",
]

View File

@ -82,7 +82,7 @@ incremental = false
# Run the script:
# scripts/tool/update_client_api_rev.sh new_rev_id
# ⚠️⚠️⚠️️
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c87d0f05e988a02e9272a42722b304289be320e4" }
client-api = { git = "https://github.com/AppFlowy-IO/AppFlowy-Cloud", rev = "c739029d99517282d2ec1593523a47b51a85231b" }
# Please use the following script to update collab.
# 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:
# scripts/tool/update_collab_source.sh
# ⚠️⚠️⚠️️
collab = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-folder = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "01f92f7bb" }
collab-persistence = { 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 = "bab20052" }
collab-document = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-database = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-plugins = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-user = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-entity = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }
collab-persistence = { git = "https://github.com/AppFlowy-IO/AppFlowy-Collab", rev = "bab20052" }

View File

@ -144,19 +144,10 @@ pub struct ViewTest {
pub workspace: WorkspacePB,
pub child_view: ViewPB,
}
impl ViewTest {
#[allow(dead_code)]
pub async fn new(sdk: &EventIntegrationTest, layout: ViewLayoutPB, data: Vec<u8>) -> Self {
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 {
parent_view_id: workspace.id.clone(),

View File

@ -22,7 +22,7 @@ async fn af_cloud_edit_document_test() {
let rx = test
.notification_sender
.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
.unwrap();

View File

@ -5,7 +5,6 @@ use std::sync::{Arc, Weak};
use parking_lot::RwLock;
use serde_repr::*;
use collab_integrate::YrsDocAction;
use flowy_error::{FlowyError, FlowyResult};
use flowy_server::af_cloud::AFCloudServer;
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::supabase_config::SupabaseConfiguration;
use flowy_sqlite::kv::StorePreferences;
use flowy_user::services::database::{
get_user_profile, get_user_workspace, open_collab_db, open_user_db,
};
use flowy_user::services::database::{get_user_profile, get_user_workspace, open_user_db};
use flowy_user_deps::cloud::UserCloudService;
use flowy_user_deps::entities::*;
@ -195,14 +192,4 @@ impl LocalServerDB for LocalServerDBImpl {
let user_workspace = get_user_workspace(&sqlite_db, uid)?;
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)
}
}

View File

@ -17,7 +17,9 @@ use flowy_database_deps::cloud::{
use flowy_document2::deps::DocumentData;
use flowy_document_deps::cloud::{DocumentCloudService, DocumentSnapshot};
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_user::event_map::UserCloudServiceProvider;
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 })
}
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(
&self,
workspace_id: &str,
@ -245,7 +258,7 @@ impl DocumentCloudService for ServerProvider {
&self,
document_id: &str,
workspace_id: &str,
) -> FutureResult<Vec<Vec<u8>>, Error> {
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
let workspace_id = workspace_id.to_string();
let document_id = document_id.to_string();
let server = self.get_server(&self.get_server_type());
@ -308,7 +321,7 @@ impl CollabStorageProvider for ServerProvider {
to_fut(async move {
let mut plugins: Vec<Arc<dyn CollabPlugin>> = vec![];
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(
collab_object.uid,
collab_object.device_id.clone(),
@ -316,7 +329,7 @@ impl CollabStorageProvider for ServerProvider {
let sync_object = SyncObject::from(collab_object);
let (sink, stream) = (channel.sink(), channel.stream());
let sink_config = SinkConfig::new()
.send_timeout(6)
.send_timeout(8)
.with_strategy(SinkStrategy::FixInterval(Duration::from_secs(2)));
let sync_plugin = SyncPlugin::new(
origin,
@ -326,6 +339,7 @@ impl CollabStorageProvider for ServerProvider {
sink_config,
stream,
Some(channel),
!is_connected,
ws_connect_state,
);
plugins.push(Arc::new(sync_plugin));

View File

@ -1,12 +1,13 @@
use std::sync::Arc;
use anyhow::Context;
use tracing::event;
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use flowy_database2::DatabaseManager;
use flowy_document2::manager::DocumentManager;
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_deps::cloud::UserCloudConfig;
use flowy_user_deps::entities::{AuthType, UserProfile, UserWorkspace};
@ -59,7 +60,7 @@ impl UserStatusCallback for UserStatusCallbackImpl {
.initialize(
user_id,
&user_workspace.id,
FolderInitializeDataSource::LocalDisk {
FolderInitDataSource::LocalDisk {
create_if_not_exist: false,
},
)
@ -82,8 +83,9 @@ impl UserStatusCallback for UserStatusCallbackImpl {
&self,
user_id: i64,
user_workspace: &UserWorkspace,
_device_id: &str,
device_id: &str,
) -> Fut<FlowyResult<()>> {
let device_id = device_id.to_owned();
let user_id = user_id.to_owned();
let user_workspace = user_workspace.clone();
let folder_manager = self.folder_manager.clone();
@ -91,6 +93,13 @@ impl UserStatusCallback for UserStatusCallbackImpl {
let document_manager = self.document_manager.clone();
to_fut(async move {
event!(
tracing::Level::TRACE,
"Notify did sign in: latest_workspace: {:?}, device_id: {}",
user_workspace,
device_id
);
folder_manager
.initialize_with_workspace_id(user_id, &user_workspace.id)
.await?;
@ -113,8 +122,9 @@ impl UserStatusCallback for UserStatusCallbackImpl {
is_new_user: bool,
user_profile: &UserProfile,
user_workspace: &UserWorkspace,
_device_id: &str,
device_id: &str,
) -> Fut<FlowyResult<()>> {
let device_id = device_id.to_owned();
let user_profile = user_profile.clone();
let folder_manager = self.folder_manager.clone();
let database_manager = self.database_manager.clone();
@ -122,12 +132,20 @@ impl UserStatusCallback for UserStatusCallbackImpl {
let document_manager = self.document_manager.clone();
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
.initialize_with_new_user(
user_profile.uid,
&user_profile.token,
is_new_user,
FolderInitializeDataSource::LocalDisk {
FolderInitDataSource::LocalDisk {
create_if_not_exist: true,
},
&user_workspace.id,

View File

@ -196,7 +196,6 @@ impl AppFlowyCore {
let cloned_user_session = Arc::downgrade(&user_manager);
if let Some(user_session) = cloned_user_session.upgrade() {
event!(tracing::Level::DEBUG, "init user session",);
if let Err(err) = user_session
.init(user_status_callback, collab_interact_impl)
.await

View File

@ -13,7 +13,7 @@ use collab_database::views::{CreateDatabaseParams, CreateViewParams, DatabaseLay
use collab_entity::CollabType;
use futures::executor::block_on;
use tokio::sync::RwLock;
use tracing::{instrument, trace};
use tracing::{event, instrument, trace};
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use collab_integrate::{CollabPersistenceConfig, RocksCollabDB};
@ -80,6 +80,11 @@ impl DatabaseManager {
workspace_id: String,
database_views_aggregate_id: String,
) -> 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_builder = UserDatabaseCollabServiceImpl {
workspace_id: workspace_id.clone(),
@ -114,7 +119,11 @@ impl DatabaseManager {
}
// 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(
uid,
&database_views_aggregate_id,

View File

@ -1,6 +1,7 @@
use anyhow::Error;
pub use collab_document::blocks::DocumentData;
use flowy_error::FlowyError;
use lib_infra::future::FutureResult;
/// A trait for document cloud service.
@ -11,7 +12,7 @@ pub trait DocumentCloudService: Send + Sync + 'static {
&self,
document_id: &str,
workspace_id: &str,
) -> FutureResult<Vec<Vec<u8>>, Error>;
) -> FutureResult<Vec<Vec<u8>>, FlowyError>;
fn get_document_snapshots(
&self,

View File

@ -1,14 +1,14 @@
use std::sync::Weak;
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::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_entity::CollabType;
use parking_lot::RwLock;
use tracing::instrument;
use tracing::{event, instrument};
use collab_integrate::collab_builder::AppFlowyCollabBuilder;
use collab_integrate::RocksCollabDB;
@ -109,10 +109,29 @@ impl DocumentManager {
let mut updates = vec![];
if !self.is_doc_exist(doc_id)? {
// Try to get the document from the cloud service
updates = self
let result: Result<CollabRawData, FlowyError> = self
.cloud_service
.get_document_updates(&self.user.workspace_id()?, doc_id)
.await?;
.get_document_updates(doc_id, &self.user.workspace_id()?)
.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()?;

View File

@ -129,7 +129,7 @@ impl DocumentCloudService for LocalTestDocumentCloudServiceImpl {
&self,
_document_id: &str,
_workspace_id: &str,
) -> FutureResult<Vec<Vec<u8>>, Error> {
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
FutureResult::new(async move { Ok(vec![]) })
}

View File

@ -98,9 +98,6 @@ pub enum ErrorCode {
#[error("user id is empty or whitespace")]
UserIdInvalid = 30,
#[error("User not exist")]
UserNotExist = 31,
#[error("Text is too long")]
TextTooLong = 32,

View File

@ -55,6 +55,10 @@ impl FlowyError {
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!(record_not_found, ErrorCode::RecordNotFound);
static_flowy_error!(workspace_name, ErrorCode::WorkspaceNameInvalid);
@ -87,7 +91,6 @@ impl FlowyError {
);
static_flowy_error!(name_empty, ErrorCode::UserNameIsEmpty);
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!(invalid_data, ErrorCode::InvalidParams);
static_flowy_error!(out_of_bounds, ErrorCode::OutOfBounds);

View File

@ -1,26 +1,25 @@
use client_api::error::AppError;
use client_api::error::{AppResponseError, ErrorCode as AppErrorCode};
use crate::{ErrorCode, FlowyError};
impl From<AppError> for FlowyError {
fn from(error: AppError) -> Self {
impl From<AppResponseError> for FlowyError {
fn from(error: AppResponseError) -> Self {
let code = match error.code {
client_api::error::ErrorCode::Ok => ErrorCode::Internal,
client_api::error::ErrorCode::Unhandled => ErrorCode::Internal,
client_api::error::ErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
client_api::error::ErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
client_api::error::ErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
client_api::error::ErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
client_api::error::ErrorCode::OAuthError => ErrorCode::UserUnauthorized,
client_api::error::ErrorCode::MissingPayload => ErrorCode::MissingPayload,
client_api::error::ErrorCode::OpenError => ErrorCode::Internal,
client_api::error::ErrorCode::InvalidUrl => ErrorCode::InvalidURL,
client_api::error::ErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
client_api::error::ErrorCode::UrlMissingParameter => ErrorCode::InvalidParams,
client_api::error::ErrorCode::InvalidOAuthProvider => ErrorCode::InvalidAuthConfig,
client_api::error::ErrorCode::NotLoggedIn => ErrorCode::UserUnauthorized,
client_api::error::ErrorCode::NotEnoughPermissions => ErrorCode::NotEnoughPermissions,
client_api::error::ErrorCode::UserNameIsEmpty => ErrorCode::UserNameIsEmpty,
AppErrorCode::Ok => ErrorCode::Internal,
AppErrorCode::Unhandled => ErrorCode::Internal,
AppErrorCode::RecordNotFound => ErrorCode::RecordNotFound,
AppErrorCode::RecordAlreadyExists => ErrorCode::RecordAlreadyExists,
AppErrorCode::InvalidEmail => ErrorCode::EmailFormatInvalid,
AppErrorCode::InvalidPassword => ErrorCode::PasswordFormatInvalid,
AppErrorCode::OAuthError => ErrorCode::UserUnauthorized,
AppErrorCode::MissingPayload => ErrorCode::MissingPayload,
AppErrorCode::OpenError => ErrorCode::Internal,
AppErrorCode::InvalidUrl => ErrorCode::InvalidURL,
AppErrorCode::InvalidRequestParams => ErrorCode::InvalidParams,
AppErrorCode::UrlMissingParameter => ErrorCode::InvalidParams,
AppErrorCode::InvalidOAuthProvider => ErrorCode::InvalidAuthConfig,
AppErrorCode::NotLoggedIn => ErrorCode::UserUnauthorized,
AppErrorCode::NotEnoughPermissions => ErrorCode::NotEnoughPermissions,
_ => ErrorCode::Internal,
};

View File

@ -6,8 +6,16 @@ use lib_infra::future::FutureResult;
/// [FolderCloudService] represents the cloud service for folder.
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 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(
&self,
workspace_id: &str,
@ -39,3 +47,10 @@ pub fn gen_workspace_id() -> Uuid {
pub fn gen_view_id() -> Uuid {
uuid::Uuid::new_v4()
}
#[derive(Debug)]
pub struct WorkspaceRecord {
pub id: String,
pub name: String,
pub created_at: i64,
}

View File

@ -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)]
pub(crate) async fn get_workspace_views_handler(
folder: AFPluginState<Weak<FolderManager>>,
@ -48,32 +56,12 @@ pub(crate) async fn get_workspace_views_handler(
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)]
pub(crate) async fn read_current_workspace_setting_handler(
folder: AFPluginState<Weak<FolderManager>>,
) -> DataResult<WorkspaceSettingPB, FlowyError> {
let folder = upgrade_folder(folder)?;
let setting = folder
.get_workspace_setting_pb()
.await
.ok_or(FlowyError::record_not_found())?;
let setting = folder.get_workspace_setting_pb().await?;
data_result_ok(setting)
}
@ -82,10 +70,7 @@ pub(crate) async fn read_current_workspace_handler(
folder: AFPluginState<Weak<FolderManager>>,
) -> DataResult<WorkspacePB, FlowyError> {
let folder = upgrade_folder(folder)?;
let workspace = folder
.get_workspace_pb()
.await
.ok_or(FlowyError::record_not_found())?;
let workspace = folder.get_workspace_pb().await?;
data_result_ok(workspace)
}

View File

@ -14,7 +14,6 @@ pub fn init(folder: Weak<FolderManager>) -> AFPlugin {
.event(FolderEvent::CreateWorkspace, create_workspace_handler)
.event(FolderEvent::GetCurrentWorkspaceSetting, read_current_workspace_setting_handler)
.event(FolderEvent::ReadCurrentWorkspace, read_current_workspace_handler)
.event(FolderEvent::OpenWorkspace, open_workspace_handler)
.event(FolderEvent::ReadWorkspaceViews, get_workspace_views_handler)
// View
.event(FolderEvent::CreateView, create_view_handler)
@ -59,10 +58,6 @@ pub enum FolderEvent {
#[event(input = "WorkspaceIdPB")]
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.
/// Only the first level of child views are included.
#[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")]

View File

@ -1,4 +1,5 @@
use std::collections::HashSet;
use std::fmt::{Display, Formatter};
use std::ops::Deref;
use std::sync::{Arc, Weak};
@ -124,17 +125,27 @@ impl FolderManager {
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)]
pub async fn initialize(
&self,
uid: i64,
workspace_id: &str,
initial_data: FolderInitializeDataSource,
initial_data: FolderInitDataSource,
) -> 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());
let workspace_id = workspace_id.to_string();
if let Ok(collab_db) = self.user.collab_db(uid) {
// Get the collab db for the user with given user id.
let 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 {
@ -143,7 +154,7 @@ impl FolderManager {
};
let folder = match initial_data {
FolderInitializeDataSource::LocalDisk {
FolderInitDataSource::LocalDisk {
create_if_not_exist,
} => {
let is_exist = is_exist_in_local_disk(&self.user, &workspace_id).unwrap_or(false);
@ -174,7 +185,7 @@ impl FolderManager {
));
}
},
FolderInitializeDataSource::Cloud(raw_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));
@ -184,7 +195,7 @@ impl FolderManager {
.await?;
Folder::open(UserId::from(uid), collab, Some(folder_notifier))?
},
FolderInitializeDataSource::FolderData(folder_data) => {
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![])
@ -198,21 +209,14 @@ impl FolderManager {
},
};
tracing::debug!("Current workspace_id: {}", workspace_id);
let folder_state_rx = folder.subscribe_sync_state();
*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_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(())
}
@ -239,7 +243,7 @@ impl FolderManager {
/// Initialize the folder with the given workspace id.
/// 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(
&self,
user_id: i64,
@ -250,7 +254,8 @@ impl FolderManager {
.get_folder_updates(workspace_id, user_id)
.await?;
info!(
event!(
Level::INFO,
"Get folder updates via {}, number of updates: {}",
self.cloud_service.service_name(),
folder_updates.len()
@ -260,7 +265,7 @@ impl FolderManager {
.initialize(
user_id,
workspace_id,
FolderInitializeDataSource::Cloud(folder_updates),
FolderInitDataSource::Cloud(folder_updates),
)
.await?;
Ok(())
@ -268,18 +273,13 @@ impl FolderManager {
/// Initialize the folder for the new user.
/// Using the [DefaultFolderBuilder] to create the default workspace for the new user.
#[instrument(
name = "folder_initialize_with_new_user",
level = "debug",
skip_all,
err
)]
#[instrument(level = "info", skip_all, err)]
pub async fn initialize_with_new_user(
&self,
user_id: i64,
_token: &str,
is_new: bool,
data_source: FolderInitializeDataSource,
data_source: FolderInitDataSource,
workspace_id: &str,
) -> FlowyResult<()> {
// Create the default workspace if the user is new
@ -306,7 +306,7 @@ impl FolderManager {
.initialize(
user_id,
workspace_id,
FolderInitializeDataSource::Cloud(folder_updates),
FolderInitDataSource::Cloud(folder_updates),
)
.await?;
},
@ -348,20 +348,24 @@ impl FolderManager {
self.with_folder(|| None, |folder| folder.get_current_workspace())
}
pub async fn get_workspace_setting_pb(&self) -> Option<WorkspaceSettingPB> {
let workspace_id = self.get_current_workspace_id().await.ok()?;
pub async fn get_workspace_setting_pb(&self) -> FlowyResult<WorkspaceSettingPB> {
let workspace_id = self.get_current_workspace_id().await?;
let latest_view = self.get_current_view().await;
Some(WorkspaceSettingPB {
Ok(WorkspaceSettingPB {
workspace_id,
latest_view,
})
}
pub async fn get_workspace_pb(&self) -> Option<WorkspacePB> {
pub async fn get_workspace_pb(&self) -> FlowyResult<WorkspacePB> {
let workspace_pb = {
let guard = self.mutex_folder.lock();
let folder = guard.as_ref()?;
let workspace = folder.get_current_workspace()?;
let folder = guard
.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
.views
@ -378,7 +382,7 @@ impl FolderManager {
}
};
Some(workspace_pb)
Ok(workspace_pb)
}
async fn get_current_workspace_id(&self) -> FlowyResult<String> {
@ -1274,7 +1278,7 @@ impl Deref for MutexFolder {
unsafe impl Sync 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
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
@ -1283,6 +1287,16 @@ pub enum FolderInitializeDataSource {
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> {
let uid = user.user_id()?;
if let Some(collab_db) = user.collab_db(uid)?.upgrade() {

View File

@ -20,7 +20,7 @@ where
&self,
document_id: &str,
workspace_id: &str,
) -> FutureResult<Vec<Vec<u8>>, Error> {
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
let workspace_id = workspace_id.to_string();
let try_get_client = self.0.try_get_client();
let document_id = document_id.to_string();

View File

@ -4,7 +4,9 @@ use collab::core::origin::CollabOrigin;
use collab_entity::CollabType;
use flowy_error::FlowyError;
use flowy_folder_deps::cloud::{Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace};
use flowy_folder_deps::cloud::{
Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace, WorkspaceRecord,
};
use lib_infra::future::FutureResult;
use crate::af_cloud::AFServer;
@ -19,6 +21,35 @@ where
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(
&self,
workspace_id: &str,

View File

@ -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();
FutureResult::new(async move {
let workspaces = try_get_client?.get_workspaces().await?;

View File

@ -4,12 +4,12 @@ use std::sync::Arc;
use anyhow::Error;
use client_api::notify::{TokenState, TokenStateReceiver};
use client_api::ws::{
BusinessID, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
BusinessID, ConnectState, WSClient, WSClientConfig, WSConnectStateReceiver, WebSocketChannel,
};
use client_api::Client;
use tokio::sync::watch;
use tokio_stream::wrappers::WatchStream;
use tracing::{error, info};
use tracing::{error, event, info};
use flowy_database_deps::cloud::DatabaseCloudService;
use flowy_document_deps::cloud::DocumentCloudService;
@ -145,7 +145,8 @@ impl AppFlowyServer for AFCloudServer {
fn collab_ws_channel(
&self,
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) {
let object_id = object_id.to_string();
let weak_ws_client = Arc::downgrade(&self.ws_client);
@ -155,7 +156,7 @@ impl AppFlowyServer for AFCloudServer {
Some(ws_client) => {
let channel = ws_client.subscribe(BusinessID::CollabId, object_id).ok();
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,25 +191,34 @@ fn spawn_ws_conn(
if let Some(ws_client) = weak_ws_client.upgrade() {
let mut state_recv = ws_client.subscribe_connect_state();
while let Ok(state) = state_recv.recv().await {
if !state.is_timeout() {
continue;
}
info!("[websocket] state: {:?}", state);
match state {
ConnectState::PingTimeout => {
// Try to reconnect if the connection is timed out.
if let (Some(api_client), Some(device_id)) =
(weak_api_client.upgrade(), weak_device_id.upgrade())
{
if enable_sync.load(Ordering::SeqCst) {
info!("🟢websocket state: {:?}, reconnecting", state);
let device_id = device_id.read().clone();
match api_client.ws_url(&device_id) {
Ok(ws_addr) => {
event!(tracing::Level::INFO, "🟢reconnecting websocket");
let _ = ws_client.connect(ws_addr).await;
},
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;
}
}
},
_ => {},
}
}
}
});

View File

@ -1,6 +1,7 @@
use anyhow::Error;
use flowy_document_deps::cloud::*;
use flowy_error::FlowyError;
use lib_infra::future::FutureResult;
pub(crate) struct LocalServerDocumentCloudServiceImpl();
@ -10,7 +11,7 @@ impl DocumentCloudService for LocalServerDocumentCloudServiceImpl {
&self,
_document_id: &str,
_workspace_id: &str,
) -> FutureResult<Vec<Vec<u8>>, Error> {
) -> FutureResult<Vec<Vec<u8>>, FlowyError> {
FutureResult::new(async move { Ok(vec![]) })
}

View File

@ -3,7 +3,7 @@ use std::sync::Arc;
use anyhow::Error;
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::util::timestamp;
@ -11,6 +11,7 @@ use lib_infra::util::timestamp;
use crate::local_server::LocalServerDB;
pub(crate) struct LocalServerFolderCloudServiceImpl {
#[allow(dead_code)]
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(
&self,
_workspace_id: &str,
@ -43,18 +52,12 @@ impl FolderCloudService for LocalServerFolderCloudServiceImpl {
FutureResult::new(async move { Ok(vec![]) })
}
fn get_folder_updates(&self, workspace_id: &str, uid: i64) -> FutureResult<Vec<Vec<u8>>, Error> {
let weak_db = Arc::downgrade(&self.db);
let workspace_id = workspace_id.to_string();
FutureResult::new(async move {
match weak_db.upgrade() {
None => Ok(vec![]),
Some(db) => {
let updates = db.get_collab_updates(uid, &workspace_id)?;
Ok(updates)
},
}
})
fn get_folder_updates(
&self,
_workspace_id: &str,
_uid: i64,
) -> FutureResult<Vec<Vec<u8>>, Error> {
FutureResult::new(async move { Ok(vec![]) })
}
fn service_name(&self) -> String {

View File

@ -116,7 +116,13 @@ impl UserCloudService for LocalServerUserAuthServiceImpl {
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![]) })
}

View File

@ -23,7 +23,6 @@ use crate::AppFlowyServer;
pub trait LocalServerDB: Send + Sync + 'static {
fn get_user_profile(&self, uid: i64) -> Result<UserProfile, 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 {

View File

@ -104,7 +104,8 @@ pub trait AppFlowyServer: Send + Sync + 'static {
fn collab_ws_channel(
&self,
_object_id: &str,
) -> FutureResult<Option<(Arc<WebSocketChannel>, WSConnectStateReceiver)>, anyhow::Error> {
) -> FutureResult<Option<(Arc<WebSocketChannel>, WSConnectStateReceiver, bool)>, anyhow::Error>
{
FutureResult::new(async { Ok(None) })
}

View File

@ -32,7 +32,7 @@ where
&self,
document_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 document_id = document_id.to_string();
let (tx, rx) = channel();
@ -43,7 +43,7 @@ where
let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest);
let updates = action.run_with_fix_interval(5, 10).await?;
if updates.is_empty() {
return Err(FlowyError::collab_not_sync().into());
return Err(FlowyError::collab_not_sync());
}
Ok(updates)
}

View File

@ -9,6 +9,7 @@ use tokio::sync::oneshot::channel;
use flowy_folder_deps::cloud::{
gen_workspace_id, Folder, FolderCloudService, FolderData, FolderSnapshot, Workspace,
WorkspaceRecord,
};
use lib_dispatch::prelude::af_spawn;
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(
&self,
workspace_id: &str,

View File

@ -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();
FutureResult::new(async move {
let postgrest = try_get_postgrest?;

View File

@ -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::sync::Arc;
use std::time::Duration;
use anyhow::Error;
use tokio::sync::{watch, RwLock};
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 {
queue: TaskQueue,
store: TaskStore,
@ -122,6 +122,9 @@ impl TaskDispatcher {
}
}
pub fn clear_task(&mut self) {
self.store.clear();
}
pub fn next_task_id(&self) -> TaskId {
self.store.next_task_id()
}

View File

@ -93,8 +93,10 @@ pub trait UserCloudService: Send + Sync + 'static {
/// return None if the user is not found
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
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(
&self,

View File

@ -138,7 +138,7 @@ pub struct UserWorkspace {
pub id: String,
pub name: String,
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")]
pub database_views_aggregate_id: String,
}

View File

@ -1,9 +1,12 @@
use std::convert::TryInto;
use validator::Validate;
use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
use flowy_user_deps::entities::*;
use crate::entities::parser::{UserEmail, UserIcon, UserName, UserOpenaiKey, UserPassword};
use crate::entities::required_not_empty_str;
use crate::entities::AuthTypePB;
use crate::errors::ErrorCode;
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 {
#[pb(index = 1)]
pub id: String,
#[validate(custom = "required_not_empty_str")]
pub workspace_id: String,
#[pb(index = 2)]
pub name: String,
@ -229,7 +233,7 @@ pub struct UserWorkspacePB {
impl From<UserWorkspace> for UserWorkspacePB {
fn from(value: UserWorkspace) -> Self {
Self {
id: value.id,
workspace_id: value.id,
name: value.name,
}
}

View File

@ -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,
}

View File

@ -2,7 +2,6 @@ use std::sync::Weak;
use std::{convert::TryInto, sync::Arc};
use serde_json::Value;
use tracing::event;
use flowy_error::{ErrorCode, FlowyError, FlowyResult};
use flowy_sqlite::kv::StorePreferences;
@ -105,17 +104,11 @@ pub async fn get_user_profile_handler(
user_profile.email = "".to_string();
}
event!(
tracing::Level::DEBUG,
"Get user profile: {:?}",
user_profile
);
data_result_ok(user_profile.into())
}
#[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)?;
manager.sign_out().await?;
Ok(())
@ -425,7 +418,7 @@ pub async fn get_cloud_config_handler(
}
#[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>>,
) -> DataResult<RepeatedUserWorkspacePB, FlowyError> {
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)]
pub async fn open_workspace_handler(
data: AFPluginData<UserWorkspacePB>,
data: AFPluginData<UserWorkspaceIdPB>,
manager: AFPluginState<Weak<UserManager>>,
) -> Result<(), FlowyError> {
let manager = upgrade_manager(manager)?;
let params = data.into_inner();
manager.open_workspace(&params.id).await?;
let params = data.validate()?.into_inner();
manager.open_workspace(&params.workspace_id).await?;
Ok(())
}

View File

@ -29,7 +29,7 @@ pub fn init(user_session: Weak<UserManager>) -> AFPlugin {
.event(UserEvent::SignUp, sign_up)
.event(UserEvent::InitUser, init_user_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::SetAppearanceSetting, set_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::GetSignInURL, get_sign_in_url_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::UpdateNetworkState, update_network_state_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::RemoveWorkspaceMember, delete_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)]
@ -129,10 +129,10 @@ pub enum UserEvent {
CheckEncryptionSign = 16,
/// Return the all the workspaces of the user
#[event()]
GetAllUserWorkspaces = 20,
#[event(output = "RepeatedUserWorkspacePB")]
GetAllWorkspace = 17,
#[event(input = "UserWorkspacePB")]
#[event(input = "UserWorkspaceIdPB")]
OpenWorkspace = 21,
#[event(input = "NetworkStatePB")]

View File

@ -1,4 +1,5 @@
use std::string::ToString;
use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::{Arc, Weak};
use collab_user::core::MutexUserAwareness;
@ -55,7 +56,7 @@ impl UserSessionConfig {
}
pub struct UserManager {
database: UserDB,
database: Arc<UserDB>,
session_config: UserSessionConfig,
pub(crate) cloud_services: Arc<dyn UserCloudServiceProvider>,
pub(crate) store_preferences: Arc<StorePreferences>,
@ -64,7 +65,8 @@ pub struct UserManager {
pub(crate) collab_builder: Weak<AppFlowyCollabBuilder>,
pub(crate) collab_interact: RwLock<Arc<dyn CollabInteract>>,
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 {
@ -74,10 +76,11 @@ impl UserManager {
store_preferences: Arc<StorePreferences>,
collab_builder: Weak<AppFlowyCollabBuilder>,
) -> 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>> =
RwLock::new(Arc::new(DefaultUserStatusCallback));
let refresh_user_profile_since = AtomicI64::new(0);
let user_manager = Arc::new(Self {
database,
session_config,
@ -89,6 +92,7 @@ impl UserManager {
collab_interact: RwLock::new(Arc::new(DefaultCollabInteract)),
resumable_sign_up: Default::default(),
current_session: Default::default(),
refresh_user_profile_since,
});
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,
/// 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.
#[instrument(level = "debug", skip_all, err)]
pub async fn init<C: UserStatusCallback + 'static, I: CollabInteract>(
&self,
user_status_callback: C,
collab_interact: I,
) -> 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() {
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) {
error!("Set token failed: {}", err);
}
@ -134,10 +153,13 @@ impl UserManager {
// Subscribe the token state
let weak_pool = Arc::downgrade(&self.db_pool(user.uid)?);
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 {
while let Some(token_state) = token_state_rx.next().await {
debug!("Token state changed: {:?}", token_state);
match token_state {
UserTokenState::Refresh { token } => {
// Only save the token if the token is different from the current token
if token != user.token {
if let Some(pool) = weak_pool.upgrade() {
// Save the new token
@ -147,19 +169,14 @@ impl UserManager {
}
}
},
UserTokenState::Invalid => {
send_auth_state_notification(AuthStateChangedPB {
state: AuthStatePB::InvalidAuth,
message: "Token is invalid".to_string(),
})
.send();
},
UserTokenState::Invalid => {},
}
}
});
}
// Do the user data migration if needed
event!(tracing::Level::INFO, "Prepare user data migration");
match (
self.database.get_collab_db(session.user_id),
self.database.get_pool(session.user_id),
@ -202,8 +219,6 @@ impl UserManager {
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(())
}
@ -380,6 +395,7 @@ impl UserManager {
self
.save_auth_data(&response, auth_type, &new_session)
.await?;
self
.user_status_callback
.read()
@ -445,14 +461,28 @@ impl UserManager {
pub async fn get_user_profile(&self, uid: i64) -> Result<UserProfile, FlowyError> {
let user: UserProfile = user_table::dsl::user_table
.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();
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<()> {
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 result: Result<UserProfile, FlowyError> = self
.cloud_services
@ -494,12 +524,12 @@ impl UserManager {
},
Err(err) => {
// If the user is not found, notify the frontend to logout
if err.is_record_not_found() {
if err.is_unauthorized() {
event!(
tracing::Level::INFO,
"User is not found on the server when refreshing profile"
tracing::Level::ERROR,
"User is unauthorized, sign out the user"
);
self.sign_out().await?;
send_auth_state_notification(AuthStateChangedPB {
state: AuthStatePB::InvalidAuth,
message: "User is not found on the server".to_string(),
@ -641,6 +671,7 @@ impl UserManager {
Ok(url)
}
#[instrument(level = "info", skip_all, err)]
async fn save_auth_data(
&self,
response: &impl UserAuthResponse,

View File

@ -5,11 +5,13 @@ use collab::core::origin::{CollabClient, CollabOrigin};
use collab_document::document::Document;
use collab_document::document_data::default_document_data;
use collab_folder::Folder;
use tracing::{event, instrument};
use collab_integrate::{RocksCollabDB, YrsDocAction};
use flowy_error::{internal_error, FlowyResult};
use crate::migrations::migration::UserDataMigration;
use crate::migrations::util::load_collab;
use crate::services::entities::Session;
/// Migrate the first level documents of the workspace by inserting documents
@ -20,39 +22,42 @@ impl UserDataMigration for HistoricalEmptyDocumentMigration {
"historical_empty_document"
}
#[instrument(name = "HistoricalEmptyDocumentMigration", skip_all, err)]
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> {
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"));
// Deserialize the folder from the raw data
let folder = Folder::from_collab_raw_data(
session.user_id,
origin.clone(),
updates,
&session.user_workspace.id,
vec![],
)?;
if let Ok(folder_collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id)
{
let folder = Folder::open(session.user_id, folder_collab, None)?;
// 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);
for view in migration_views {
// Read all updates of the view
if let Ok(view_updates) = write_txn.get_all_updates(session.user_id, &view.id) {
if Document::from_updates(origin.clone(), view_updates, &view.id, vec![]).is_err() {
if load_collab(session.user_id, &write_txn, &view.id).is_err() {
// Create a document with default data
let document_data = default_document_data();
let collab = Arc::new(MutexCollab::new(origin.clone(), &view.id, vec![]));
if let Ok(document) = Document::create_with_data(collab.clone(), document_data) {
// Remove all old updates and then insert the new update
let (doc_state, sv) = document.get_collab().encode_as_update_v1();
write_txn
.flush_doc_with(session.user_id, &view.id, &doc_state, &sv)
.map_err(internal_error)?;
if let Err(err) = write_txn.flush_doc_with(session.user_id, &view.id, &doc_state, &sv) {
event!(
tracing::Level::ERROR,
"Failed to migrate document {}, 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)?;
Ok(())
}

View File

@ -2,7 +2,6 @@ use std::sync::Arc;
use chrono::NaiveDateTime;
use diesel::{RunQueryDsl, SqliteConnection};
use tracing::event;
use collab_integrate::RocksCollabDB;
use flowy_error::FlowyResult;
@ -55,12 +54,6 @@ impl UserLocalDataMigration {
{
let migration_name = migration.name().to_string();
if !duplicated_names.contains(&migration_name) {
event!(
tracing::Level::INFO,
"Running migration {}",
migration.name()
);
migration.run(&self.session, &self.collab_db)?;
applied_migrations.push(migration.name().to_string());
save_record(&conn, &migration_name);

View File

@ -3,4 +3,5 @@ pub use define::*;
mod define;
pub mod document_empty_content;
pub mod migration;
mod util;
pub mod workspace_and_favorite_v1;

View 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)))
}

View File

@ -1,12 +1,13 @@
use std::sync::Arc;
use collab::core::origin::{CollabClient, CollabOrigin};
use collab_folder::Folder;
use tracing::instrument;
use collab_integrate::{RocksCollabDB, YrsDocAction};
use flowy_error::{internal_error, FlowyResult};
use crate::migrations::migration::UserDataMigration;
use crate::migrations::util::load_collab;
use crate::services::entities::Session;
/// 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"
}
#[instrument(name = "FavoriteV1AndWorkspaceArrayMigration", skip_all, err)]
fn run(&self, session: &Session, collab_db: &Arc<RocksCollabDB>) -> FlowyResult<()> {
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"));
// Deserialize the folder from the raw data
let folder = Folder::from_collab_raw_data(
session.user_id,
origin,
updates,
&session.user_workspace.id,
vec![],
)?;
if let Ok(collab) = load_collab(session.user_id, &write_txn, &session.user_workspace.id) {
let folder = Folder::open(session.user_id, collab, None)?;
folder.migrate_workspace_to_view();
let favorite_view_ids = folder
@ -48,8 +41,9 @@ impl UserDataMigration for FavoriteV1AndWorkspaceArrayMigration {
write_txn
.flush_doc_with(session.user_id, &session.user_workspace.id, &doc_state, &sv)
.map_err(internal_error)?;
write_txn.commit_transaction().map_err(internal_error)?;
}
write_txn.commit_transaction().map_err(internal_error)?;
Ok(())
}
}

View File

@ -2,6 +2,7 @@ use std::convert::TryFrom;
use std::sync::Arc;
use collab_entity::{CollabObject, CollabType};
use tracing::{error, instrument};
use flowy_error::{FlowyError, FlowyResult};
use flowy_sqlite::schema::user_workspace_table;
@ -15,8 +16,14 @@ use crate::notification::{send_notification, UserNotification};
use crate::services::user_workspace_sql::UserWorkspaceTable;
impl UserManager {
#[instrument(skip(self), err)]
pub async fn open_workspace(&self, workspace_id: &str) -> FlowyResult<()> {
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 Err(err) = self
.user_status_callback
@ -25,7 +32,7 @@ impl UserManager {
.open_workspace(uid, &user_workspace)
.await
{
tracing::error!("Open workspace failed: {:?}", err);
error!("Open workspace failed: {:?}", err);
}
}
Ok(())
@ -101,7 +108,7 @@ impl UserManager {
if let Ok(service) = self.cloud_services.get_user_service() {
if let Ok(pool) = self.db_pool(uid) {
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 repeated_workspace_pbs = RepeatedUserWorkspacePB::from(new_user_workspaces);
send_notification(&uid.to_string(), UserNotification::DidUpdateUserWorkspaces)

View File

@ -7,7 +7,6 @@ edition = "2018"
[dependencies]
tracing-log = { version = "0.2"}
tracing-subscriber = { version = "0.3.17", features = ["registry", "env-filter", "ansi", "json"] }
tracing-bunyan-formatter = "0.3.9"
tracing-appender = "0.2.2"

View File

@ -1,9 +1,11 @@
use std::sync::RwLock;
use chrono::Local;
use lazy_static::lazy_static;
use tracing::subscriber::set_global_default;
use tracing_appender::{non_blocking::WorkerGuard, rolling::RollingFileAppender};
use tracing_bunyan_formatter::JsonStorageLayer;
use tracing_subscriber::fmt::format::Writer;
use tracing_subscriber::{layer::SubscriberExt, EnvFilter};
use crate::layer::FlowyFormattingLayer;
@ -43,12 +45,12 @@ impl Builder {
let (non_blocking, guard) = tracing_appender::non_blocking(self.file_appender);
let subscriber = tracing_subscriber::fmt()
.with_timer(CustomTime)
.with_ansi(true)
.with_target(true)
.with_target(false)
.with_max_level(tracing::Level::TRACE)
.with_thread_ids(false)
.with_file(false)
.with_writer(std::io::stderr)
.with_writer(std::io::stdout)
.pretty()
.with_env_filter(env_filter)
.finish()
@ -56,7 +58,15 @@ impl Builder {
.with(FlowyFormattingLayer::new(non_blocking));
set_global_default(subscriber).map_err(|e| format!("{:?}", e))?;
*LOG_GUARD.write().unwrap() = Some(guard);
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"))
}
}