Merge pull request #1034 from AppFlowy-IO/gotrue-admin-creation
feat: shift admin user creation responsibility to gotrue service
This commit is contained in:
commit
b1ef53435f
|
|
@ -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"
|
|
||||||
}
|
|
||||||
|
|
@ -56,11 +56,14 @@ GOTRUE_SMTP_USER=email_sender@some_company.com
|
||||||
GOTRUE_SMTP_PASS=email_sender_password
|
GOTRUE_SMTP_PASS=email_sender_password
|
||||||
GOTRUE_SMTP_ADMIN_EMAIL=comp_admin@some_company.com
|
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
|
# You can use this user to login to the admin panel
|
||||||
GOTRUE_ADMIN_EMAIL=admin@example.com
|
GOTRUE_ADMIN_EMAIL=admin@example.com
|
||||||
GOTRUE_ADMIN_PASSWORD=password
|
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
|
# 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
|
# 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
|
# If you are using a different domain, you need to change the redirect_uri in the OAuth2 configuration
|
||||||
|
|
|
||||||
3
dev.env
3
dev.env
|
|
@ -39,6 +39,9 @@ GOTRUE_MAILER_TEMPLATES_EMAIL_CHANGE=https://raw.githubusercontent.com/AppFlowy-
|
||||||
GOTRUE_ADMIN_EMAIL=admin@example.com
|
GOTRUE_ADMIN_EMAIL=admin@example.com
|
||||||
GOTRUE_ADMIN_PASSWORD=password
|
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.
|
# 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.
|
# 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.
|
# If you're using an Nginx proxy as part of your setup, this host should be set to the domain managed by the proxy.
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ services:
|
||||||
image: pgvector/pgvector:pg16
|
image: pgvector/pgvector:pg16
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 6
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_USER=${POSTGRES_USER:-postgres}
|
- POSTGRES_USER=${POSTGRES_USER:-postgres}
|
||||||
- POSTGRES_DB=${POSTGRES_DB:-postgres}
|
- POSTGRES_DB=${POSTGRES_DB:-postgres}
|
||||||
|
|
@ -50,14 +55,28 @@ services:
|
||||||
|
|
||||||
gotrue:
|
gotrue:
|
||||||
restart: on-failure
|
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:
|
environment:
|
||||||
# There are a lot of options to configure GoTrue. You can reference the example config:
|
# There are a lot of options to configure GoTrue. You can reference the example config:
|
||||||
# https://github.com/supabase/gotrue/blob/master/example.env
|
# 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
|
- GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application
|
||||||
- URI_ALLOW_LIST=* # adjust restrict if necessary
|
- URI_ALLOW_LIST=* # adjust restrict if necessary
|
||||||
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret
|
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret
|
||||||
- GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP}
|
- 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
|
- GOTRUE_DB_DRIVER=postgres
|
||||||
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
||||||
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
||||||
|
|
@ -122,6 +141,9 @@ services:
|
||||||
FEATURES: ""
|
FEATURES: ""
|
||||||
PROFILE: ci
|
PROFILE: ci
|
||||||
image: appflowyinc/appflowy_cloud:${APPFLOWY_CLOUD_VERSION:-latest}
|
image: appflowyinc/appflowy_cloud:${APPFLOWY_CLOUD_VERSION:-latest}
|
||||||
|
depends_on:
|
||||||
|
gotrue:
|
||||||
|
condition: service_healthy
|
||||||
|
|
||||||
admin_frontend:
|
admin_frontend:
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
|
|
|
||||||
|
|
@ -43,15 +43,21 @@ services:
|
||||||
|
|
||||||
gotrue:
|
gotrue:
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
image: supabase/gotrue:v2.159.1
|
image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest}
|
||||||
depends_on:
|
depends_on:
|
||||||
- postgres
|
- postgres
|
||||||
environment:
|
environment:
|
||||||
# Gotrue config: https://github.com/supabase/gotrue/blob/master/example.env
|
# 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
|
- GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application
|
||||||
- URI_ALLOW_LIST=* # adjust restrict if necessary
|
- URI_ALLOW_LIST=* # adjust restrict if necessary
|
||||||
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret
|
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret
|
||||||
- GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP}
|
- 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
|
- GOTRUE_DB_DRIVER=postgres
|
||||||
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
||||||
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,11 @@ services:
|
||||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
|
||||||
- POSTGRES_HOST=${POSTGRES_HOST:-postgres}
|
- POSTGRES_HOST=${POSTGRES_HOST:-postgres}
|
||||||
- SUPABASE_PASSWORD=${SUPABASE_PASSWORD:-root}
|
- SUPABASE_PASSWORD=${SUPABASE_PASSWORD:-root}
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "${POSTGRES_USER}"]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 6
|
||||||
volumes:
|
volumes:
|
||||||
- ./migrations/before:/docker-entrypoint-initdb.d
|
- ./migrations/before:/docker-entrypoint-initdb.d
|
||||||
- postgres_data:/var/lib/postgresql/data
|
- postgres_data:/var/lib/postgresql/data
|
||||||
|
|
@ -46,14 +51,28 @@ services:
|
||||||
build:
|
build:
|
||||||
context: docker/gotrue
|
context: docker/gotrue
|
||||||
dockerfile: Dockerfile
|
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}
|
image: appflowyinc/gotrue:${GOTRUE_VERSION:-latest}
|
||||||
environment:
|
environment:
|
||||||
# There are a lot of options to configure GoTrue. You can reference the example config:
|
# There are a lot of options to configure GoTrue. You can reference the example config:
|
||||||
# https://github.com/supabase/gotrue/blob/master/example.env
|
# 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
|
- GOTRUE_SITE_URL=appflowy-flutter:// # redirected to AppFlowy application
|
||||||
- URI_ALLOW_LIST=* # adjust restrict if necessary
|
- URI_ALLOW_LIST=* # adjust restrict if necessary
|
||||||
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret
|
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET} # authentication secret
|
||||||
- GOTRUE_JWT_EXP=${GOTRUE_JWT_EXP}
|
- 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
|
- GOTRUE_DB_DRIVER=postgres
|
||||||
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
- API_EXTERNAL_URL=${API_EXTERNAL_URL}
|
||||||
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
- DATABASE_URL=${GOTRUE_DATABASE_URL}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ use openssl::x509::X509;
|
||||||
use secrecy::{ExposeSecret, Secret};
|
use secrecy::{ExposeSecret, Secret};
|
||||||
use sqlx::{postgres::PgPoolOptions, PgPool};
|
use sqlx::{postgres::PgPoolOptions, PgPool};
|
||||||
use tokio::sync::RwLock;
|
use tokio::sync::RwLock;
|
||||||
use tracing::{error, info, warn};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use appflowy_ai_client::client::AppFlowyAIClient;
|
use appflowy_ai_client::client::AppFlowyAIClient;
|
||||||
use appflowy_collaborate::actix_ws::server::RealtimeServerActor;
|
use appflowy_collaborate::actix_ws::server::RealtimeServerActor;
|
||||||
|
|
@ -43,7 +43,6 @@ use appflowy_collaborate::indexer::IndexerProvider;
|
||||||
use appflowy_collaborate::snapshot::SnapshotControl;
|
use appflowy_collaborate::snapshot::SnapshotControl;
|
||||||
use appflowy_collaborate::CollaborationServer;
|
use appflowy_collaborate::CollaborationServer;
|
||||||
use database::file::s3_client_impl::{AwsS3BucketClientImpl, S3BucketStorage};
|
use database::file::s3_client_impl::{AwsS3BucketClientImpl, S3BucketStorage};
|
||||||
use gotrue::grant::{Grant, PasswordGrant};
|
|
||||||
use mailer::sender::Mailer;
|
use mailer::sender::Mailer;
|
||||||
use snowflake::Snowflake;
|
use snowflake::Snowflake;
|
||||||
use tonic_proto::history::history_client::HistoryClient;
|
use tonic_proto::history::history_client::HistoryClient;
|
||||||
|
|
@ -240,7 +239,7 @@ pub async fn init_state(config: &Config, rt_cmd_tx: CLCommandSender) -> Result<A
|
||||||
// Gotrue
|
// Gotrue
|
||||||
info!("Connecting to GoTrue...");
|
info!("Connecting to GoTrue...");
|
||||||
let gotrue_client = get_gotrue_client(&config.gotrue).await?;
|
let gotrue_client = get_gotrue_client(&config.gotrue).await?;
|
||||||
let gotrue_admin = setup_admin_account(gotrue_client.clone(), &pg_pool, &config.gotrue).await?;
|
let gotrue_admin = get_admin_client(gotrue_client.clone(), &config.gotrue);
|
||||||
|
|
||||||
// Redis
|
// Redis
|
||||||
info!("Connecting to Redis...");
|
info!("Connecting to Redis...");
|
||||||
|
|
@ -345,82 +344,17 @@ pub async fn init_state(config: &Config, rt_cmd_tx: CLCommandSender) -> Result<A
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_admin_account(
|
fn get_admin_client(
|
||||||
gotrue_client: gotrue::api::Client,
|
gotrue_client: gotrue::api::Client,
|
||||||
pg_pool: &PgPool,
|
|
||||||
gotrue_setting: &GoTrueSetting,
|
gotrue_setting: &GoTrueSetting,
|
||||||
) -> Result<GoTrueAdmin, Error> {
|
) -> GoTrueAdmin {
|
||||||
let admin_email = gotrue_setting.admin_email.as_str();
|
let admin_email = gotrue_setting.admin_email.as_str();
|
||||||
let password = gotrue_setting.admin_password.expose_secret();
|
let password = gotrue_setting.admin_password.expose_secret();
|
||||||
let gotrue_admin = GoTrueAdmin::new(
|
GoTrueAdmin::new(
|
||||||
admin_email.to_owned(),
|
admin_email.to_owned(),
|
||||||
password.to_owned(),
|
password.to_owned(),
|
||||||
gotrue_client.clone(),
|
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::<uuid::Uuid>()?;
|
|
||||||
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<redis::aio::ConnectionManager, Error> {
|
async fn get_redis_client(redis_uri: &str) -> Result<redis::aio::ConnectionManager, Error> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue