Ver código fonte

feat: add open ai key to database (#1852)

* feat: add open ai key to database

* chore: refactor code
Lucas.Xu 2 anos atrás
pai
commit
72e155f5b9

+ 6 - 0
frontend/app_flowy/assets/translations/en.json

@@ -191,6 +191,12 @@
       "create": "Create",
       "folderPath": "Path to store your folder",
       "locationCannotBeEmpty": "Path cannot be empty"
+    },
+    "user": {
+      "name": "Name",
+      "icon": "Icon",
+      "selectAnIcon": "Select an icon",
+      "pleaseInputYourOpenAIKey": "please input your OpenAI key"
     }
   },
   "grid": {

+ 5 - 0
frontend/app_flowy/lib/user/application/user_service.dart

@@ -21,6 +21,7 @@ class UserService {
     String? password,
     String? email,
     String? iconUrl,
+    String? openAIKey,
   }) {
     var payload = UpdateUserProfilePayloadPB.create()..id = userId;
 
@@ -40,6 +41,10 @@ class UserService {
       payload.iconUrl = iconUrl;
     }
 
+    if (openAIKey != null) {
+      payload.openaiKey = openAIKey;
+    }
+
     return UserEventUpdateUserProfile(payload).send();
   }
 

+ 10 - 0
frontend/app_flowy/lib/workspace/application/user/settings_user_bloc.dart

@@ -43,6 +43,14 @@ class SettingsUserViewBloc extends Bloc<SettingsUserEvent, SettingsUserState> {
             );
           });
         },
+        updateUserOpenaiKey: (openAIKey) {
+          _userService.updateUserProfile(openAIKey: openAIKey).then((result) {
+            result.fold(
+              (l) => null,
+              (err) => Log.error(err),
+            );
+          });
+        },
       );
     });
   }
@@ -73,6 +81,8 @@ class SettingsUserEvent with _$SettingsUserEvent {
   const factory SettingsUserEvent.updateUserName(String name) = _UpdateUserName;
   const factory SettingsUserEvent.updateUserIcon(String iconUrl) =
       _UpdateUserIcon;
+  const factory SettingsUserEvent.updateUserOpenaiKey(String openAIKey) =
+      _UpdateUserOpenaiKey;
   const factory SettingsUserEvent.didReceiveUserProfile(
       UserProfilePB newUserProfile) = _DidReceiveUserProfile;
 }

+ 50 - 14
frontend/app_flowy/lib/workspace/presentation/settings/widgets/settings_user_view.dart

@@ -7,6 +7,8 @@ import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:flowy_infra_ui/widget/spacing.dart';
 import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pb.dart';
 import 'package:flowy_infra/image.dart';
+import 'package:app_flowy/generated/locale_keys.g.dart';
+import 'package:easy_localization/easy_localization.dart';
 
 import 'dart:convert';
 
@@ -28,7 +30,9 @@ class SettingsUserView extends StatelessWidget {
             children: [
               _renderUserNameInput(context),
               const VSpace(20),
-              _renderCurrentIcon(context)
+              _renderCurrentIcon(context),
+              const VSpace(20),
+              _renderCurrentOpenaiKey(context)
             ],
           ),
         ),
@@ -49,6 +53,12 @@ class SettingsUserView extends StatelessWidget {
     }
     return _CurrentIcon(iconUrl);
   }
+
+  Widget _renderCurrentOpenaiKey(BuildContext context) {
+    String openAIKey =
+        context.read<SettingsUserViewBloc>().state.userProfile.openaiKey;
+    return _OpenaiKeyInput(openAIKey);
+  }
 }
 
 @visibleForTesting
