diff --git a/admin_frontend/README.md b/admin_frontend/README.md index ce26934f..a2b8c75c 100644 --- a/admin_frontend/README.md +++ b/admin_frontend/README.md @@ -1,6 +1,7 @@ # Admin Frontend ## Partial Local Environment + - Go to source root folder of `AppFlowy-Cloud` - Start running locally dependency servers: `docker compose --file docker-compose-dev.yml up -d` - Start SQLX migrations `cargo sqlx database create && cargo sqlx migrate run && cargo sqlx prepare --workspace` @@ -9,6 +10,7 @@ - Run `cargo watch -x run -w .`, this watch for source changes, rebuild and rerun the app. ## Full Local Integration Environment + - Start the whole stack: `docker compose up -d` - Go to [web server](localhost) - After editing source files, do `docker compose up -d --no-deps --build admin_frontend` diff --git a/admin_frontend/assets/discord/README.md b/admin_frontend/assets/discord/README.md index e8208fc9..bea6203a 100644 --- a/admin_frontend/assets/discord/README.md +++ b/admin_frontend/assets/discord/README.md @@ -1,2 +1,3 @@ # Discord + - Assets are derived from: https://discord.com/branding diff --git a/admin_frontend/assets/esim_turkey.png b/admin_frontend/assets/esim_turkey.png new file mode 100644 index 00000000..eefac59a Binary files /dev/null and b/admin_frontend/assets/esim_turkey.png differ diff --git a/admin_frontend/assets/github/README.md b/admin_frontend/assets/github/README.md index 4581f5d9..6136987f 100644 --- a/admin_frontend/assets/github/README.md +++ b/admin_frontend/assets/github/README.md @@ -1,2 +1,3 @@ # Github + - Assets derived from: https://github.com/logos diff --git a/admin_frontend/assets/google/README.md b/admin_frontend/assets/google/README.md index c2bc3e46..1ed35d3e 100644 --- a/admin_frontend/assets/google/README.md +++ b/admin_frontend/assets/google/README.md @@ -1,2 +1,3 @@ # Google OAuth Sign Logo + Assets in this directory are generated from: https://developers.google.com/identity/branding-guidelines diff --git a/admin_frontend/src/templates.rs b/admin_frontend/src/templates.rs index eba560c4..745a9126 100644 --- a/admin_frontend/src/templates.rs +++ b/admin_frontend/src/templates.rs @@ -7,7 +7,9 @@ pub struct ChangePassword; #[derive(Template)] #[template(path = "pages/login.html")] -pub struct Login; +pub struct Login<'a> { + pub oauth_providers: Vec<&'a str>, +} // #[derive(Template)] // #[template(path = "login.html")] diff --git a/admin_frontend/src/web_app.rs b/admin_frontend/src/web_app.rs index 6c4b819c..c41b787a 100644 --- a/admin_frontend/src/web_app.rs +++ b/admin_frontend/src/web_app.rs @@ -64,8 +64,10 @@ pub async fn user_user_handler( render_template(templates::UserDetails { user: &user }) } -pub async fn login_handler() -> Result, WebAppError> { - render_template(templates::Login {}) +pub async fn login_handler(State(state): State) -> Result, WebAppError> { + let external = state.gotrue_client.settings().await?.external; + let oauth_providers = external.oauth_providers(); + render_template(templates::Login { oauth_providers }) } pub async fn user_change_password_handler() -> Result, WebAppError> { diff --git a/admin_frontend/templates/components/invite.html b/admin_frontend/templates/components/invite.html index 34af82c2..6a41bd79 100644 --- a/admin_frontend/templates/components/invite.html +++ b/admin_frontend/templates/components/invite.html @@ -16,9 +16,7 @@ - + diff --git a/admin_frontend/templates/layouts/base.html b/admin_frontend/templates/layouts/base.html index 51d566fc..92b43e8e 100644 --- a/admin_frontend/templates/layouts/base.html +++ b/admin_frontend/templates/layouts/base.html @@ -24,15 +24,15 @@ -{% endblock %} + + {% endblock %} + diff --git a/libs/client-api/src/http.rs b/libs/client-api/src/http.rs index 175bbc81..47f63b4a 100644 --- a/libs/client-api/src/http.rs +++ b/libs/client-api/src/http.rs @@ -44,7 +44,7 @@ use url::Url; use crate::retry::{RefreshTokenAction, RefreshTokenRetryCondition}; use crate::ws::{WSClientHttpSender, WSError}; use gotrue_entity::dto::SignUpResponse::{Authenticated, NotAuthenticated}; -use gotrue_entity::dto::{GotrueTokenResponse, OAuthProvider, UpdateGotrueUserParams, User}; +use gotrue_entity::dto::{GotrueTokenResponse, AuthProvider, UpdateGotrueUserParams, User}; use realtime_entity::realtime_proto::HttpRealtimeMessage; /// `Client` is responsible for managing communication with the GoTrue API and cloud storage. @@ -210,7 +210,7 @@ impl Client { #[instrument(level = "debug", skip_all, err)] pub async fn generate_oauth_url_with_provider( &self, - provider: &OAuthProvider, + provider: &AuthProvider, ) -> Result { let settings = self.gotrue_client.settings().await?; if !settings.external.has_provider(provider) { @@ -225,7 +225,7 @@ impl Client { .append_pair("provider", provider.as_str()) .append_pair("redirect_to", DESKTOP_CALLBACK_URL); - if let OAuthProvider::Google = provider { + if let AuthProvider::Google = provider { url .query_pairs_mut() // In many cases, especially for server-side applications or mobile apps that might need to diff --git a/libs/gotrue-entity/src/dto.rs b/libs/gotrue-entity/src/dto.rs index 422347bb..5bebbd74 100644 --- a/libs/gotrue-entity/src/dto.rs +++ b/libs/gotrue-entity/src/dto.rs @@ -103,16 +103,30 @@ pub struct GoTrueSettings { pub struct GoTrueOAuthProviderSettings(BTreeMap); impl GoTrueOAuthProviderSettings { - pub fn has_provider(&self, p: &OAuthProvider) -> bool { + pub fn has_provider(&self, p: &AuthProvider) -> bool { let a = self.0.get(p.as_str()); match a { Some(v) => *v, None => false, } } + + pub fn oauth_providers(&self) -> Vec<&str> { + self + .0 + .iter() + .filter(|&(key, &value)| value && key != "email" && key != "phone") + .map(|(key, _value)| key.as_str()) + .collect() + } } -pub enum OAuthProvider { +pub enum AuthProvider { + // Non-OAuth providers + Email, + Phone, + + // OAuth providers Apple, Azure, Bitbucket, @@ -131,63 +145,61 @@ pub enum OAuthProvider { Workos, Twitch, Twitter, - Email, - Phone, Zoom, } -impl OAuthProvider { +impl AuthProvider { pub fn as_str(&self) -> &str { match self { - OAuthProvider::Apple => "apple", - OAuthProvider::Azure => "azure", - OAuthProvider::Bitbucket => "bitbucket", - OAuthProvider::Discord => "discord", - OAuthProvider::Facebook => "facebook", - OAuthProvider::Figma => "figma", - OAuthProvider::Github => "github", - OAuthProvider::Gitlab => "gitlab", - OAuthProvider::Google => "google", - OAuthProvider::Keycloak => "keycloak", - OAuthProvider::Kakao => "kakao", - OAuthProvider::Linkedin => "linkedin", - OAuthProvider::Notion => "notion", - OAuthProvider::Spotify => "spotify", - OAuthProvider::Slack => "slack", - OAuthProvider::Workos => "workos", - OAuthProvider::Twitch => "twitch", - OAuthProvider::Twitter => "twitter", - OAuthProvider::Email => "email", - OAuthProvider::Phone => "phone", - OAuthProvider::Zoom => "zoom", + AuthProvider::Apple => "apple", + AuthProvider::Azure => "azure", + AuthProvider::Bitbucket => "bitbucket", + AuthProvider::Discord => "discord", + AuthProvider::Facebook => "facebook", + AuthProvider::Figma => "figma", + AuthProvider::Github => "github", + AuthProvider::Gitlab => "gitlab", + AuthProvider::Google => "google", + AuthProvider::Keycloak => "keycloak", + AuthProvider::Kakao => "kakao", + AuthProvider::Linkedin => "linkedin", + AuthProvider::Notion => "notion", + AuthProvider::Spotify => "spotify", + AuthProvider::Slack => "slack", + AuthProvider::Workos => "workos", + AuthProvider::Twitch => "twitch", + AuthProvider::Twitter => "twitter", + AuthProvider::Email => "email", + AuthProvider::Phone => "phone", + AuthProvider::Zoom => "zoom", } } } -impl OAuthProvider { - pub fn from>(value: A) -> Option { +impl AuthProvider { + pub fn from>(value: A) -> Option { match value.as_ref() { - "apple" => Some(OAuthProvider::Apple), - "azure" => Some(OAuthProvider::Azure), - "bitbucket" => Some(OAuthProvider::Bitbucket), - "discord" => Some(OAuthProvider::Discord), - "facebook" => Some(OAuthProvider::Facebook), - "figma" => Some(OAuthProvider::Figma), - "github" => Some(OAuthProvider::Github), - "gitlab" => Some(OAuthProvider::Gitlab), - "google" => Some(OAuthProvider::Google), - "keycloak" => Some(OAuthProvider::Keycloak), - "kakao" => Some(OAuthProvider::Kakao), - "linkedin" => Some(OAuthProvider::Linkedin), - "notion" => Some(OAuthProvider::Notion), - "spotify" => Some(OAuthProvider::Spotify), - "slack" => Some(OAuthProvider::Slack), - "workos" => Some(OAuthProvider::Workos), - "twitch" => Some(OAuthProvider::Twitch), - "twitter" => Some(OAuthProvider::Twitter), - "email" => Some(OAuthProvider::Email), - "phone" => Some(OAuthProvider::Phone), - "zoom" => Some(OAuthProvider::Zoom), + "apple" => Some(AuthProvider::Apple), + "azure" => Some(AuthProvider::Azure), + "bitbucket" => Some(AuthProvider::Bitbucket), + "discord" => Some(AuthProvider::Discord), + "facebook" => Some(AuthProvider::Facebook), + "figma" => Some(AuthProvider::Figma), + "github" => Some(AuthProvider::Github), + "gitlab" => Some(AuthProvider::Gitlab), + "google" => Some(AuthProvider::Google), + "keycloak" => Some(AuthProvider::Keycloak), + "kakao" => Some(AuthProvider::Kakao), + "linkedin" => Some(AuthProvider::Linkedin), + "notion" => Some(AuthProvider::Notion), + "spotify" => Some(AuthProvider::Spotify), + "slack" => Some(AuthProvider::Slack), + "workos" => Some(AuthProvider::Workos), + "twitch" => Some(AuthProvider::Twitch), + "twitter" => Some(AuthProvider::Twitter), + "email" => Some(AuthProvider::Email), + "phone" => Some(AuthProvider::Phone), + "zoom" => Some(AuthProvider::Zoom), _ => None, } } diff --git a/libs/gotrue/src/api.rs b/libs/gotrue/src/api.rs index 7671cb09..7aae8a39 100644 --- a/libs/gotrue/src/api.rs +++ b/libs/gotrue/src/api.rs @@ -4,7 +4,7 @@ use crate::params::{ }; use anyhow::Context; use gotrue_entity::dto::{ - AdminListUsersResponse, GoTrueSettings, GotrueTokenResponse, OAuthProvider, SignUpResponse, + AdminListUsersResponse, AuthProvider, GoTrueSettings, GotrueTokenResponse, SignUpResponse, UpdateGotrueUserParams, User, }; use gotrue_entity::error::{GoTrueError, GoTrueErrorSerde, GotrueClientError}; @@ -24,7 +24,7 @@ impl Client { } } - pub fn oauth_url(&self, provider: &OAuthProvider) -> String { + pub fn oauth_url(&self, provider: &AuthProvider) -> String { format!("{}/authorize?provider={}", self.base_url, provider.as_str()) } diff --git a/tests/user/sign_up.rs b/tests/user/sign_up.rs index a822db18..573c6fab 100644 --- a/tests/user/sign_up.rs +++ b/tests/user/sign_up.rs @@ -1,5 +1,5 @@ use app_error::ErrorCode; -use gotrue_entity::dto::OAuthProvider; +use gotrue_entity::dto::AuthProvider; use crate::{ localhost_client, test_appflowy_cloud_client, @@ -52,7 +52,7 @@ async fn sign_up_but_existing_user() { async fn sign_up_oauth_not_available() { let c = localhost_client(); let err = c - .generate_oauth_url_with_provider(&OAuthProvider::Zoom) + .generate_oauth_url_with_provider(&AuthProvider::Zoom) .await .err() .unwrap(); @@ -68,14 +68,14 @@ async fn sign_up_oauth_not_available() { async fn sign_up_with_google_oauth() { let c = localhost_client(); let url = c - .generate_oauth_url_with_provider(&OAuthProvider::Google) + .generate_oauth_url_with_provider(&AuthProvider::Google) .await .unwrap(); assert!(!url.is_empty()); let c = test_appflowy_cloud_client(); let url = c - .generate_oauth_url_with_provider(&OAuthProvider::Google) + .generate_oauth_url_with_provider(&AuthProvider::Google) .await .unwrap(); assert!(!url.is_empty());