mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
feat: show loading indicator when loading data from remote (#3120)
* chore: show circle indicator if fetch the data from remote * chore: fix the lb warnings * chore: create sdk-build for macOS
This commit is contained in:
parent
ab7acbd5de
commit
9a72f31d60
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@ -20,14 +20,6 @@ jobs:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# - name: Create .env file
|
||||
# working-directory: frontend/appflowy_flutter
|
||||
# run: |
|
||||
# touch .env
|
||||
# echo SUPABASE_URL=${{ secrets.HOST_URL }} >> .env
|
||||
# echo SUPABASE_ANON_KEY=${{ secrets.HOST_ANON_KEY }} >> .env
|
||||
# echo SUPABASE_JWT_SECRET=${{ secrets.HOST_JWT_SECRET }} >> .env
|
||||
|
||||
- name: Build release notes
|
||||
run: |
|
||||
touch ${{ env.RELEASE_NOTES_PATH }}
|
||||
|
@ -13,6 +13,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'database_view_service.dart';
|
||||
import 'defines.dart';
|
||||
import 'layout/layout_service.dart';
|
||||
@ -92,6 +93,8 @@ class DatabaseController {
|
||||
final DatabaseGroupListener _groupListener;
|
||||
final DatabaseLayoutSettingListener _layoutListener;
|
||||
|
||||
final ValueNotifier<bool> _isLoading = ValueNotifier(true);
|
||||
|
||||
DatabaseController({required ViewPB view})
|
||||
: viewId = view.id,
|
||||
_databaseViewBackendSvc = DatabaseViewBackendService(viewId: view.id),
|
||||
@ -109,6 +112,12 @@ class DatabaseController {
|
||||
_listenOnLayoutChanged();
|
||||
}
|
||||
|
||||
void setIsLoading(bool isLoading) {
|
||||
_isLoading.value = isLoading;
|
||||
}
|
||||
|
||||
ValueNotifier<bool> get isLoading => _isLoading;
|
||||
|
||||
void addListener({
|
||||
DatabaseCallbacks? onDatabaseChanged,
|
||||
DatabaseLayoutSettingCallbacks? onLayoutChanged,
|
||||
|
@ -3,12 +3,16 @@ import 'dart:collection';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/database_entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
import '../grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'field/field_controller.dart';
|
||||
import 'row/row_cache.dart';
|
||||
import 'row/row_service.dart';
|
||||
|
||||
part 'defines.freezed.dart';
|
||||
|
||||
typedef OnFieldsChanged = void Function(UnmodifiableListView<FieldInfo>);
|
||||
typedef OnFiltersChanged = void Function(List<FilterInfo>);
|
||||
typedef OnSortsChanged = void Function(List<SortInfo>);
|
||||
@ -27,3 +31,11 @@ typedef OnNumOfRowsChanged = void Function(
|
||||
);
|
||||
|
||||
typedef OnError = void Function(FlowyError);
|
||||
|
||||
@freezed
|
||||
class LoadingState with _$LoadingState {
|
||||
const factory LoadingState.loading() = _Loading;
|
||||
const factory LoadingState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:appflowy/plugins/database_view/application/defines.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy_board/appflowy_board.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
@ -254,11 +255,14 @@ class BoardBloc extends Bloc<BoardEvent, BoardState> {
|
||||
Future<void> _openGrid(Emitter<BoardState> emit) async {
|
||||
final result = await databaseController.open();
|
||||
result.fold(
|
||||
(grid) => emit(
|
||||
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
|
||||
),
|
||||
(grid) {
|
||||
databaseController.setIsLoading(false);
|
||||
emit(
|
||||
state.copyWith(loadingState: LoadingState.finish(left(unit))),
|
||||
);
|
||||
},
|
||||
(err) => emit(
|
||||
state.copyWith(loadingState: GridLoadingState.finish(right(err))),
|
||||
state.copyWith(loadingState: LoadingState.finish(right(err))),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -323,7 +327,7 @@ class BoardState with _$BoardState {
|
||||
required Option<DatabasePB> grid,
|
||||
required List<String> groupIds,
|
||||
required Option<BoardEditingRow> editingRow,
|
||||
required GridLoadingState loadingState,
|
||||
required LoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
}) = _BoardState;
|
||||
|
||||
@ -333,18 +337,10 @@ class BoardState with _$BoardState {
|
||||
groupIds: [],
|
||||
editingRow: none(),
|
||||
noneOrError: none(),
|
||||
loadingState: const _Loading(),
|
||||
loadingState: const LoadingState.loading(),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridLoadingState with _$GridLoadingState {
|
||||
const factory GridLoadingState.loading() = _Loading;
|
||||
const factory GridLoadingState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
}
|
||||
|
||||
class GridFieldEquatable extends Equatable {
|
||||
final UnmodifiableListView<FieldPB> _fields;
|
||||
const GridFieldEquatable(
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/plugins/database_view/application/cell/cell_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/defines.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/field/field_controller.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy_backend/dispatch/dispatch.dart';
|
||||
@ -133,11 +134,14 @@ class CalendarBloc extends Bloc<CalendarEvent, CalendarState> {
|
||||
Future<void> _openDatabase(Emitter<CalendarState> emit) async {
|
||||
final result = await databaseController.open();
|
||||
result.fold(
|
||||
(database) => emit(
|
||||
state.copyWith(loadingState: DatabaseLoadingState.finish(left(unit))),
|
||||
),
|
||||
(database) {
|
||||
databaseController.setIsLoading(false);
|
||||
emit(
|
||||
state.copyWith(loadingState: LoadingState.finish(left(unit))),
|
||||
);
|
||||
},
|
||||
(err) => emit(
|
||||
state.copyWith(loadingState: DatabaseLoadingState.finish(right(err))),
|
||||
state.copyWith(loadingState: LoadingState.finish(right(err))),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -425,7 +429,7 @@ class CalendarState with _$CalendarState {
|
||||
CalendarEventData<CalendarDayEvent>? updateEvent,
|
||||
required List<String> deleteEventIds,
|
||||
required Option<CalendarLayoutSettingPB> settings,
|
||||
required DatabaseLoadingState loadingState,
|
||||
required LoadingState loadingState,
|
||||
required Option<FlowyError> noneOrError,
|
||||
}) = _CalendarState;
|
||||
|
||||
@ -436,18 +440,10 @@ class CalendarState with _$CalendarState {
|
||||
deleteEventIds: [],
|
||||
settings: none(),
|
||||
noneOrError: none(),
|
||||
loadingState: const _Loading(),
|
||||
loadingState: const LoadingState.loading(),
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DatabaseLoadingState with _$DatabaseLoadingState {
|
||||
const factory DatabaseLoadingState.loading() = _Loading;
|
||||
const factory DatabaseLoadingState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
}
|
||||
|
||||
class CalendarEditingRow {
|
||||
RowPB row;
|
||||
int? index;
|
||||
|
@ -159,10 +159,22 @@ class _CalendarPageState extends State<CalendarPage> {
|
||||
],
|
||||
child: BlocBuilder<CalendarBloc, CalendarState>(
|
||||
builder: (context, state) {
|
||||
return _buildCalendar(
|
||||
context,
|
||||
_eventController,
|
||||
state.settings.foldLeft(0, (previous, a) => a.firstDayOfWeek),
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: widget.databaseController.isLoading,
|
||||
builder: (_, value, ___) {
|
||||
if (value) {
|
||||
return const Center(
|
||||
child: CircularProgressIndicator.adaptive(),
|
||||
);
|
||||
} else {
|
||||
return _buildCalendar(
|
||||
context,
|
||||
_eventController,
|
||||
state.settings
|
||||
.foldLeft(0, (previous, a) => a.firstDayOfWeek),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
|
@ -1,11 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'package:appflowy/plugins/database_view/application/defines.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_cache.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/row/row_service.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/filter/filter_info.dart';
|
||||
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/sort/sort_info.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
@ -134,12 +134,13 @@ class GridBloc extends Bloc<GridEvent, GridState> {
|
||||
final result = await databaseController.open();
|
||||
result.fold(
|
||||
(grid) {
|
||||
databaseController.setIsLoading(false);
|
||||
emit(
|
||||
state.copyWith(loadingState: GridLoadingState.finish(left(unit))),
|
||||
state.copyWith(loadingState: LoadingState.finish(left(unit))),
|
||||
);
|
||||
},
|
||||
(err) => emit(
|
||||
state.copyWith(loadingState: GridLoadingState.finish(right(err))),
|
||||
state.copyWith(loadingState: LoadingState.finish(right(err))),
|
||||
),
|
||||
);
|
||||
}
|
||||
@ -177,7 +178,7 @@ class GridState with _$GridState {
|
||||
required GridFieldEquatable fields,
|
||||
required List<RowInfo> rowInfos,
|
||||
required int rowCount,
|
||||
required GridLoadingState loadingState,
|
||||
required LoadingState loadingState,
|
||||
required bool reorderable,
|
||||
required ChangedReason reason,
|
||||
required List<SortInfo> sorts,
|
||||
@ -191,21 +192,13 @@ class GridState with _$GridState {
|
||||
grid: none(),
|
||||
viewId: viewId,
|
||||
reorderable: true,
|
||||
loadingState: const _Loading(),
|
||||
loadingState: const LoadingState.loading(),
|
||||
reason: const InitialListState(),
|
||||
filters: [],
|
||||
sorts: [],
|
||||
);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class GridLoadingState with _$GridLoadingState {
|
||||
const factory GridLoadingState.loading() = _Loading;
|
||||
const factory GridLoadingState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
}
|
||||
|
||||
class GridFieldEquatable extends Equatable {
|
||||
final List<FieldInfo> _fields;
|
||||
const GridFieldEquatable(
|
||||
|
@ -47,20 +47,29 @@ class GridSettingBar extends StatelessWidget {
|
||||
listener: (context, state) => toggleExtension.toggle(),
|
||||
),
|
||||
],
|
||||
child: SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
const Spacer(),
|
||||
const FilterButton(),
|
||||
const SortButton(),
|
||||
SettingButton(
|
||||
databaseController: controller,
|
||||
),
|
||||
],
|
||||
),
|
||||
child: ValueListenableBuilder<bool>(
|
||||
valueListenable: controller.isLoading,
|
||||
builder: (context, value, child) {
|
||||
if (value) {
|
||||
return const SizedBox.shrink();
|
||||
} else {
|
||||
return SizedBox(
|
||||
height: 40,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
SizedBox(width: GridSize.leadingHeaderPadding),
|
||||
const Spacer(),
|
||||
const FilterButton(),
|
||||
const SortButton(),
|
||||
SettingButton(
|
||||
databaseController: controller,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -93,30 +93,46 @@ class _DatabaseTabBarViewState extends State<DatabaseTabBarView> {
|
||||
],
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
BlocBuilder<GridTabBarBloc, GridTabBarState>(
|
||||
builder: (context, state) {
|
||||
return const Flexible(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 50),
|
||||
child: DatabaseTabBar(),
|
||||
),
|
||||
);
|
||||
BlocBuilder<GridTabBarBloc, GridTabBarState>(
|
||||
builder: (context, state) {
|
||||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: state
|
||||
.tabBarControllerByViewId[state.parentView.id]!
|
||||
.controller
|
||||
.isLoading,
|
||||
builder: (_, value, ___) {
|
||||
if (value) {
|
||||
return const SizedBox.shrink();
|
||||
} else {
|
||||
return Row(
|
||||
children: [
|
||||
BlocBuilder<GridTabBarBloc, GridTabBarState>(
|
||||
builder: (context, state) {
|
||||
return const Flexible(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(left: 50),
|
||||
child: DatabaseTabBar(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocBuilder<GridTabBarBloc, GridTabBarState>(
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
width: 300,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 50),
|
||||
child: pageSettingBarFromState(state),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
BlocBuilder<GridTabBarBloc, GridTabBarState>(
|
||||
builder: (context, state) {
|
||||
return SizedBox(
|
||||
width: 300,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(right: 50),
|
||||
child: pageSettingBarFromState(state),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
BlocBuilder<GridTabBarBloc, GridTabBarState>(
|
||||
builder: (context, state) {
|
||||
|
@ -65,7 +65,8 @@ class _DocumentPageState extends State<DocumentPage> {
|
||||
child: BlocBuilder<DocumentBloc, DocumentState>(
|
||||
builder: (context, state) {
|
||||
return state.loadingState.when(
|
||||
loading: () => const SizedBox.shrink(),
|
||||
loading: () =>
|
||||
const Center(child: CircularProgressIndicator.adaptive()),
|
||||
finish: (result) => result.fold(
|
||||
(error) {
|
||||
Log.error(error);
|
||||
|
@ -18,7 +18,7 @@ class UserBackendService {
|
||||
static Future<Either<FlowyError, UserProfilePB>>
|
||||
getCurrentUserProfile() async {
|
||||
final result = await UserEventGetUserProfile().send().then((value) {
|
||||
value.fold((l) => null, (r) => Log.error(r));
|
||||
value.fold((l) => null, (r) => Log.info(r));
|
||||
return value;
|
||||
});
|
||||
return result.swap();
|
||||
|
@ -10,10 +10,10 @@ use collab_document::document_data::default_document_data;
|
||||
use collab_document::YrsDocAction;
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::document::MutexDocument;
|
||||
use flowy_document_deps::cloud::DocumentCloudService;
|
||||
use flowy_error::{internal_error, FlowyError, FlowyResult};
|
||||
|
||||
use crate::document::MutexDocument;
|
||||
use crate::entities::DocumentSnapshotPB;
|
||||
|
||||
pub trait DocumentUser: Send + Sync {
|
||||
@ -77,13 +77,13 @@ impl DocumentManager {
|
||||
let mut updates = vec![];
|
||||
if !self.is_doc_exist(doc_id)? {
|
||||
// Try to get the document from the cloud service
|
||||
if let Ok(document_updates) = self.cloud_service.get_document_updates(doc_id).await {
|
||||
updates = document_updates;
|
||||
} else {
|
||||
return Err(
|
||||
FlowyError::record_not_found().context(format!("document: {} is not exist", doc_id)),
|
||||
);
|
||||
};
|
||||
match self.cloud_service.get_document_updates(doc_id).await {
|
||||
Ok(document_updates) => updates = document_updates,
|
||||
Err(e) => {
|
||||
tracing::error!("Get document data failed: {:?}", e);
|
||||
return Err(FlowyError::internal().context("Can't not read the document data"));
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
tracing::debug!("open_document: {:?}", doc_id);
|
||||
|
@ -1,108 +0,0 @@
|
||||
# AppFlowy Cloud Architecture
|
||||
|
||||
AppFlowy supports multiple cloud solutions. Users can choose their preferred cloud provider, such as Supabase, Firebase,
|
||||
AWS, or our own AppFlowyCloud (Self-hosted server).
|
||||
|
||||

|
||||
|
||||
## Design
|
||||
|
||||
AppFlowy use the traits [AppFlowyServer] to abstract the cloud provider. Each cloud provider implements the [AppFlowyServer]
|
||||
trait. As the image below shows. Users can choose their preferred cloud provider or simply use the default option, which is the LocalServer. When using the
|
||||
LocalServer, data is stored on the local file system. Users can migrate to a cloud provider if needed. For instance, one
|
||||
could migrate from LocalServer to AppFlowyCloud. This migration would create a new user in the cloud and transfer all the
|
||||
data from the local database to the cloud.
|
||||
|
||||

|
||||
|
||||
## AppFlowy Cloud Implementation (WIP)
|
||||
|
||||
### Restful API
|
||||
|
||||
### Table schema
|
||||
|
||||
## Supabase Implementation
|
||||
|
||||
### Table schema
|
||||

|
||||
|
||||
1. `af_roles` table: This table contains a list of roles that are used in your application, such as 'Owner', 'Member', and 'Guest'.
|
||||
|
||||
2. `af_permissions` table: This table stores permissions that are used in your application. Each permission has a name, a description, and an access level.
|
||||
|
||||
3. `af_role_permissions` table: This is a many-to-many relation table between roles and permissions. It represents which permissions a role has.
|
||||
|
||||
4. `af_user` table: This stores the details of users like uuid, email, uid, name, created_at. Here, uid is an auto-incrementing integer that uniquely identifies a user.
|
||||
|
||||
5. `af_workspace` table: This table contains all the workspaces. Each workspace has an owner which is associated with the uid of a user in the `af_user` table.
|
||||
|
||||
6. `af_workspace_member` table: This table maintains a list of all the members associated with a workspace and their roles.
|
||||
|
||||
7. `af_collab` and `af_collab_member` tables: These tables store the collaborations and their members respectively. Each collaboration has an owner and a workspace associated with it.
|
||||
|
||||
8. `af_collab_update`, `af_collab_update_document`, `af_collab_update_database`, `af_collab_update_w_database`, `af_collab_update_folder`, `af_database_row_update` tables: These tables are used for handling updates to collaborations.
|
||||
|
||||
9. `af_collab_statistics`, `af_collab_snapshot`, `af_collab_state`: These tables and view are used for maintaining statistics and snapshots of collaborations.
|
||||
|
||||
10. `af_user_profile_view` view: This view is used to get the latest workspace_id for each user.
|
||||
|
||||

|
||||
Here's a detailed description for each of these triggers:
|
||||
|
||||
1. `create_af_workspace_trigger`:
|
||||
|
||||
This trigger is designed to automate the process of workspace creation in the `af_workspace` table after a new user is inserted into the `af_user` table. When a new user is added, this trigger fires and inserts a new record into the `af_workspace` table, setting the `owner_uid` to the UID of the new user.
|
||||
|
||||
2. `manage_af_workspace_member_role_trigger`:
|
||||
|
||||
This trigger helps to manage the roles of workspace members. After an insert operation on the `af_workspace` table, this trigger automatically fires and creates a new record in the `af_workspace_member` table. The new record identifies the user as a member of the workspace with the role 'Owner'. This ensures that every new workspace has an owner.
|
||||
|
||||
3. `insert_into_af_collab_trigger`:
|
||||
|
||||
The purpose of this trigger is to ensure consistency between the `af_collab_update` and `af_collab` tables. When an insert operation is about to be performed on the `af_collab_update` table, this trigger fires before the insert operation. It checks if a corresponding collaboration exists in the `af_collab` table using the oid and uid. If a corresponding collaboration does not exist, the trigger creates one, using the oid, uid, and current timestamp. This way, every collab update operation corresponds to a valid collaboration.
|
||||
|
||||
4. `insert_into_af_collab_member_trigger`:
|
||||
|
||||
This trigger helps to manage the membership of users in collaborations. After a new collaboration is inserted into the `af_collab` table, this trigger fires. It checks if a corresponding collaboration member exists in the `af_collab_member` table. If a corresponding member does not exist, the trigger creates one, using the collaboration id and user id. This ensures that every collaboration has at least one member.
|
||||
|
||||
5. `af_collab_snapshot_update_edit_count_trigger`:
|
||||
|
||||
This trigger is designed to keep track of the number of edits on each collaboration snapshot in the `af_collab_snapshot` table. When an update operation is performed on the `af_collab_snapshot` table, this trigger fires. It increments the `edit_count` of the corresponding record in the `af_collab_snapshot` table by one. This ensures that the application can keep track of how many times each collaboration snapshot has been edited.
|
||||
|
||||
|
||||
### Supabase configuration
|
||||
|
||||
#### Test
|
||||
In order to run the test, you need to set up the .env.test file.
|
||||
```dotenv
|
||||
# Supabase configuration
|
||||
SUPABASE_URL="your-supabase-url"
|
||||
SUPABASE_ANON_KEY="your-supabase-anonymous-key"
|
||||
SUPABASE_KEY="your-supabase-key"
|
||||
SUPABASE_JWT_SECRET="your-supabase-jwt-secret"
|
||||
|
||||
# Supabase Database configuration
|
||||
SUPABASE_DB="your-supabase-db-url"
|
||||
SUPABASE_DB_USER="your-db-username"
|
||||
SUPABASE_DB_PORT="your-db-port"
|
||||
SUPABASE_DB_PASSWORD="your-db-password"
|
||||
```
|
||||
|
||||
1. `SUPABASE_URL`: This is the URL of your Supabase server instance. Your application will use this URL to interact with the Supabase service.
|
||||
|
||||
2. `SUPABASE_ANON_KEY`: This is the anonymous API key from Supabase, used for operations that don't require user authentication. Operations performed with this key are done as the anonymous role in the database.
|
||||
|
||||
3. `SUPABASE_KEY`: This is the API key with higher privileges from Supabase. It is generally used for server-side operations that require more permissions than an anonymous user.
|
||||
|
||||
4. `SUPABASE_JWT_SECRET`: This is the secret used to verify JWT tokens generated by Supabase. JWT or JSON Web Token is a standard method for securely transferring data between parties as a JSON object.
|
||||
|
||||
5. `SUPABASE_DB`: This is the URL for the database your Supabase server instance is using.
|
||||
|
||||
6. `SUPABASE_DB_USER`: This is the username used to authenticate with the Supabase database, in this case, it's 'postgres', which is a common default for PostgreSQL.
|
||||
|
||||
7. `SUPABASE_DB_PORT`: This is the port number where your Supabase database service is accessible. The default PostgreSQL port is 5432, and you are using this default port.
|
||||
|
||||
8. `SUPABASE_DB_PASSWORD`: This is the password used to authenticate the `SUPABASE_DB_USER` with the Supabase database.
|
||||
|
||||
For example, if you want to run the supabase tests located in flowy-test crate. You need to put the `.env.test` file under
|
||||
the flowy-test folder.
|
Binary file not shown.
Before Width: | Height: | Size: 61 KiB |
@ -1,78 +0,0 @@
|
||||
@startuml
|
||||
title "Application"
|
||||
left to right direction
|
||||
package "AppFlowy Application" {
|
||||
[User]
|
||||
}
|
||||
|
||||
cloud "Supabase Server" {
|
||||
[RESTful Component]
|
||||
[Realtime Component]
|
||||
[Postgres DB]
|
||||
}
|
||||
|
||||
database "LocalServer" {
|
||||
[Local Server Component]
|
||||
}
|
||||
|
||||
|
||||
cloud "AppFlowy Cloud Server" {
|
||||
[RESTful Component] as [AppFlowy RESTful Component]
|
||||
[Realtime Component] as [AppFlowy Realtime Component]
|
||||
[Postgres DB] as [AppFlowy Postgres DB]
|
||||
}
|
||||
|
||||
User --> [AppFlowy Application]
|
||||
[AppFlowy Application] --> [Local Server Component] : Connect
|
||||
|
||||
[AppFlowy Application] --> [RESTful Component] : RESTful API Communication
|
||||
[AppFlowy Application] <..> [Realtime Component] : WebSocket Communication
|
||||
|
||||
[AppFlowy Application] --> [AppFlowy RESTful Component] : RESTful API Communication
|
||||
[AppFlowy Application] <..> [AppFlowy Realtime Component] : WebSocket Communication
|
||||
|
||||
@enduml
|
||||
|
||||
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
interface AppFlowyServer {
|
||||
+ enable_sync(_enable: bool)
|
||||
+ user_service(): Arc<dyn UserService>
|
||||
+ folder_service(): Arc<dyn FolderCloudService>
|
||||
+ database_service(): Arc<dyn DatabaseCloudService>
|
||||
+ document_service(): Arc<dyn DocumentCloudService>
|
||||
+ collab_storage(): Option<Arc<dyn RemoteCollabStorage>>
|
||||
}
|
||||
|
||||
class SupabaseServer {
|
||||
+ enable_sync(_enable: bool)
|
||||
+ user_service(): Arc<dyn UserService>
|
||||
+ folder_service(): Arc<dyn FolderCloudService>
|
||||
+ database_service(): Arc<dyn DatabaseCloudService>
|
||||
+ document_service(): Arc<dyn DocumentCloudService>
|
||||
+ collab_storage(): Option<Arc<dyn RemoteCollabStorage>>
|
||||
}
|
||||
|
||||
class SelfHostServer {
|
||||
+ user_service(): Arc<dyn UserService>
|
||||
+ folder_service(): Arc<dyn FolderCloudService>
|
||||
+ database_service(): Arc<dyn DatabaseCloudService>
|
||||
+ document_service(): Arc<dyn DocumentCloudService>
|
||||
+ collab_storage(): Option<Arc<dyn RemoteCollabStorage>>
|
||||
}
|
||||
|
||||
class LocalServer {
|
||||
+ user_service(): Arc<dyn UserService>
|
||||
+ folder_service(): Arc<dyn FolderCloudService>
|
||||
+ database_service(): Arc<dyn DatabaseCloudService>
|
||||
+ document_service(): Arc<dyn DocumentCloudService>
|
||||
+ collab_storage(): Option<Arc<dyn RemoteCollabStorage>>
|
||||
}
|
||||
|
||||
SupabaseServer -u-|> AppFlowyServer
|
||||
SelfHostServer -u-|> AppFlowyServer
|
||||
LocalServer -u-|> AppFlowyServer
|
||||
|
||||
@enduml
|
Binary file not shown.
Before Width: | Height: | Size: 59 KiB |
Binary file not shown.
Before Width: | Height: | Size: 75 KiB |
@ -1,203 +0,0 @@
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
entity "af_roles" as roles {
|
||||
id : SERIAL (PK)
|
||||
name : TEXT
|
||||
}
|
||||
|
||||
entity "af_permissions" as permissions {
|
||||
id : SERIAL (PK)
|
||||
name : VARCHAR(255)
|
||||
access_level : INTEGER
|
||||
description : TEXT
|
||||
}
|
||||
|
||||
entity "af_role_permissions" as role_permissions {
|
||||
role_id : INT (FK af_roles.id)
|
||||
permission_id : INT (FK af_permissions.id)
|
||||
--
|
||||
(role_id, permission_id) : PK
|
||||
}
|
||||
|
||||
entity "af_user" as user {
|
||||
uuid : UUID (PK)
|
||||
email : TEXT
|
||||
uid : BIGSERIAL
|
||||
name : TEXT
|
||||
created_at : TIMESTAMP WITH TIME ZONE
|
||||
}
|
||||
|
||||
entity "af_workspace" as workspace {
|
||||
workspace_id : UUID (PK)
|
||||
database_storage_id : UUID
|
||||
owner_uid : BIGINT (FK af_user.uid)
|
||||
created_at : TIMESTAMP WITH TIME ZONE
|
||||
workspace_type : INTEGER
|
||||
workspace_name : TEXT
|
||||
}
|
||||
|
||||
entity "af_workspace_member" as workspace_member {
|
||||
uid : BIGINT
|
||||
role_id : INT (FK af_roles.id)
|
||||
workspace_id : UUID (FK af_workspace.workspace_id)
|
||||
created_at : TIMESTAMP WITH TIME ZONE
|
||||
updated_at : TIMESTAMP WITH TIME ZONE
|
||||
--
|
||||
(uid, workspace_id) : PK
|
||||
}
|
||||
|
||||
entity "af_collab" as collab {
|
||||
oid : TEXT (PK)
|
||||
owner_uid : BIGINT
|
||||
workspace_id : UUID (FK af_workspace.workspace_id)
|
||||
access_level : INTEGER
|
||||
created_at : TIMESTAMP WITH TIME ZONE
|
||||
}
|
||||
|
||||
entity "af_collab_update" as collab_update {
|
||||
oid : TEXT (FK af_collab.oid)
|
||||
key : BIGSERIAL
|
||||
value : BYTEA
|
||||
value_size : INTEGER
|
||||
partition_key : INTEGER
|
||||
uid : BIGINT
|
||||
md5 : TEXT
|
||||
created_at : TIMESTAMP WITH TIME ZONE
|
||||
workspace_id : UUID (FK af_workspace.workspace_id)
|
||||
--
|
||||
(oid, key, partition_key) : PK
|
||||
}
|
||||
|
||||
|
||||
entity "af_collab_update_document" as af_collab_update_document {
|
||||
Inherits af_collab_update (partition_key = 0)
|
||||
}
|
||||
|
||||
entity "af_collab_update_database" as af_collab_update_database {
|
||||
Inherits af_collab_update (partition_key = 1)
|
||||
}
|
||||
|
||||
entity "af_collab_update_w_database" as af_collab_update_w_database {
|
||||
Inherits af_collab_update (partition_key = 2)
|
||||
}
|
||||
|
||||
entity "af_collab_update_folder" as af_collab_update_folder {
|
||||
Inherits af_collab_update (partition_key = 3)
|
||||
}
|
||||
|
||||
af_collab_update_document -u-|> collab_update
|
||||
af_collab_update_database -u-|> collab_update
|
||||
af_collab_update_w_database -u-|> collab_update
|
||||
af_collab_update_folder -u-|> collab_update
|
||||
|
||||
entity "af_database_row_update" as database_row_update {
|
||||
oid : TEXT
|
||||
key : BIGSERIAL
|
||||
value : BYTEA
|
||||
value_size : INTEGER
|
||||
partition_key : INTEGER
|
||||
uid : BIGINT
|
||||
md5 : TEXT
|
||||
workspace_id : UUID (FK af_workspace.workspace_id)
|
||||
--
|
||||
(oid, key) : PK
|
||||
}
|
||||
|
||||
entity "af_collab_member" as collab_member {
|
||||
uid : BIGINT (FK af_user.uid)
|
||||
oid : TEXT (FK af_collab.oid)
|
||||
role_id : INTEGER (FK af_roles.id)
|
||||
--
|
||||
(uid, oid) : PK
|
||||
}
|
||||
|
||||
entity "af_collab_statistics" as collab_statistics {
|
||||
oid : TEXT (PK)
|
||||
edit_count : BIGINT
|
||||
}
|
||||
|
||||
entity "af_collab_snapshot" as collab_snapshot {
|
||||
sid : BIGSERIAL (PK)
|
||||
oid : TEXT (FK af_collab.oid)
|
||||
name : TEXT
|
||||
blob : BYTEA
|
||||
blob_size : INTEGER
|
||||
edit_count : BIGINT
|
||||
created_at : TIMESTAMP WITH TIME ZONE
|
||||
}
|
||||
|
||||
|
||||
roles <-- role_permissions : FK
|
||||
permissions <-u- role_permissions : FK
|
||||
user <-- collab : FK
|
||||
user <-- workspace : FK
|
||||
user <-- collab_member : FK
|
||||
roles <-- workspace_member : FK
|
||||
workspace <-- workspace_member : FK
|
||||
workspace <-- collab : FK
|
||||
workspace <-- database_row_update : FK
|
||||
collab <-- collab_update : FK
|
||||
collab <-- collab_snapshot: FK
|
||||
collab <-u- collab_member : FK
|
||||
collab <-- collab_statistics : PK
|
||||
roles <-- collab_member : FK
|
||||
|
||||
|
||||
@enduml
|
||||
|
||||
@startuml
|
||||
title Triggers in Database Schema
|
||||
|
||||
participant "af_user" as A
|
||||
participant "af_workspace" as B
|
||||
participant "af_workspace_member" as C
|
||||
participant "af_collab" as D
|
||||
participant "af_collab_update" as E
|
||||
participant "af_collab_member" as F
|
||||
participant "af_collab_statistics" as G
|
||||
participant "af_collab_snapshot" as H
|
||||
|
||||
A -> B: create_af_workspace_trigger
|
||||
note right
|
||||
This trigger fires after an insert on af_user. It automatically creates a workspace
|
||||
with the uid of the new user as the owner_uid.
|
||||
end note
|
||||
|
||||
B -> C: manage_af_workspace_member_role_trigger
|
||||
note right
|
||||
This trigger fires after an insert on af_workspace. It automatically
|
||||
creates a workspace member in the af_workspace_member table with the
|
||||
role 'Owner'.
|
||||
end note
|
||||
|
||||
E -> D: insert_into_af_collab_trigger
|
||||
note right
|
||||
This trigger fires before an insert on af_collab_update.
|
||||
It checks if a corresponding collab exists in the af_collab table.
|
||||
If not, it creates one with the oid, uid, and current timestamp.
|
||||
end note
|
||||
|
||||
D -> F: insert_into_af_collab_member_trigger
|
||||
note right
|
||||
This trigger fires after an insert on af_collab.
|
||||
It automatically adds the collab's owner to the af_collab_member
|
||||
table with the role 'Owner'.
|
||||
end note
|
||||
|
||||
E -> G: af_collab_update_edit_count_trigger
|
||||
note right
|
||||
This trigger fires after an insert on af_collab_update.
|
||||
It increments the edit_count of the corresponding collab in
|
||||
the af_collab_statistics table.
|
||||
end note
|
||||
|
||||
H -> G: af_collab_snapshot_update_edit_count_trigger
|
||||
note right
|
||||
This trigger fires after an insert on af_collab_snapshot.
|
||||
It sets the edit_count of the new snapshot to the current
|
||||
edit_count of the collab in the af_collab_statistics table.
|
||||
end note
|
||||
|
||||
@enduml
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 192 KiB |
@ -21,16 +21,16 @@ use crate::supabase::api::util::{ExtendedResponse, InsertParamsBuilder};
|
||||
use crate::supabase::api::{PostgresWrapper, SupabaseServerService};
|
||||
use crate::supabase::define::*;
|
||||
|
||||
pub struct RESTfulSupabaseCollabStorageImpl<T>(T);
|
||||
pub struct SupabaseCollabStorageImpl<T>(T);
|
||||
|
||||
impl<T> RESTfulSupabaseCollabStorageImpl<T> {
|
||||
impl<T> SupabaseCollabStorageImpl<T> {
|
||||
pub fn new(server: T) -> Self {
|
||||
Self(server)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T> RemoteCollabStorage for RESTfulSupabaseCollabStorageImpl<T>
|
||||
impl<T> RemoteCollabStorage for SupabaseCollabStorageImpl<T>
|
||||
where
|
||||
T: SupabaseServerService,
|
||||
{
|
||||
|
@ -12,17 +12,17 @@ use crate::supabase::api::request::{
|
||||
};
|
||||
use crate::supabase::api::SupabaseServerService;
|
||||
|
||||
pub struct RESTfulSupabaseDatabaseServiceImpl<T> {
|
||||
pub struct SupabaseDatabaseServiceImpl<T> {
|
||||
server: T,
|
||||
}
|
||||
|
||||
impl<T> RESTfulSupabaseDatabaseServiceImpl<T> {
|
||||
impl<T> SupabaseDatabaseServiceImpl<T> {
|
||||
pub fn new(server: T) -> Self {
|
||||
Self { server }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DatabaseCloudService for RESTfulSupabaseDatabaseServiceImpl<T>
|
||||
impl<T> DatabaseCloudService for SupabaseDatabaseServiceImpl<T>
|
||||
where
|
||||
T: SupabaseServerService,
|
||||
{
|
||||
|
@ -11,14 +11,14 @@ use lib_infra::future::FutureResult;
|
||||
use crate::supabase::api::request::{get_latest_snapshot_from_server, FetchObjectUpdateAction};
|
||||
use crate::supabase::api::SupabaseServerService;
|
||||
|
||||
pub struct RESTfulSupabaseDocumentServiceImpl<T>(T);
|
||||
impl<T> RESTfulSupabaseDocumentServiceImpl<T> {
|
||||
pub struct SupabaseDocumentServiceImpl<T>(T);
|
||||
impl<T> SupabaseDocumentServiceImpl<T> {
|
||||
pub fn new(server: T) -> Self {
|
||||
Self(server)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DocumentCloudService for RESTfulSupabaseDocumentServiceImpl<T>
|
||||
impl<T> DocumentCloudService for SupabaseDocumentServiceImpl<T>
|
||||
where
|
||||
T: SupabaseServerService,
|
||||
{
|
||||
@ -31,7 +31,7 @@ where
|
||||
async move {
|
||||
let postgrest = try_get_postgrest?;
|
||||
let action = FetchObjectUpdateAction::new(document_id, CollabType::Document, postgrest);
|
||||
action.run_with_fix_interval(5, 5).await
|
||||
action.run_with_fix_interval(5, 10).await
|
||||
}
|
||||
.await,
|
||||
)
|
||||
|
@ -19,15 +19,15 @@ use crate::supabase::api::util::{ExtendedResponse, InsertParamsBuilder};
|
||||
use crate::supabase::api::SupabaseServerService;
|
||||
use crate::supabase::define::*;
|
||||
|
||||
pub struct RESTfulSupabaseFolderServiceImpl<T>(T);
|
||||
pub struct SupabaseFolderServiceImpl<T>(T);
|
||||
|
||||
impl<T> RESTfulSupabaseFolderServiceImpl<T> {
|
||||
impl<T> SupabaseFolderServiceImpl<T> {
|
||||
pub fn new(server: T) -> Self {
|
||||
Self(server)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FolderCloudService for RESTfulSupabaseFolderServiceImpl<T>
|
||||
impl<T> FolderCloudService for SupabaseFolderServiceImpl<T>
|
||||
where
|
||||
T: SupabaseServerService,
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ use chrono::{DateTime, Utc};
|
||||
use collab_plugins::cloud_storage::{CollabObject, CollabType, RemoteCollabSnapshot};
|
||||
use serde_json::Value;
|
||||
use tokio_retry::strategy::FixedInterval;
|
||||
use tokio_retry::{Action, Retry};
|
||||
use tokio_retry::{Action, Condition, RetryIf};
|
||||
|
||||
use flowy_database_deps::cloud::{CollabObjectUpdate, CollabObjectUpdateByOid};
|
||||
use lib_infra::util::md5;
|
||||
@ -34,18 +34,20 @@ impl FetchObjectUpdateAction {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(self) -> Retry<Take<FixedInterval>, FetchObjectUpdateAction> {
|
||||
pub fn run(self) -> RetryIf<Take<FixedInterval>, FetchObjectUpdateAction, RetryCondition> {
|
||||
let postgrest = self.postgrest.clone();
|
||||
let retry_strategy = FixedInterval::new(Duration::from_secs(5)).take(3);
|
||||
Retry::spawn(retry_strategy, self)
|
||||
RetryIf::spawn(retry_strategy, self, RetryCondition(postgrest))
|
||||
}
|
||||
|
||||
pub fn run_with_fix_interval(
|
||||
self,
|
||||
secs: u64,
|
||||
times: usize,
|
||||
) -> Retry<Take<FixedInterval>, FetchObjectUpdateAction> {
|
||||
) -> RetryIf<Take<FixedInterval>, FetchObjectUpdateAction, RetryCondition> {
|
||||
let postgrest = self.postgrest.clone();
|
||||
let retry_strategy = FixedInterval::new(Duration::from_secs(secs)).take(times);
|
||||
Retry::spawn(retry_strategy, self)
|
||||
RetryIf::spawn(retry_strategy, self, RetryCondition(postgrest))
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,9 +91,10 @@ impl BatchFetchObjectUpdateAction {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(self) -> Retry<Take<FixedInterval>, BatchFetchObjectUpdateAction> {
|
||||
pub fn run(self) -> RetryIf<Take<FixedInterval>, BatchFetchObjectUpdateAction, RetryCondition> {
|
||||
let postgrest = self.postgrest.clone();
|
||||
let retry_strategy = FixedInterval::new(Duration::from_secs(5)).take(3);
|
||||
Retry::spawn(retry_strategy, self)
|
||||
RetryIf::spawn(retry_strategy, self, RetryCondition(postgrest))
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,3 +305,10 @@ fn decode_hex_string(s: &str) -> Option<Vec<u8>> {
|
||||
let s = s.strip_prefix("\\x")?;
|
||||
hex::decode(s).ok()
|
||||
}
|
||||
|
||||
pub struct RetryCondition(Weak<PostgresWrapper>);
|
||||
impl Condition<anyhow::Error> for RetryCondition {
|
||||
fn should_retry(&mut self, _error: &anyhow::Error) -> bool {
|
||||
self.0.upgrade().is_some()
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
use flowy_user_deps::cloud::UserService;
|
||||
|
||||
use crate::supabase::api::{
|
||||
RESTfulPostgresServer, RESTfulSupabaseCollabStorageImpl, RESTfulSupabaseDatabaseServiceImpl,
|
||||
RESTfulSupabaseDocumentServiceImpl, RESTfulSupabaseFolderServiceImpl,
|
||||
RESTfulSupabaseUserAuthServiceImpl, SupabaseServerServiceImpl,
|
||||
RESTfulPostgresServer, RESTfulSupabaseUserAuthServiceImpl, SupabaseCollabStorageImpl,
|
||||
SupabaseDatabaseServiceImpl, SupabaseDocumentServiceImpl, SupabaseFolderServiceImpl,
|
||||
SupabaseServerServiceImpl,
|
||||
};
|
||||
use crate::AppFlowyServer;
|
||||
|
||||
@ -96,25 +96,25 @@ impl AppFlowyServer for SupabaseServer {
|
||||
}
|
||||
|
||||
fn folder_service(&self) -> Arc<dyn FolderCloudService> {
|
||||
Arc::new(RESTfulSupabaseFolderServiceImpl::new(
|
||||
SupabaseServerServiceImpl(self.restful_postgres.clone()),
|
||||
))
|
||||
Arc::new(SupabaseFolderServiceImpl::new(SupabaseServerServiceImpl(
|
||||
self.restful_postgres.clone(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn database_service(&self) -> Arc<dyn DatabaseCloudService> {
|
||||
Arc::new(RESTfulSupabaseDatabaseServiceImpl::new(
|
||||
SupabaseServerServiceImpl(self.restful_postgres.clone()),
|
||||
))
|
||||
Arc::new(SupabaseDatabaseServiceImpl::new(SupabaseServerServiceImpl(
|
||||
self.restful_postgres.clone(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn document_service(&self) -> Arc<dyn DocumentCloudService> {
|
||||
Arc::new(RESTfulSupabaseDocumentServiceImpl::new(
|
||||
SupabaseServerServiceImpl(self.restful_postgres.clone()),
|
||||
))
|
||||
Arc::new(SupabaseDocumentServiceImpl::new(SupabaseServerServiceImpl(
|
||||
self.restful_postgres.clone(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn collab_storage(&self) -> Option<Arc<dyn RemoteCollabStorage>> {
|
||||
Some(Arc::new(RESTfulSupabaseCollabStorageImpl::new(
|
||||
Some(Arc::new(SupabaseCollabStorageImpl::new(
|
||||
SupabaseServerServiceImpl(self.restful_postgres.clone()),
|
||||
)))
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ use uuid::Uuid;
|
||||
use flowy_database_deps::cloud::DatabaseCloudService;
|
||||
use flowy_folder_deps::cloud::FolderCloudService;
|
||||
use flowy_server::supabase::api::{
|
||||
RESTfulPostgresServer, RESTfulSupabaseCollabStorageImpl, RESTfulSupabaseDatabaseServiceImpl,
|
||||
RESTfulSupabaseFolderServiceImpl, RESTfulSupabaseUserAuthServiceImpl, SupabaseServerServiceImpl,
|
||||
RESTfulPostgresServer, RESTfulSupabaseUserAuthServiceImpl, SupabaseCollabStorageImpl,
|
||||
SupabaseDatabaseServiceImpl, SupabaseFolderServiceImpl, SupabaseServerServiceImpl,
|
||||
};
|
||||
use flowy_server::supabase::define::{USER_EMAIL, USER_UUID};
|
||||
use flowy_server_config::supabase_config::SupabaseConfiguration;
|
||||
@ -25,7 +25,7 @@ pub fn get_supabase_config() -> Option<SupabaseConfiguration> {
|
||||
pub fn collab_service() -> Arc<dyn RemoteCollabStorage> {
|
||||
let config = SupabaseConfiguration::from_env().unwrap();
|
||||
let server = Arc::new(RESTfulPostgresServer::new(config));
|
||||
Arc::new(RESTfulSupabaseCollabStorageImpl::new(
|
||||
Arc::new(SupabaseCollabStorageImpl::new(
|
||||
SupabaseServerServiceImpl::new(server),
|
||||
))
|
||||
}
|
||||
@ -33,7 +33,7 @@ pub fn collab_service() -> Arc<dyn RemoteCollabStorage> {
|
||||
pub fn database_service() -> Arc<dyn DatabaseCloudService> {
|
||||
let config = SupabaseConfiguration::from_env().unwrap();
|
||||
let server = Arc::new(RESTfulPostgresServer::new(config));
|
||||
Arc::new(RESTfulSupabaseDatabaseServiceImpl::new(
|
||||
Arc::new(SupabaseDatabaseServiceImpl::new(
|
||||
SupabaseServerServiceImpl::new(server),
|
||||
))
|
||||
}
|
||||
@ -49,7 +49,7 @@ pub fn user_auth_service() -> Arc<dyn UserService> {
|
||||
pub fn folder_service() -> Arc<dyn FolderCloudService> {
|
||||
let config = SupabaseConfiguration::from_env().unwrap();
|
||||
let server = Arc::new(RESTfulPostgresServer::new(config));
|
||||
Arc::new(RESTfulSupabaseFolderServiceImpl::new(
|
||||
Arc::new(SupabaseFolderServiceImpl::new(
|
||||
SupabaseServerServiceImpl::new(server),
|
||||
))
|
||||
}
|
||||
|
@ -584,10 +584,7 @@ impl UserSession {
|
||||
match KV::get_object::<Session>(&self.session_config.session_cache_key) {
|
||||
None => Err(FlowyError::new(
|
||||
ErrorCode::RecordNotFound,
|
||||
format!(
|
||||
"Can't find the value of {}, User is not logged in",
|
||||
self.session_config.session_cache_key
|
||||
),
|
||||
"User is not logged in",
|
||||
)),
|
||||
Some(session) => Ok(session),
|
||||
}
|
||||
|
@ -59,6 +59,19 @@ script = [
|
||||
]
|
||||
script_runner = "@shell"
|
||||
|
||||
[tasks.sdk-build.mac]
|
||||
private = true
|
||||
script = [
|
||||
"""
|
||||
cd rust-lib/
|
||||
rustup show
|
||||
echo cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
RUSTFLAGS="-C target-cpu=native -C link-arg=-mmacosx-version-min=11.0" cargo build --package=dart-ffi --target ${RUST_COMPILE_TARGET} --features "${FLUTTER_DESKTOP_FEATURES}"
|
||||
cd ../
|
||||
""",
|
||||
]
|
||||
script_runner = "@shell"
|
||||
|
||||
[tasks.sdk-build-android]
|
||||
private = true
|
||||
script = [
|
||||
|
Loading…
Reference in New Issue
Block a user