Skip to content

Commit c41a7aa

Browse files
authored
chore: support localhost and development mode (#4384)
* chore: update local port for development * chore: support localhost development * chore: fix rust test * chore: update setting * fix: ci * fix: ci * fix: ci * fix: ci * chore: fix docker build * chore: fix ci
1 parent d200c40 commit c41a7aa

File tree

20 files changed

+269
-155
lines changed

20 files changed

+269
-155
lines changed

.github/workflows/flutter_ci.yaml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -239,22 +239,17 @@ jobs:
239239
working-directory: AppFlowy-Cloud
240240
run: |
241241
# log level
242-
cp dev.env .env
242+
cp deploy.env .env
243243
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
244-
sed -i 's/GOTRUE_SMTP_USER=.*/GOTRUE_SMTP_USER=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_USER }}/' .env
245-
sed -i 's/GOTRUE_SMTP_PASS=.*/GOTRUE_SMTP_PASS=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_PASS }}/' .env
246-
sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_ADMIN_EMAIL }}/' .env
247244
sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
248245
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
249-
cat .env
250246
251247
- name: Run Docker-Compose
252248
working-directory: AppFlowy-Cloud
253249
run: |
254250
docker compose down -v --remove-orphans
255251
docker pull appflowyinc/appflowy_cloud:latest
256252
docker compose up -d
257-
docker ps -a
258253
259254
- name: Checkout source code
260255
uses: actions/checkout@v2

.github/workflows/rust_ci.yaml

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,22 +77,26 @@ jobs:
7777
working-directory: AppFlowy-Cloud
7878
run: |
7979
# log level
80-
cp dev.env .env
80+
cp deploy.env .env
8181
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
82-
sed -i 's/GOTRUE_SMTP_USER=.*/GOTRUE_SMTP_USER=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_USER }}/' .env
83-
sed -i 's/GOTRUE_SMTP_PASS=.*/GOTRUE_SMTP_PASS=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_PASS }}/' .env
84-
sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.INTEGRATION_TEST_GOTRUE_SMTP_ADMIN_EMAIL }}/' .env
85-
sed -i 's/GOTRUE_EXTERNAL_GOOGLE_ENABLED=.*/GOTRUE_EXTERNAL_GOOGLE_ENABLED=true/' .env
8682
sed -i 's|API_EXTERNAL_URL=.*|API_EXTERNAL_URL=http://localhost|' .env
8783
8884
- name: Run Docker-Compose
8985
working-directory: AppFlowy-Cloud
9086
run: |
87+
docker pull appflowyinc/appflowy_cloud:latest
9188
docker compose up -d
9289
9390
- name: Run rust-lib tests
9491
working-directory: frontend/rust-lib
95-
run: RUST_LOG=info RUST_BACKTRACE=1 cargo test --no-default-features --features="rev-sqlite"
92+
env:
93+
RUST_LOG: info
94+
RUST_BACKTRACE: 1
95+
af_cloud_test_base_url: http://localhost
96+
af_cloud_test_ws_url: ws://localhost/ws
97+
af_cloud_test_gotrue_url: http://localhost/gotrue
98+
run: cargo test --no-default-features --features="rev-sqlite" -- --nocapture
99+
96100

97101
- name: rustfmt rust-lib
98102
run: cargo fmt --all -- --check

frontend/appflowy_flutter/integration_test/util/base.dart

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,12 @@ extension AppFlowyTestBase on WidgetTester {
5959
break;
6060
case AuthenticatorType.supabase:
6161
break;
62-
case AuthenticatorType.appflowyCloud:
63-
rustEnvs["GOTRUE_ADMIN_EMAIL"] = "[email protected]";
64-
rustEnvs["GOTRUE_ADMIN_PASSWORD"] = "password";
65-
break;
6662
case AuthenticatorType.appflowyCloudSelfHost:
6763
rustEnvs["GOTRUE_ADMIN_EMAIL"] = "[email protected]";
6864
rustEnvs["GOTRUE_ADMIN_PASSWORD"] = "password";
6965
break;
66+
default:
67+
throw Exception("not supported");
7068
}
7169
}
7270
return rustEnvs;
@@ -86,20 +84,14 @@ extension AppFlowyTestBase on WidgetTester {
8684
() => SupabaseMockAuthService(),
8785
);
8886
break;
89-
case AuthenticatorType.appflowyCloud:
90-
await useAppFlowyCloud();
91-
getIt.unregister<AuthService>();
92-
getIt.registerFactory<AuthService>(
93-
() => AppFlowyCloudMockAuthService(email: email),
94-
);
95-
break;
9687
case AuthenticatorType.appflowyCloudSelfHost:
9788
await useAppFlowyCloud();
9889
getIt.unregister<AuthService>();
9990
getIt.registerFactory<AuthService>(
10091
() => AppFlowyCloudMockAuthService(email: email),
10192
);
102-
break;
93+
default:
94+
throw Exception("not supported");
10395
}
10496
}
10597
},

