diff --git a/.sqlx/query-884c44d3a87ca4e520f9e8cec6ba673ea4e196920636e4a4db9d42fad3ef4d73.json b/.sqlx/query-884c44d3a87ca4e520f9e8cec6ba673ea4e196920636e4a4db9d42fad3ef4d73.json deleted file mode 100644 index aafc6f23..00000000 --- a/.sqlx/query-884c44d3a87ca4e520f9e8cec6ba673ea4e196920636e4a4db9d42fad3ef4d73.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "\n UPDATE auth.users\n SET role = 'supabase_admin', email_confirmed_at = NOW()\n WHERE id = $1\n ", - "describe": { - "columns": [], - "parameters": { - "Left": [ - "Uuid" - ] - }, - "nullable": [] - }, - "hash": "884c44d3a87ca4e520f9e8cec6ba673ea4e196920636e4a4db9d42fad3ef4d73" -} diff --git a/deploy.env b/deploy.env index afae1ebf..5b4a4f47 100644 --- a/deploy.env +++ b/deploy.env @@ -56,11 +56,14 @@ GOTRUE_SMTP_USER=email_sender@some_company.com GOTRUE_SMTP_PASS=email_sender_password GOTRUE_SMTP_ADMIN_EMAIL=comp_admin@some_company.com -# This user will be created when AppFlowy Cloud starts successfully +# This user will be created when GoTrue starts successfully # You can use this user to login to the admin panel GOTRUE_ADMIN_EMAIL=admin@example.com GOTRUE_ADMIN_PASSWORD=password +# Set this to true if users can only join by invite +GOTRUE_DISABLE_SIGNUP=false + # User will be redirected to this after Email or OAuth login # Change this to your own domain where you host the docker-compose or gotrue # If you are using a different domain, you need to change the redirect_uri in the OAuth2 configuration diff --git a/dev.env b/dev.env index 57c52fcf..15002a98 100644 --- a/dev.env +++ b/dev.env @@ -39,6 +39,9 @@ GOTRUE_MAILER_TEMPLATES_EMAIL_CHANGE=https://raw.githubusercontent.com/AppFlowy- GOTRUE_ADMIN_EMAIL=admin@example.com GOTRUE_ADMIN_PASSWORD=password +# Set this to true if users can only join by invite +GOTRUE_DISABLE_SIGNUP=false + # The email verification link provided to users will redirect them to this specified host. # You should update this setting to reflect the domain where you are hosting your application with docker-compose or gotrue. # If you're using an Nginx proxy as part of your setup, this host should be set to the domain managed by the proxy. diff --git a/docker-compose-ci.yml b/docker-compose-ci.yml index b56851b9..c9f1c5cb 100644 --- a/docker-compose-ci.yml +++ b/docker-compose-ci.yml @@ -33,6 +33,11 @@ services: image: pgvector/pgvector:pg16 ports: - "5432:5432" + healthcheck: + test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}"] + interval: 5s + timeout: 5s + retries: 6 environment: - POSTGRES_USER=${POSTGRES_USER:-postgres} - POSTGRES_DB=${POSTGRES_DB:-postgres} @@ -50,14 +55,28 @@ services: gotrue: restart: on-failure - image: supabase/gotrue:v2.159.1 + image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest} + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "nc", "-z", "localhost", "9999"] + interval: 5s + timeout: 5s + retries: 6 environment: # There are a lot of options to configure GoTrue. You can reference the example config: # https://github.com/supabase/gotrue/blob/master/example.env + - GOTRUE_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL} + - GOTRUE_ADMIN_PASSWORD=${GOTRUE_ADMIN_PASSWORD} + - GOTRUE_DISABLE_SIGNUP=${GOTRUE_DISABLE_SIGNUP:-false} - GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application - URI_ALLOW_LIST=* # adjust restrict if necessary - GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret - GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP} + # Without this environment variable, the createuser command will create an admin + # with the `admin` role as opposed to `supabase_admin` + - GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin - GOTRUE_DB_DRIVER=postgres - API_EXTERNAL_URL=${API_EXTERNAL_URL} - DATABASE_URL=${GOTRUE_DATABASE_URL} @@ -122,6 +141,9 @@ services: FEATURES: "" PROFILE: ci image: appflowyinc/appflowy_cloud:${APPFLOWY_CLOUD_VERSION:-latest} + depends_on: + gotrue: + condition: service_healthy admin_frontend: restart: on-failure diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 1f314aae..4f6cbcd9 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -43,15 +43,21 @@ services: gotrue: restart: on-failure - image: supabase/gotrue:v2.159.1 + image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest} depends_on: - postgres environment: # Gotrue config: https://github.com/supabase/gotrue/blob/master/example.env + - GOTRUE_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL} + - GOTRUE_ADMIN_PASSWORD=${GOTRUE_ADMIN_PASSWORD} + - GOTRUE_DISABLE_SIGNUP=${GOTRUE_DISABLE_SIGNUP:-false} - GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application - URI_ALLOW_LIST=* # adjust restrict if necessary - GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret - GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP} + # Without this environment variable, the createuser command will create an admin + # with the `admin` role as opposed to `supabase_admin` + - GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin - GOTRUE_DB_DRIVER=postgres - API_EXTERNAL_URL=${API_EXTERNAL_URL} - DATABASE_URL=${GOTRUE_DATABASE_URL} diff --git a/docker-compose.yml b/docker-compose.yml index b39056cd..23bf7182 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,6 +33,11 @@ services: - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} - POSTGRES_HOST=${POSTGRES_HOST:-postgres} - SUPABASE_PASSWORD=${SUPABASE_PASSWORD:-root} + healthcheck: + test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}"] + interval: 5s + timeout: 5s + retries: 6 volumes: - ./migrations/before:/docker-entrypoint-initdb.d - postgres_data:/var/lib/postgresql/data @@ -46,14 +51,28 @@ services: build: context: docker/gotrue dockerfile: Dockerfile + depends_on: + postgres: + condition: service_healthy + healthcheck: + test: ["CMD", "nc", "-z", "localhost", "9999"] + interval: 5s + timeout: 5s + retries: 6 image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest} environment: # There are a lot of options to configure GoTrue. You can reference the example config: # https://github.com/supabase/gotrue/blob/master/example.env + - GOTRUE_ADMIN_EMAIL=${GOTRUE_ADMIN_EMAIL} + - GOTRUE_ADMIN_PASSWORD=${GOTRUE_ADMIN_PASSWORD} + - GOTRUE_DISABLE_SIGNUP=${GOTRUE_DISABLE_SIGNUP:-false} - GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application - URI_ALLOW_LIST=* # adjust restrict if necessary - GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret - GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP} + # Without this environment variable, the createuser command will create an admin + # with the `admin` role as opposed to `supabase_admin` + - GOTRUE_JWT_ADMIN_GROUP_NAME=supabase_admin - GOTRUE_DB_DRIVER=postgres - API_EXTERNAL_URL=${API_EXTERNAL_URL} - DATABASE_URL=${GOTRUE_DATABASE_URL} diff --git a/src/application.rs b/src/application.rs index a85fd5f6..be9a2e62 100644 --- a/src/application.rs +++ b/src/application.rs @@ -32,7 +32,7 @@ use openssl::x509::X509; use secrecy::{ExposeSecret, Secret}; use sqlx::{postgres::PgPoolOptions, PgPool}; use tokio::sync::RwLock; -use tracing::{error, info, warn}; +use tracing::{error, info}; use appflowy_ai_client::client::AppFlowyAIClient; use appflowy_collaborate::actix_ws::server::RealtimeServerActor; @@ -43,7 +43,6 @@ use appflowy_collaborate::indexer::IndexerProvider; use appflowy_collaborate::snapshot::SnapshotControl; use appflowy_collaborate::CollaborationServer; use database::file::s3_client_impl::{AwsS3BucketClientImpl, S3BucketStorage}; -use gotrue::grant::{Grant, PasswordGrant}; use mailer::sender::Mailer; use snowflake::Snowflake; use tonic_proto::history::history_client::HistoryClient; @@ -240,7 +239,7 @@ pub async fn init_state(config: &Config, rt_cmd_tx: CLCommandSender) -> Result Result Result { +) -> GoTrueAdmin { let admin_email = gotrue_setting.admin_email.as_str(); let password = gotrue_setting.admin_password.expose_secret(); - let gotrue_admin = GoTrueAdmin::new( + GoTrueAdmin::new( admin_email.to_owned(), password.to_owned(), gotrue_client.clone(), - ); - - match gotrue_client - .token(&Grant::Password(PasswordGrant { - email: admin_email.to_owned(), - password: password.clone(), - })) - .await - { - Ok(_token) => return Ok(gotrue_admin), - Err(err) => tracing::warn!("Failed to get token: {:?}", err), - }; - - let res_resp = gotrue_client.sign_up(admin_email, password, None).await; - match res_resp { - Err(err) => { - if let app_error::gotrue::GoTrueError::Internal(err) = err { - match (err.code, err.msg.as_str()) { - (400..=499, "User already registered") => { - info!("Admin user already registered"); - Ok(gotrue_admin) - }, - _ => Err(err.into()), - } - } else { - Err(err.into()) - } - }, - Ok(resp) => { - let admin_user = { - match resp { - gotrue_entity::dto::SignUpResponse::Authenticated(resp) => resp.user, - gotrue_entity::dto::SignUpResponse::NotAuthenticated(user) => user, - } - }; - match admin_user.role.as_str() { - "supabase_admin" => { - info!("Admin user already created and set role to supabase_admin"); - Ok(gotrue_admin) - }, - _ => { - let user_id = admin_user.id.parse::()?; - let result = sqlx::query!( - r#" - UPDATE auth.users - SET role = 'supabase_admin', email_confirmed_at = NOW() - WHERE id = $1 - "#, - user_id, - ) - .execute(pg_pool) - .await - .context("failed to update the admin user")?; - - if result.rows_affected() != 1 { - warn!("Failed to update the admin user"); - } else { - info!("Admin user created and set role to supabase_admin"); - } - - Ok(gotrue_admin) - }, - } - }, - } + ) } async fn get_redis_client(redis_uri: &str) -> Result {