@@ -62,15 +72,41 @@ class UserNameInput extends StatelessWidget {
   @override
   Widget build(BuildContext context) {
     return TextField(
-        controller: TextEditingController()..text = name,
-        decoration: const InputDecoration(
-          labelText: 'Name',
-        ),
-        onSubmitted: (val) {
-          context
-              .read<SettingsUserViewBloc>()
-              .add(SettingsUserEvent.updateUserName(val));
-        });
+      controller: TextEditingController()..text = name,
+      decoration: InputDecoration(
+        labelText: LocaleKeys.settings_user_name.tr(),
+      ),
+      onSubmitted: (val) {
+        context
+            .read<SettingsUserViewBloc>()
+            .add(SettingsUserEvent.updateUserName(val));
+      },
+    );
+  }
+}
+
+class _OpenaiKeyInput extends StatelessWidget {
+  final String openAIKey;
+  const _OpenaiKeyInput(
+    this.openAIKey, {
+    Key? key,
+  }) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return TextField(
+      controller: TextEditingController()..text = openAIKey,
+      decoration: InputDecoration(
+        labelText: 'Openai Key',
+        hintText: LocaleKeys.settings_user_pleaseInputYourOpenAIKey.tr(),
+      ),
+      onSubmitted: (val) {
+        // TODO: validate key
+        context
+            .read<SettingsUserViewBloc>()
+            .add(SettingsUserEvent.updateUserOpenaiKey(val));
+      },
+    );
   }
 }
 