frontend/appflowy_flutter/lib/env/cloud_env.dart

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ Future<void> setAuthenticatorType(AuthenticatorType ty) async {
2727
break;
2828
case AuthenticatorType.appflowyCloud:
2929
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 2.toString());
30-
await setAppFlowyCloudUrl(const Some(kAppflowyCloudUrl));
3130
break;
3231
case AuthenticatorType.appflowyCloudSelfHost:
3332
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 3.toString());
3433
break;
34+
case AuthenticatorType.appflowyCloudDevelop:
35+
await getIt<KeyValueStorage>().set(KVKeys.kCloudType, 4.toString());
36+
break;
3537
}
3638
}
3739

@@ -64,6 +66,8 @@ Future<AuthenticatorType> getAuthenticatorType() async {
6466
return AuthenticatorType.appflowyCloud;
6567
case "3":
6668
return AuthenticatorType.appflowyCloudSelfHost;
69+
case "4":
70+
return AuthenticatorType.appflowyCloudDevelop;
6771
default:
6872
await setAuthenticatorType(AuthenticatorType.appflowyCloud);
6973
return AuthenticatorType.appflowyCloud;
@@ -87,8 +91,7 @@ bool get isAuthEnabled {
8791
return env.supabaseConfig.isValid;
8892
}
8993

90-
if (env.authenticatorType == AuthenticatorType.appflowyCloudSelfHost ||
91-
env.authenticatorType == AuthenticatorType.appflowyCloud) {
94+
if (env.authenticatorType.isAppFlowyCloudEnabled) {
9295
return env.appflowyCloudConfig.isValid;
9396
}
9497

@@ -117,12 +120,15 @@ enum AuthenticatorType {
117120
local,
118121
supabase,
119122
appflowyCloud,
120-
appflowyCloudSelfHost;
123+
appflowyCloudSelfHost,
124+
// The 'appflowyCloudDevelop' type is used for develop purposes only.
125+
appflowyCloudDevelop;
121126

122127
bool get isLocal => this == AuthenticatorType.local;
123128

124129
bool get isAppFlowyCloudEnabled =>
125130
this == AuthenticatorType.appflowyCloudSelfHost ||
131+
this == AuthenticatorType.appflowyCloudDevelop ||
126132
this == AuthenticatorType.appflowyCloud;
127133

128134
bool get isSupabaseEnabled => this == AuthenticatorType.supabase;
@@ -137,6 +143,8 @@ enum AuthenticatorType {
137143
return 2;
138144
case AuthenticatorType.appflowyCloudSelfHost:
139145
return 3;
146+
case AuthenticatorType.appflowyCloudDevelop:
147+
return 4;
140148
}
141149
}
142150

@@ -150,6 +158,8 @@ enum AuthenticatorType {
150158
return AuthenticatorType.appflowyCloud;
151159
case 3:
152160
return AuthenticatorType.appflowyCloudSelfHost;
161+
case 4:
162+
return AuthenticatorType.appflowyCloudDevelop;
153163
default:
154164
return AuthenticatorType.local;
155165
}
@@ -185,26 +195,25 @@ class AppFlowyCloudSharedEnv {
185195
// If [Env.enableCustomCloud] is true, then use the custom cloud configuration.
186196
if (Env.enableCustomCloud) {
187197
// Use the custom cloud configuration.
188-
var cloudType = await getAuthenticatorType();
198+
var authenticatorType = await getAuthenticatorType();
199+
200+
final appflowyCloudConfig = authenticatorType.isLocal
201+
? AppFlowyCloudConfiguration.defaultConfig()
202+
: await getAppFlowyCloudConfig(authenticatorType);
203+
final supabaseCloudConfig = authenticatorType.isLocal
204+
? SupabaseConfiguration.defaultConfig()
205+
: await getSupabaseCloudConfig();
189206

190207
// In the backend, the value '2' represents the use of AppFlowy Cloud. However, in the frontend,
191208
// we distinguish between [AuthenticatorType.appflowyCloudSelfHost] and [AuthenticatorType.appflowyCloud].
192209
// When the cloud type is [AuthenticatorType.appflowyCloudSelfHost] in the frontend, it should be
193210
// converted to [AuthenticatorType.appflowyCloud] to align with the backend representation,
194211
// where both types are indicated by the value '2'.
195-
if (cloudType == AuthenticatorType.appflowyCloudSelfHost) {
196-
cloudType = AuthenticatorType.appflowyCloud;
212+
if (authenticatorType.isAppFlowyCloudEnabled) {
213+
authenticatorType = AuthenticatorType.appflowyCloud;
197214
}
198-
199-
final appflowyCloudConfig = cloudType.isLocal
200-
? AppFlowyCloudConfiguration.defaultConfig()
201-
: await getAppFlowyCloudConfig();
202-
final supabaseCloudConfig = cloudType.isLocal
203-
? SupabaseConfiguration.defaultConfig()
204-
: await getSupabaseCloudConfig();
205-
206215
return AppFlowyCloudSharedEnv(
207-
authenticatorType: cloudType,
216+
authenticatorType: authenticatorType,
208217
appflowyCloudConfig: appflowyCloudConfig,
209218
supabaseConfig: supabaseCloudConfig,
210219
);
@@ -235,13 +244,16 @@ class AppFlowyCloudSharedEnv {
235244
Future<AppFlowyCloudConfiguration> configurationFromUri(
236245
Uri baseUri,
237246
String baseUrl,
247+
AuthenticatorType authenticatorType,
238248
) async {
239-
// When the host is set to 'localhost', the application will utilize the local configuration. This setup assumes that 'localhost' does not employ a reverse proxy, therefore default port settings are used.
240-
if (baseUri.host == "localhost") {
249+
// In development mode, the app is configured to access the AppFlowy cloud server directly through specific ports.
250+
// This setup bypasses the need for Nginx, meaning that the AppFlowy cloud should be running without an Nginx server
251+
// in the development environment.
252+
if (authenticatorType == AuthenticatorType.appflowyCloudDevelop) {
241253
return AppFlowyCloudConfiguration(
242254
base_url: "$baseUrl:8000",
243255
ws_base_url: "ws://${baseUri.host}:8000/ws",
244-
gotrue_url: "$baseUrl:9998",
256+
gotrue_url: "$baseUrl:9999",
245257
);
246258
} else {
247259
return AppFlowyCloudConfiguration(
@@ -252,12 +264,14 @@ Future<AppFlowyCloudConfiguration> configurationFromUri(
252264
}
253265
}
254266

255-
Future<AppFlowyCloudConfiguration> getAppFlowyCloudConfig() async {
267+
Future<AppFlowyCloudConfiguration> getAppFlowyCloudConfig(
268+
AuthenticatorType authenticatorType,
269+
) async {
256270
final baseURL = await getAppFlowyCloudUrl();
257271

258272
try {
259273
final uri = Uri.parse(baseURL);
260-
return await configurationFromUri(uri, baseURL);
274+
return await configurationFromUri(uri, baseURL, authenticatorType);
261275
} catch (e) {
262276
Log.error("Failed to parse AppFlowy Cloud URL: $e");
263277
return AppFlowyCloudConfiguration.defaultConfig();

frontend/appflowy_flutter/lib/env/env.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ abstract class Env {
1010
static bool get enableCustomCloud {
1111
return Env.authenticatorType ==
1212
AuthenticatorType.appflowyCloudSelfHost.value ||
13-
Env.authenticatorType == AuthenticatorType.appflowyCloud.value &&
13+
Env.authenticatorType == AuthenticatorType.appflowyCloud.value ||
14+
Env.authenticatorType == AuthenticatorType.appflowyCloudDevelop.value &&
1415
_Env.afCloudUrl.isEmpty;
1516
}
1617

frontend/appflowy_flutter/lib/startup/deps_resolver.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ void _resolveUserDeps(GetIt getIt, IntegrationMode mode) {
142142
break;
143143
case AuthenticatorType.appflowyCloud:
144144
case AuthenticatorType.appflowyCloudSelfHost:
145+
case AuthenticatorType.appflowyCloudDevelop:
145146
getIt.registerFactory<AuthService>(() => AppFlowyCloudAuthService());
146147
break;
147148
}

frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_appflowy_cloud.dart

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,15 @@ import 'package:flutter_bloc/flutter_bloc.dart';
1919
import 'package:url_launcher/url_launcher.dart';
2020

2121
class AppFlowyCloudViewSetting extends StatelessWidget {
22+
final String serverURL;
23+
final AuthenticatorType authenticatorType;
2224
final VoidCallback restartAppFlowy;
23-
const AppFlowyCloudViewSetting({required this.restartAppFlowy, super.key});
25+
const AppFlowyCloudViewSetting({
26+
required this.restartAppFlowy,
27+
super.key,
28+
this.serverURL = kAppflowyCloudUrl,
29+
this.authenticatorType = AuthenticatorType.appflowyCloud,
30+
});
2431

2532
@override
2633
Widget build(BuildContext context) {
@@ -58,11 +65,8 @@ class AppFlowyCloudViewSetting extends StatelessWidget {
5865
NavigatorAlertDialog(
5966
title: LocaleKeys.settings_menu_restartAppTip.tr(),
6067
confirm: () async {
61-
await setAppFlowyCloudUrl(
62-
const Some(kAppflowyCloudUrl),
63-
);
64-
65-
await setAuthenticatorType(AuthenticatorType.appflowyCloud);
68+
await setAppFlowyCloudUrl(Some(serverURL));
69+
await setAuthenticatorType(authenticatorType);
6670
restartAppFlowy();
6771
},
6872
).show(context);

frontend/appflowy_flutter/lib/workspace/presentation/settings/widgets/setting_cloud.dart

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'package:appflowy/env/cloud_env.dart';
22
import 'package:appflowy/env/env.dart';
33
import 'package:appflowy/generated/flowy_svgs.g.dart';
44
import 'package:appflowy/generated/locale_keys.g.dart';
5+
import 'package:appflowy/startup/startup.dart';
56
import 'package:appflowy/workspace/application/settings/cloud_setting_bloc.dart';
67
import 'package:appflowy/workspace/presentation/settings/widgets/setting_local_cloud.dart';
78
import 'package:appflowy_popover/appflowy_popover.dart';
@@ -86,6 +87,12 @@ class SettingCloud extends StatelessWidget {
8687
return CustomAppFlowyCloudView(
8788
restartAppFlowy: didResetServerUrl,
8889
);
90+
case AuthenticatorType.appflowyCloudDevelop:
91+
return AppFlowyCloudViewSetting(
92+
serverURL: "http://localhost",
93+
authenticatorType: AuthenticatorType.appflowyCloudDevelop,
94+
restartAppFlowy: didResetServerUrl,
95+
);
8996
}
9097
}
9198
}
@@ -111,16 +118,26 @@ class CloudTypeSwitcher extends StatelessWidget {
111118
onPressed: () {},
112119
),
113120
popupBuilder: (BuildContext context) {
121+
final isDevelopMode = integrationMode().isDevelop;
122+
// Only show the appflowyCloudDevelop in develop mode
123+
final values = AuthenticatorType.values
124+
.where(
125+
(element) =>
126+
isDevelopMode ||
127+
element != AuthenticatorType.appflowyCloudDevelop,
128+
)
129+
.toList();
130+
114131
return ListView.builder(
115132
shrinkWrap: true,
116133
itemBuilder: (context, index) {
117134
return CloudTypeItem(
118-
cloudType: AuthenticatorType.values[index],
135+
cloudType: values[index],
119136
currentCloudtype: cloudType,
120137
onSelected: onSelected,
121138
);
122139
},
123-
itemCount: AuthenticatorType.values.length,
140+
itemCount: values.length,
124141
);
125142
},
126143
);
@@ -171,5 +188,7 @@ String titleFromCloudType(AuthenticatorType cloudType) {
171188
return LocaleKeys.settings_menu_cloudAppFlowy.tr();
172189
case AuthenticatorType.appflowyCloudSelfHost:
173190
return LocaleKeys.settings_menu_cloudAppFlowySelfHost.tr();
191+
case AuthenticatorType.appflowyCloudDevelop:
192+
return "AppFlowyCloud Develop";
174193
}
175194
}

0 commit comments

Comments
 (0)