feat: ci: dockerize gotrue auth server (#13)

* feat: ci: dockerize gotrue auth server

* feat: build: add gotrue to docker-compose

* fix: build: docker-compose + test

* feat: ci: simplify tests

* feat: ci: add back needed services for compilation

* fix: ci: github secrets

* fix: ci: use signup

* fix: ci: add export

* fix: test: generate unique email for registration to avoid conflict
This commit is contained in:
Zack 2023-09-06 14:12:39 +08:00 committed by GitHub
parent c8ae099e90
commit b28d5dda4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 143 additions and 71 deletions

View File

@ -11,8 +11,9 @@ env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
SQLX_VERSION: 0.6.2 SQLX_VERSION: 0.6.2
SQLX_FEATURES: "rustls,postgres" SQLX_FEATURES: "rustls,postgres"
APP__GOTRUE__BASE_URL: ${{ secrets.APP__GOTRUE__BASE_URL }} APP__GOTRUE__BASE_URL: http://localhost:3000
APP__GOTRUE__JWT_SECRET: ${{ secrets.APP__GOTRUE__JWT_SECRET }} APP__GOTRUE__JWT_SECRET: hello123
GOTRUE_SMTP_PASS: ${{ secrets.GOTRUE_SMTP_PASS }}
jobs: jobs:
test: test:
@ -31,6 +32,7 @@ jobs:
image: redis:7 image: redis:7
ports: ports:
- 6379:6379 - 6379:6379
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
@ -52,11 +54,21 @@ jobs:
- name: Check sqlx-data.json is up-to-date - name: Check sqlx-data.json is up-to-date
run: | run: |
cargo sqlx prepare --check -- --bin appflowy_cloud cargo sqlx prepare --check -- --bin appflowy_cloud
- name: Run Docker-Compose
run: |
docker-compose up -d
- name: Add registered user
run: |
export GOTRUE_MAILER_AUTOCONFIRM=true
docker-compose up -d
curl localhost:9998/signup \
--data-raw '{"email":"xigahi8979@tipent.com","password":"Hello123!"}' \
--header 'Content-Type: application/json'
export GOTRUE_MAILER_AUTOCONFIRM=false
docker-compose up -d
- name: Run tests - name: Run tests
run: | run: |
cargo build
cargo run &
sleep 5 # wait some time for server to be ready
cargo test cargo test

View File

@ -1,4 +1,5 @@
FROM lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef FROM lukemathwalker/cargo-chef:latest-rust-1.69.0 as chef
WORKDIR /app WORKDIR /app
RUN apt update && apt install lld clang -y RUN apt update && apt install lld clang -y

7
build/init.sql Normal file
View File

@ -0,0 +1,7 @@
CREATE USER supabase_admin LOGIN CREATEROLE CREATEDB REPLICATION BYPASSRLS;
-- Supabase super admin
CREATE USER supabase_auth_admin NOINHERIT CREATEROLE LOGIN NOREPLICATION PASSWORD 'root';
CREATE SCHEMA IF NOT EXISTS auth AUTHORIZATION supabase_auth_admin;
GRANT CREATE ON DATABASE postgres TO supabase_auth_admin;
ALTER USER supabase_auth_admin SET search_path = 'auth';

19
build/init_gotrue.sh Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env bash
cd "$(dirname "$0")"
set -e
git clone https://github.com/supabase/gotrue.git
cp gotrue.env.docker gotrue/.env.docker
cd gotrue
# avoid port conflict with host postgres
sed -i "s/'5432:5432'/'5433:5432'/" docker-compose-dev.yml
make dev &
while true; do
curl localhost:9999/health && break
echo "waiting for gotrue to be ready..."
sleep 1
done

View File

@ -9,8 +9,11 @@ database:
port: 5432 port: 5432
username: "postgres" username: "postgres"
password: "password" password: "password"
database_name: "appflowy" database_name: "postgres"
websocket: websocket:
heartbeat_interval: 8 heartbeat_interval: 8
client_timeout: 10 client_timeout: 10
redis_uri: "redis://127.0.0.1:6379" redis_uri: "redis://127.0.0.1:6379"
gotrue:
base_url: "http://gotrue:9999"
jwt_secret: "hello123"

View File

@ -1,18 +1,46 @@
version: '3' version: '3'
services: services:
postgres: postgres:
image: postgres:14 image: postgres
environment: environment:
- POSTGRES_USER=${POSTGRES_USER:-postgres} - POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-postgres} - POSTGRES_DB=${POSTGRES_DB:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
- POSTGRES_HOST=${POSTGRES_HOST:-postgres} - POSTGRES_HOST=${POSTGRES_HOST:-postgres}
ports: ports:
- "5433:5432" - 5433:5432
volumes:
- ./build/init.sql:/docker-entrypoint-initdb.d/init.sql
redis: redis:
image: redis:7 image: redis
ports: ports:
- "6380:6379" - 6380:6379
gotrue:
image: supabase/gotrue
restart: on-failure
depends_on:
- postgres
environment:
- GOTRUE_SITE_URL=http://localhost:3000 # redirected site
- GOTRUE_JWT_SECRET=${GOTRUE_JWT_SECRET:-hello123}
- GOTRUE_DB_DRIVER=postgres
- DATABASE_URL=postgres://supabase_auth_admin:root@postgres:5432/postgres
- API_EXTERNAL_URL=http://localhost:9998 # verify site
- PORT=9999
- GOTRUE_SMTP_HOST=smtp.gmail.com
- GOTRUE_SMTP_PORT=465
- GOTRUE_SMTP_USER=noreply@appflowy.io
- GOTRUE_SMTP_PASS=${GOTRUE_SMTP_PASS:-}
- GOTRUE_SMTP_ADMIN_EMAIL=internal@appflowy.io
- GOTRUE_RATE_LIMIT_EMAIL_SENT=${GOTRUE_RATE_LIMIT_EMAIL_SENT:-100000}
- GOTRUE_SMTP_MAX_FREQUENCY=1ns
- GOTRUE_MAILER_URLPATHS_CONFIRMATION=/verify
- GOTRUE_MAILER_AUTOCONFIRM=${GOTRUE_MAILER_AUTOCONFIRM:-false}
ports:
- 9998:9999
appflowy_cloud: appflowy_cloud:
restart: on-failure restart: on-failure
environment: environment:
@ -24,5 +52,6 @@ services:
depends_on: depends_on:
- redis - redis
- postgres - postgres
- gotrue
ports: ports:
- 8000:8000 - 8000:8000

View File

@ -69,7 +69,7 @@ pub struct AccessTokenResponse {
pub access_token: String, pub access_token: String,
pub token_type: String, pub token_type: String,
pub expires_in: i64, pub expires_in: i64,
pub expires_at: i64, pub expires_at: Option<i64>, // older versions of GoTrue do not return this
pub refresh_token: String, pub refresh_token: String,
pub user: User, pub user: User,
pub provider_access_token: Option<String>, pub provider_access_token: Option<String>,

View File

@ -3,3 +3,4 @@ mod sign_in;
mod sign_out; mod sign_out;
mod sign_up; mod sign_up;
mod update; mod update;
mod utils;

View File

@ -1,13 +1,16 @@
use appflowy_cloud::client::http::Client; use appflowy_cloud::client::http::Client;
use crate::client::constants::LOCALHOST_URL; use crate::client::{
constants::LOCALHOST_URL,
utils::{generate_unique_email, REGISTERED_EMAIL, REGISTERED_PASSWORD},
};
#[tokio::test] #[tokio::test]
async fn sign_in_unknown_user() { async fn sign_in_unknown_user() {
let email = generate_unique_email();
let password = "Hello123!";
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let resp = c let resp = c.sign_in_password(&email, password).await;
.sign_in_password("unknown999@appflowy.io", "Hello123!")
.await;
let resp = resp.unwrap(); let resp = resp.unwrap();
match resp { match resp {
Ok(()) => panic!("should not be ok"), Ok(()) => panic!("should not be ok"),
@ -22,16 +25,14 @@ async fn sign_in_unknown_user() {
async fn sign_in_wrong_password() { async fn sign_in_wrong_password() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let email = generate_unique_email();
let password = "Hello123!";
{ {
let _ = c let _ = c.sign_up(&email, password).await.unwrap();
.sign_up("unknown123@appflowy.io", "Hello123!")
.await
.unwrap();
} }
let resp = c let wrong_password = "Hllo123!";
.sign_in_password("unknown123@appflowy.io", "Hllo123!") let resp = c.sign_in_password(&email, wrong_password).await;
.await;
let resp = resp.unwrap(); let resp = resp.unwrap();
match resp { match resp {
Ok(()) => panic!("should not be ok"), Ok(()) => panic!("should not be ok"),
@ -46,16 +47,13 @@ async fn sign_in_wrong_password() {
async fn sign_in_unconfirmed_email() { async fn sign_in_unconfirmed_email() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let email = generate_unique_email();
let password = "Hello123!";
{ {
let _ = c let _ = c.sign_up(&email, password).await.unwrap();
.sign_up("unknown123@appflowy.io", "Hello123!")
.await
.unwrap();
} }
let resp = c let resp = c.sign_in_password(&email, password).await;
.sign_in_password("unknown123@appflowy.io", "Hello123!")
.await;
let resp = resp.unwrap(); let resp = resp.unwrap();
match resp { match resp {
Ok(()) => panic!("should not be ok"), Ok(()) => panic!("should not be ok"),
@ -70,10 +68,9 @@ async fn sign_in_unconfirmed_email() {
async fn sign_in_success() { async fn sign_in_success() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
// pre-registered and confirmed email let resp = c
let email = "xigahi8979@tipent.com"; .sign_in_password(REGISTERED_EMAIL, REGISTERED_PASSWORD)
.await;
let resp = c.sign_in_password(email, "Hello123!").await;
let resp = resp.unwrap(); let resp = resp.unwrap();
match resp { match resp {
Ok(()) => {}, Ok(()) => {},

View File

@ -1,6 +1,9 @@
use appflowy_cloud::client::http::Client; use appflowy_cloud::client::http::Client;
use crate::client::constants::LOCALHOST_URL; use crate::client::{
constants::LOCALHOST_URL,
utils::{REGISTERED_EMAIL, REGISTERED_PASSWORD},
};
#[tokio::test] #[tokio::test]
async fn sign_out_but_not_sign_in() { async fn sign_out_but_not_sign_in() {
@ -13,9 +16,7 @@ async fn sign_out_but_not_sign_in() {
async fn sign_out_after_sign_in() { async fn sign_out_after_sign_in() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
// pre-registered and confirmed email c.sign_in_password(REGISTERED_EMAIL, REGISTERED_PASSWORD)
let email = "xigahi8979@tipent.com";
c.sign_in_password(email, "Hello123!")
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();

View File

@ -1,27 +1,30 @@
use crate::client::constants::LOCALHOST_URL; use crate::client::{constants::LOCALHOST_URL, utils::generate_unique_email};
use appflowy_cloud::client::http::Client; use appflowy_cloud::client::http::Client;
#[tokio::test] #[tokio::test]
async fn sign_up_success() { async fn sign_up_success() {
let email = generate_unique_email();
let password = "Hello!123#";
let c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let resp = c let resp = c.sign_up(&email, password).await.unwrap();
.sign_up("exampleuser@appflowy.io", "Hello!123#")
.await
.unwrap();
assert!(resp.confirmation_sent_at.is_some()); assert!(resp.confirmation_sent_at.is_some());
assert!(resp.confirmed_at.is_none()); assert!(resp.confirmed_at.is_none());
} }
#[tokio::test] #[tokio::test]
async fn sign_up_invalid_email() { async fn sign_up_invalid_email() {
let invalid_email = "not_email_address";
let password = "Hello!123#";
let c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let resp = c.sign_up("exampleuser", "Hello!123#").await; let resp = c.sign_up(invalid_email, password).await;
assert!(resp.is_err()); assert!(resp.is_err());
} }
#[tokio::test] #[tokio::test]
async fn sign_up_invalid_password() { async fn sign_up_invalid_password() {
let email = generate_unique_email();
let password = "123";
let c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let resp = c.sign_up("exampleuser@appflowy.io", "123").await; let resp = c.sign_up(&email, password).await;
assert!(resp.is_err()); assert!(resp.is_err());
} }

View File

@ -1,46 +1,52 @@
use appflowy_cloud::client::http::Client; use appflowy_cloud::client::http::Client;
use crate::client::constants::LOCALHOST_URL; use crate::client::{
constants::LOCALHOST_URL,
utils::{generate_unique_email, REGISTERED_EMAIL, REGISTERED_PASSWORD},
};
#[tokio::test] #[tokio::test]
async fn update_but_not_logged_in() { async fn update_but_not_logged_in() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let res = c.update("new_email_182@somemail.com", "Hello123!!").await; let new_email = generate_unique_email();
let new_password = "Hello123!";
let res = c.update(&new_email, new_password).await;
assert!(res.is_err()); assert!(res.is_err());
} }
#[tokio::test] #[tokio::test]
async fn update_password_same_password() { async fn update_password_same_password() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let email = "xigahi8979@tipent.com"; c.sign_in_password(REGISTERED_EMAIL, REGISTERED_PASSWORD)
let password = "Hello123!"; .await
c.sign_in_password(email, password).await.unwrap().unwrap(); .unwrap()
.unwrap();
let res = c.update(email, "Hello123!").await; c.update(REGISTERED_EMAIL, REGISTERED_PASSWORD)
assert!(res.is_err()); .await
.unwrap();
} }
#[tokio::test] #[tokio::test]
async fn update_password_and_revert() { async fn update_password_and_revert() {
let email = "xigahi8979@tipent.com";
let old_password = "Hello123!";
let new_password = "Hello456!"; let new_password = "Hello456!";
{ {
// change password to new_password // change password to new_password
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
c.sign_in_password(email, old_password) c.sign_in_password(REGISTERED_EMAIL, REGISTERED_PASSWORD)
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
c.update(email, new_password).await.unwrap(); c.update(REGISTERED_EMAIL, new_password).await.unwrap();
} }
{ {
// revert password to old_password // revert password to old_password
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL); let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
c.sign_in_password(email, new_password) c.sign_in_password(REGISTERED_EMAIL, new_password)
.await .await
.unwrap() .unwrap()
.unwrap(); .unwrap();
c.update(email, old_password).await.unwrap(); c.update(REGISTERED_EMAIL, REGISTERED_PASSWORD)
.await
.unwrap();
} }
} }

View File

@ -1,8 +1,7 @@
use std::time::SystemTime; use std::time::SystemTime;
use appflowy_cloud::client::http::Client; pub const REGISTERED_EMAIL: &str = "xigahi8979@tipent.com";
pub const REGISTERED_PASSWORD: &str = "Hello123!";
pub const LOCALHOST_URL: &str = "http://localhost:8000"; //TODO: change to default port, use https
pub fn timestamp_nano() -> u128 { pub fn timestamp_nano() -> u128 {
SystemTime::now() SystemTime::now()
@ -11,12 +10,6 @@ pub fn timestamp_nano() -> u128 {
.as_nanos() .as_nanos()
} }
pub async fn register_deep_fake(c: &mut Client) -> (String, String, String) { pub fn generate_unique_email() -> String {
let email = format!("deep_fake{}@appflowy.io", timestamp_nano()); format!("{}@appflowy.io", timestamp_nano())
let user = "user1";
let password = "DeepFakePassword!123";
c.register(user, &email, password).await.unwrap();
assert!(c.logged_in_token().is_some());
(email, user.to_string(), password.to_string())
} }