@@ -96,7 +132,7 @@ class _CurrentIcon extends StatelessWidget {
             builder: (BuildContext context) {
               return SimpleDialog(
                 title: FlowyText.medium(
-                  'Select an Icon',
+                  LocaleKeys.settings_user_selectAnIcon.tr(),
                   fontSize: FontSizes.s16,
                 ),
                 children: <Widget>[
@@ -112,11 +148,11 @@ class _CurrentIcon extends StatelessWidget {
         },
         child: Column(
           children: <Widget>[
-            const Align(
+            Align(
                 alignment: Alignment.topLeft,
                 child: Text(
-                  "Icon",
-                  style: TextStyle(color: Colors.grey),
+                  LocaleKeys.settings_user_icon.tr(),
+                  style: const TextStyle(color: Colors.grey),
                 )),
             Align(
               alignment: Alignment.centerLeft,

+ 1 - 0
frontend/rust-lib/flowy-sqlite/migrations/2023-02-13-102237_user-add-openai_key/down.sql

@@ -0,0 +1 @@
+ALTER TABLE user_table DROP COLUMN openai_key;

+ 1 - 0
frontend/rust-lib/flowy-sqlite/migrations/2023-02-13-102237_user-add-openai_key/up.sql

@@ -0,0 +1 @@
+ALTER TABLE user_table ADD COLUMN openai_key TEXT NOT NULL DEFAULT '';

+ 1 - 0
frontend/rust-lib/flowy-sqlite/src/schema.rs

@@ -146,6 +146,7 @@ diesel::table! {
         email -> Text,
         workspace -> Text,
         icon_url -> Text,
+        openai_key -> Text,
     }
 }
 

+ 20 - 1
frontend/rust-lib/flowy-user/src/entities/user_profile.rs

@@ -2,7 +2,8 @@ use crate::errors::ErrorCode;
 use flowy_derive::ProtoBuf;
 use std::convert::TryInto;
 use user_model::{
-  UpdateUserProfileParams, UserEmail, UserIcon, UserId, UserName, UserPassword, UserProfile,
+  UpdateUserProfileParams, UserEmail, UserIcon, UserId, UserName, UserOpenaiKey, UserPassword,
+  UserProfile,
 };
 
 #[derive(Default, ProtoBuf)]
@@ -33,6 +34,9 @@ pub struct UserProfilePB {
 
   #[pb(index = 5)]
   pub icon_url: String,
+
+  #[pb(index = 6)]
+  pub openai_key: String,
 }
 
 impl std::convert::From<UserProfile> for UserProfilePB {
@@ -43,6 +47,7 @@ impl std::convert::From<UserProfile> for UserProfilePB {
       name: user_profile.name,
       token: user_profile.token,
       icon_url: user_profile.icon_url,
+      openai_key: user_profile.openai_key,
     }
   }
 }
@@ -63,6 +68,9 @@ pub struct UpdateUserProfilePayloadPB {
 
   #[pb(index = 5, one_of)]
   pub icon_url: Option<String>,
+
+  #[pb(index = 6, one_of)]
+  pub openai_key: Option<String>,
 }
 
 impl UpdateUserProfilePayloadPB {
@@ -92,6 +100,11 @@ impl UpdateUserProfilePayloadPB {
     self.icon_url = Some(icon_url.to_owned());
     self
   }
+
+  pub fn openai_key(mut self, openai_key: &str) -> Self {
+    self.openai_key = Some(openai_key.to_owned());
+    self
+  }
 }
 
 impl TryInto<UpdateUserProfileParams> for UpdateUserProfilePayloadPB {
@@ -120,12 +133,18 @@ impl TryInto<UpdateUserProfileParams> for UpdateUserProfilePayloadPB {
       Some(icon_url) => Some(UserIcon::parse(icon_url)?.0),
     };
 
+    let openai_key = match self.openai_key {
+      None => None,
+      Some(openai_key) => Some(UserOpenaiKey::parse(openai_key)?.0),
+    };
+
     Ok(UpdateUserProfileParams {
       id,
       name,
       email,
       password,
       icon_url,
+      openai_key,
     })
   }
 }

+ 5 - 0
frontend/rust-lib/flowy-user/src/services/database.rs

@@ -84,6 +84,7 @@ pub struct UserTable {
   pub(crate) email: String,
   pub(crate) workspace: String, // deprecated
   pub(crate) icon_url: String,
+  pub(crate) openai_key: String,
 }
 
 impl UserTable {
@@ -95,6 +96,7 @@ impl UserTable {
       token,
       icon_url: "".to_owned(),
       workspace: "".to_owned(),
+      openai_key: "".to_owned(),
     }
   }
 
@@ -124,6 +126,7 @@ impl std::convert::From<UserTable> for UserProfile {
       name: table.name,
       token: table.token,
       icon_url: table.icon_url,
+      openai_key: table.openai_key,
     }
   }
 }
@@ -136,6 +139,7 @@ pub struct UserTableChangeset {
   pub name: Option<String>,
   pub email: Option<String>,
   pub icon_url: Option<String>,
+  pub openai_key: Option<String>,
 }
 
 impl UserTableChangeset {
@@ -146,6 +150,7 @@ impl UserTableChangeset {
       name: params.name,
       email: params.email,
       icon_url: params.icon_url,
+      openai_key: params.openai_key,
     }
   }
 }

+ 8 - 0
shared-lib/user-model/src/lib.rs

@@ -42,6 +42,7 @@ pub struct UserProfile {
   pub name: String,
   pub token: String,
   pub icon_url: String,
+  pub openai_key: String,
 }
 
 #[derive(Serialize, Deserialize, Default, Clone, Debug)]
@@ -51,6 +52,7 @@ pub struct UpdateUserProfileParams {
   pub email: Option<String>,
   pub password: Option<String>,
   pub icon_url: Option<String>,
+  pub openai_key: Option<String>,
 }
 
 impl UpdateUserProfileParams {
@@ -61,6 +63,7 @@ impl UpdateUserProfileParams {
       email: None,
       password: None,
       icon_url: None,
+      openai_key: None,
     }
   }
 
@@ -83,4 +86,9 @@ impl UpdateUserProfileParams {
     self.icon_url = Some(icon_url.to_owned());
     self
   }
+
+  pub fn openai_key(mut self, openai_key: &str) -> Self {
+    self.openai_key = Some(openai_key.to_owned());
+    self
+  }
 }

+ 2 - 0
shared-lib/user-model/src/parser/mod.rs

@@ -3,6 +3,7 @@ mod user_email;
 mod user_icon;
 mod user_id;
 mod user_name;
+mod user_openai_key;
 mod user_password;
 mod user_workspace;
 
@@ -10,5 +11,6 @@ pub use user_email::*;
 pub use user_icon::*;
 pub use user_id::*;
 pub use user_name::*;
+pub use user_openai_key::*;
 pub use user_password::*;
 pub use user_workspace::*;

+ 16 - 0
shared-lib/user-model/src/parser/user_openai_key.rs

@@ -0,0 +1,16 @@
+use crate::errors::UserErrorCode;
+
+#[derive(Debug)]
+pub struct UserOpenaiKey(pub String);
+
+impl UserOpenaiKey {
+  pub fn parse(s: String) -> Result<UserOpenaiKey, UserErrorCode> {
+    Ok(Self(s))
+  }
+}
+
+impl AsRef<str> for UserOpenaiKey {
+  fn as_ref(&self) -> &str {
+    &self.0
+  }
+}