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
SQLX_VERSION: 0.6.2
SQLX_FEATURES: "rustls,postgres"
APP__GOTRUE__BASE_URL: ${{ secrets.APP__GOTRUE__BASE_URL }}
APP__GOTRUE__JWT_SECRET: ${{ secrets.APP__GOTRUE__JWT_SECRET }}
APP__GOTRUE__BASE_URL: http://localhost:3000
APP__GOTRUE__JWT_SECRET: hello123
GOTRUE_SMTP_PASS: ${{ secrets.GOTRUE_SMTP_PASS }}
jobs:
test:
@ -31,6 +32,7 @@ jobs:
image: redis:7
ports:
- 6379:6379
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
@ -52,11 +54,21 @@ jobs:
- name: Check sqlx-data.json is up-to-date
run: |
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
run: |
cargo build
cargo run &
sleep 5 # wait some time for server to be ready
cargo test

View File

@ -1,11 +1,12 @@
FROM lukemathwalker/cargo-chef:latest-rust-1.65.0 as chef
FROM lukemathwalker/cargo-chef:latest-rust-1.69.0 as chef
WORKDIR /app
RUN apt update && apt install lld clang -y
FROM chef as planner
COPY . .
# Compute a lock-like file for our project
RUN cargo chef prepare --recipe-path recipe.json
RUN cargo chef prepare --recipe-path recipe.json
FROM chef as builder
COPY --from=planner /app/recipe.json recipe.json

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
username: "postgres"
password: "password"
database_name: "appflowy"
database_name: "postgres"
websocket:
heartbeat_interval: 8
client_timeout: 10
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'
services:
postgres:
image: postgres:14
image: postgres
environment:
- POSTGRES_USER=${POSTGRES_USER:-postgres}
- POSTGRES_DB=${POSTGRES_DB:-postgres}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password}
- POSTGRES_HOST=${POSTGRES_HOST:-postgres}
ports:
- "5433:5432"
- 5433:5432
volumes:
- ./build/init.sql:/docker-entrypoint-initdb.d/init.sql
redis:
image: redis:7
image: redis
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:
restart: on-failure
environment:
@ -24,5 +52,6 @@ services:
depends_on:
- redis
- postgres
- gotrue
ports:
- 8000:8000

View File

@ -69,7 +69,7 @@ pub struct AccessTokenResponse {
pub access_token: String,
pub token_type: String,
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 user: User,
pub provider_access_token: Option<String>,

View File

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

View File

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

View File

@ -1,6 +1,9 @@
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]
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() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
// pre-registered and confirmed email
let email = "xigahi8979@tipent.com";
c.sign_in_password(email, "Hello123!")
c.sign_in_password(REGISTERED_EMAIL, REGISTERED_PASSWORD)
.await
.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;
#[tokio::test]
async fn sign_up_success() {
let email = generate_unique_email();
let password = "Hello!123#";
let c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let resp = c
.sign_up("exampleuser@appflowy.io", "Hello!123#")
.await
.unwrap();
let resp = c.sign_up(&email, password).await.unwrap();
assert!(resp.confirmation_sent_at.is_some());
assert!(resp.confirmed_at.is_none());
}
#[tokio::test]
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 resp = c.sign_up("exampleuser", "Hello!123#").await;
let resp = c.sign_up(invalid_email, password).await;
assert!(resp.is_err());
}
#[tokio::test]
async fn sign_up_invalid_password() {
let email = generate_unique_email();
let password = "123";
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());
}

View File

@ -1,46 +1,52 @@
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]
async fn update_but_not_logged_in() {
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());
}
#[tokio::test]
async fn update_password_same_password() {
let mut c = Client::from(reqwest::Client::new(), LOCALHOST_URL);
let email = "xigahi8979@tipent.com";
let password = "Hello123!";
c.sign_in_password(email, password).await.unwrap().unwrap();
let res = c.update(email, "Hello123!").await;
assert!(res.is_err());
c.sign_in_password(REGISTERED_EMAIL, REGISTERED_PASSWORD)
.await
.unwrap()
.unwrap();
c.update(REGISTERED_EMAIL, REGISTERED_PASSWORD)
.await
.unwrap();
}
#[tokio::test]
async fn update_password_and_revert() {
let email = "xigahi8979@tipent.com";
let old_password = "Hello123!";
let new_password = "Hello456!";
{
// change password to new_password
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
.unwrap()
.unwrap();
c.update(email, new_password).await.unwrap();
c.update(REGISTERED_EMAIL, new_password).await.unwrap();
}
{
// revert password to old_password
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
.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 appflowy_cloud::client::http::Client;
pub const LOCALHOST_URL: &str = "http://localhost:8000"; //TODO: change to default port, use https
pub const REGISTERED_EMAIL: &str = "xigahi8979@tipent.com";
pub const REGISTERED_PASSWORD: &str = "Hello123!";
pub fn timestamp_nano() -> u128 {
SystemTime::now()
@ -11,12 +10,6 @@ pub fn timestamp_nano() -> u128 {
.as_nanos()
}
pub async fn register_deep_fake(c: &mut Client) -> (String, String, String) {
let email = format!("deep_fake{}@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())
pub fn generate_unique_email() -> String {
format!("{}@appflowy.io", timestamp_nano())
}