chore: merge with main

This commit is contained in:
Fu Zi Xiang 2023-10-13 12:10:28 +08:00
commit 20927fc3f4
No known key found for this signature in database
16 changed files with 90 additions and 85 deletions

View File

@ -29,6 +29,9 @@ jobs:
- name: Replace values in .env - name: Replace values in .env
run: | run: |
# log level
sed -i 's|RUST_LOG=.*|RUST_LOG=trace|' .env
sed -i 's/GOTRUE_SMTP_USER=.*/GOTRUE_SMTP_USER=${{ secrets.GOTRUE_SMTP_USER }}/' .env sed -i 's/GOTRUE_SMTP_USER=.*/GOTRUE_SMTP_USER=${{ secrets.GOTRUE_SMTP_USER }}/' .env
sed -i 's/GOTRUE_SMTP_PASS=.*/GOTRUE_SMTP_PASS=${{ secrets.GOTRUE_SMTP_PASS }}/' .env sed -i 's/GOTRUE_SMTP_PASS=.*/GOTRUE_SMTP_PASS=${{ secrets.GOTRUE_SMTP_PASS }}/' .env
sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.GOTRUE_SMTP_ADMIN_EMAIL }}/' .env sed -i 's/GOTRUE_SMTP_ADMIN_EMAIL=.*/GOTRUE_SMTP_ADMIN_EMAIL=${{ secrets.GOTRUE_SMTP_ADMIN_EMAIL }}/' .env

View File

@ -0,0 +1,16 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO af_user (uuid, email, name)\n VALUES ($1, $2, $3)\n ON CONFLICT (email) DO NOTHING;\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "171f55ffb42bc6ae115ebf29f4cee7419690d3b13f1e27b93f03c399dd2838e6"
}

View File

@ -1,16 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n INSERT INTO af_user (uuid, email, name)\n SELECT $1, $2, $3\n WHERE NOT EXISTS (\n SELECT 1 FROM public.af_user WHERE email = $2\n )\n AND NOT EXISTS (\n SELECT 1 FROM public.af_user WHERE uuid = $1\n )\n ",
"describe": {
"columns": [],
"parameters": {
"Left": [
"Uuid",
"Text",
"Text"
]
},
"nullable": []
},
"hash": "6638a972a142a500adb842843394d64d5625b0fef0a7c14b1b751bd64567f9b7"
}

View File

@ -22,17 +22,16 @@ services:
- ./migrations/before:/docker-entrypoint-initdb.d - ./migrations/before:/docker-entrypoint-initdb.d
pgadmin: pgadmin:
build: image: dpage/pgadmin4
context: .
dockerfile: docker/pgadmin.Dockerfile
depends_on: depends_on:
- postgres - postgres
environment: environment:
- PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL} - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL}
- PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD} - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
- PGADMIN_CONFIG_WTF_CSRF_ENABLED=False
ports: ports:
- 5400:80 - 5400:80
volumes:
- ./docker/pgadmin/servers.json:/pgadmin4/servers.json
redis: redis:
image: redis image: redis

View File

@ -36,17 +36,16 @@ services:
- ./migrations/before:/docker-entrypoint-initdb.d - ./migrations/before:/docker-entrypoint-initdb.d
pgadmin: pgadmin:
build: image: dpage/pgadmin4
context: .
dockerfile: docker/pgadmin.Dockerfile
depends_on: depends_on:
- postgres - postgres
environment: environment:
- PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL} - PGADMIN_DEFAULT_EMAIL=${PGADMIN_DEFAULT_EMAIL}
- PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD} - PGADMIN_DEFAULT_PASSWORD=${PGADMIN_DEFAULT_PASSWORD}
- PGADMIN_CONFIG_WTF_CSRF_ENABLED=False
ports: ports:
- 5400:80 - 5400:80
volumes:
- ./docker/pgadmin/servers.json:/pgadmin4/servers.json
redis: redis:
image: redis image: redis

View File

@ -1,11 +0,0 @@
FROM dpage/pgadmin4
COPY ./docker/pgadmin/servers.json /pgadmin4/servers.json
COPY ./docker/pgadmin/custom_entrypoint.sh /custom_entrypoint.sh
USER root
RUN chmod +x /custom_entrypoint.sh
USER pgadmin
ENTRYPOINT ["/custom_entrypoint.sh"]

View File

@ -1,10 +0,0 @@
#!/bin/sh
# Call the original entrypoint
/entrypoint.sh "$@"
# Your additional commands to invoke setup.py (example below, adjust accordingly)
python setup.py --load-servers /path/to/your/servers.json
# Keep container running
tail -f /dev/null

View File

@ -418,7 +418,7 @@ impl Client {
Ok(()) Ok(())
} }
#[instrument(level = "debug", skip_all, err)] #[instrument(skip_all, err)]
pub async fn sign_in_password(&self, email: &str, password: &str) -> Result<bool, AppError> { pub async fn sign_in_password(&self, email: &str, password: &str) -> Result<bool, AppError> {
let access_token_resp = self let access_token_resp = self
.gotrue_client .gotrue_client

View File

@ -15,6 +15,9 @@ pub enum DatabaseError {
#[error(transparent)] #[error(transparent)]
UuidError(#[from] uuid::Error), UuidError(#[from] uuid::Error),
#[error(transparent)]
SqlxError(sqlx::Error),
#[error("Storage space not enough")] #[error("Storage space not enough")]
StorageSpaceNotEnough, StorageSpaceNotEnough,
@ -35,7 +38,7 @@ impl From<sqlx::Error> for DatabaseError {
fn from(value: sqlx::Error) -> Self { fn from(value: sqlx::Error) -> Self {
match value { match value {
Error::RowNotFound => DatabaseError::RecordNotFound, Error::RowNotFound => DatabaseError::RecordNotFound,
_ => DatabaseError::Internal(value.into()), _ => DatabaseError::SqlxError(value),
} }
} }
} }

View File

@ -1,3 +1,4 @@
use database_entity::database_error::DatabaseError;
use database_entity::AFBlobMetadata; use database_entity::AFBlobMetadata;
use rust_decimal::prelude::ToPrimitive; use rust_decimal::prelude::ToPrimitive;
use sqlx::types::Decimal; use sqlx::types::Decimal;
@ -10,7 +11,7 @@ pub async fn is_blob_metadata_exists(
pool: &PgPool, pool: &PgPool,
workspace_id: &Uuid, workspace_id: &Uuid,
file_id: &str, file_id: &str,
) -> Result<bool, sqlx::Error> { ) -> Result<bool, DatabaseError> {
let exists: (bool,) = sqlx::query_as( let exists: (bool,) = sqlx::query_as(
r#" r#"
SELECT EXISTS ( SELECT EXISTS (
@ -35,8 +36,8 @@ pub async fn insert_blob_metadata(
workspace_id: &Uuid, workspace_id: &Uuid,
file_type: &str, file_type: &str,
file_size: i64, file_size: i64,
) -> Result<AFBlobMetadata, sqlx::Error> { ) -> Result<AFBlobMetadata, DatabaseError> {
sqlx::query_as!( let metadata = sqlx::query_as!(
AFBlobMetadata, AFBlobMetadata,
r#" r#"
INSERT INTO af_blob_metadata INSERT INTO af_blob_metadata
@ -53,7 +54,8 @@ pub async fn insert_blob_metadata(
file_size file_size
) )
.fetch_one(pg_pool) .fetch_one(pg_pool)
.await .await?;
Ok(metadata)
} }
#[instrument(level = "trace", skip_all, err)] #[instrument(level = "trace", skip_all, err)]
@ -61,8 +63,8 @@ pub async fn delete_blob_metadata(
pg_pool: &PgPool, pg_pool: &PgPool,
workspace_id: &Uuid, workspace_id: &Uuid,
file_id: &str, file_id: &str,
) -> Result<AFBlobMetadata, sqlx::Error> { ) -> Result<AFBlobMetadata, DatabaseError> {
sqlx::query_as!( let metadata = sqlx::query_as!(
AFBlobMetadata, AFBlobMetadata,
r#" r#"
DELETE FROM af_blob_metadata DELETE FROM af_blob_metadata
@ -73,7 +75,8 @@ pub async fn delete_blob_metadata(
file_id, file_id,
) )
.fetch_one(pg_pool) .fetch_one(pg_pool)
.await .await?;
Ok(metadata)
} }
#[instrument(level = "trace", skip_all, err)] #[instrument(level = "trace", skip_all, err)]
@ -81,8 +84,8 @@ pub async fn get_blob_metadata(
pg_pool: &PgPool, pg_pool: &PgPool,
workspace_id: &Uuid, workspace_id: &Uuid,
file_id: &str, file_id: &str,
) -> Result<AFBlobMetadata, sqlx::Error> { ) -> Result<AFBlobMetadata, DatabaseError> {
sqlx::query_as!( let metadata = sqlx::query_as!(
AFBlobMetadata, AFBlobMetadata,
r#" r#"
SELECT * FROM af_blob_metadata SELECT * FROM af_blob_metadata
@ -92,14 +95,15 @@ pub async fn get_blob_metadata(
file_id, file_id,
) )
.fetch_one(pg_pool) .fetch_one(pg_pool)
.await .await?;
Ok(metadata)
} }
#[instrument(level = "trace", skip_all, err)] #[instrument(level = "trace", skip_all, err)]
pub async fn get_workspace_usage_size( pub async fn get_workspace_usage_size(
pool: &PgPool, pool: &PgPool,
workspace_id: &Uuid, workspace_id: &Uuid,
) -> Result<u64, sqlx::Error> { ) -> Result<u64, DatabaseError> {
let row: (Option<Decimal>,) = let row: (Option<Decimal>,) =
sqlx::query_as(r#"SELECT SUM(file_size) FROM af_blob_metadata WHERE workspace_id = $1;"#) sqlx::query_as(r#"SELECT SUM(file_size) FROM af_blob_metadata WHERE workspace_id = $1;"#)
.bind(workspace_id) .bind(workspace_id)

View File

@ -1,10 +1,13 @@
use anyhow::Context;
use database_entity::database_error::DatabaseError;
use sqlx::PgPool; use sqlx::PgPool;
use tracing::instrument;
pub async fn update_user_name( pub async fn update_user_name(
pool: &PgPool, pool: &PgPool,
uuid: &uuid::Uuid, uuid: &uuid::Uuid,
name: &str, name: &str,
) -> Result<(), sqlx::Error> { ) -> Result<(), DatabaseError> {
sqlx::query!( sqlx::query!(
r#" r#"
UPDATE af_user UPDATE af_user
@ -19,35 +22,35 @@ pub async fn update_user_name(
Ok(()) Ok(())
} }
#[instrument(skip_all, err)]
pub async fn create_user_if_not_exists( pub async fn create_user_if_not_exists(
pool: &PgPool, pool: &PgPool,
user_uuid: &uuid::Uuid, user_uuid: &uuid::Uuid,
email: &str, email: &str,
name: &str, name: &str,
) -> Result<bool, sqlx::Error> { ) -> Result<bool, DatabaseError> {
let affected_rows = sqlx::query!( let affected_rows = sqlx::query!(
r#" r#"
INSERT INTO af_user (uuid, email, name) INSERT INTO af_user (uuid, email, name)
SELECT $1, $2, $3 VALUES ($1, $2, $3)
WHERE NOT EXISTS ( ON CONFLICT (email) DO NOTHING;
SELECT 1 FROM public.af_user WHERE email = $2
)
AND NOT EXISTS (
SELECT 1 FROM public.af_user WHERE uuid = $1
)
"#, "#,
user_uuid, user_uuid,
email, email,
name name
) )
.execute(pool) .execute(pool)
.await? .await
.context(format!(
"Fail to insert user with uuid: {}, name: {}, email: {}",
user_uuid, name, email
))?
.rows_affected(); .rows_affected();
Ok(affected_rows > 0) Ok(affected_rows > 0)
} }
pub async fn uid_from_uuid(pool: &PgPool, gotrue_uuid: &uuid::Uuid) -> Result<i64, sqlx::Error> { pub async fn uid_from_uuid(pool: &PgPool, gotrue_uuid: &uuid::Uuid) -> Result<i64, DatabaseError> {
let uid = sqlx::query!( let uid = sqlx::query!(
r#" r#"
SELECT uid FROM af_user WHERE uuid = $1 SELECT uid FROM af_user WHERE uuid = $1

View File

@ -3,13 +3,14 @@ use sqlx::{
PgPool, PgPool,
}; };
use database_entity::database_error::DatabaseError;
use database_entity::{AFRole, AFUserProfileView, AFWorkspace, AFWorkspaceMember}; use database_entity::{AFRole, AFUserProfileView, AFWorkspace, AFWorkspaceMember};
pub async fn select_all_workspaces_owned( pub async fn select_all_workspaces_owned(
pool: &PgPool, pool: &PgPool,
owner_uuid: &Uuid, owner_uuid: &Uuid,
) -> Result<Vec<AFWorkspace>, sqlx::Error> { ) -> Result<Vec<AFWorkspace>, DatabaseError> {
sqlx::query_as!( let workspaces = sqlx::query_as!(
AFWorkspace, AFWorkspace,
r#" r#"
SELECT * FROM public.af_workspace WHERE owner_uid = ( SELECT * FROM public.af_workspace WHERE owner_uid = (
@ -19,14 +20,15 @@ pub async fn select_all_workspaces_owned(
owner_uuid owner_uuid
) )
.fetch_all(pool) .fetch_all(pool)
.await .await?;
Ok(workspaces)
} }
pub async fn select_user_is_workspace_owner( pub async fn select_user_is_workspace_owner(
pg_pool: &PgPool, pg_pool: &PgPool,
user_uuid: &Uuid, user_uuid: &Uuid,
workspace_uuid: &Uuid, workspace_uuid: &Uuid,
) -> Result<bool, sqlx::Error> { ) -> Result<bool, DatabaseError> {
let exists = sqlx::query_scalar!( let exists = sqlx::query_scalar!(
r#" r#"
SELECT EXISTS( SELECT EXISTS(
@ -50,7 +52,7 @@ pub async fn insert_workspace_members(
workspace_id: &uuid::Uuid, workspace_id: &uuid::Uuid,
member_emails: &[String], member_emails: &[String],
role: AFRole, role: AFRole,
) -> Result<(), sqlx::Error> { ) -> Result<(), DatabaseError> {
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO public.af_workspace_member (workspace_id, uid, role_id) INSERT INTO public.af_workspace_member (workspace_id, uid, role_id)
@ -74,7 +76,7 @@ pub async fn delete_workspace_members(
pool: &PgPool, pool: &PgPool,
workspace_id: &uuid::Uuid, workspace_id: &uuid::Uuid,
member_emails: &[String], member_emails: &[String],
) -> Result<(), sqlx::Error> { ) -> Result<(), DatabaseError> {
sqlx::query!( sqlx::query!(
r#" r#"
DELETE FROM public.af_workspace_member DELETE FROM public.af_workspace_member
@ -94,8 +96,8 @@ pub async fn delete_workspace_members(
pub async fn select_workspace_members( pub async fn select_workspace_members(
pg_pool: &PgPool, pg_pool: &PgPool,
workspace_id: &uuid::Uuid, workspace_id: &uuid::Uuid,
) -> Result<Vec<AFWorkspaceMember>, sqlx::Error> { ) -> Result<Vec<AFWorkspaceMember>, DatabaseError> {
sqlx::query_as!( let members = sqlx::query_as!(
AFWorkspaceMember, AFWorkspaceMember,
r#" r#"
SELECT af_user.email, af_workspace_member.role_id AS role SELECT af_user.email, af_workspace_member.role_id AS role
@ -106,14 +108,15 @@ pub async fn select_workspace_members(
workspace_id workspace_id
) )
.fetch_all(pg_pool) .fetch_all(pg_pool)
.await .await?;
Ok(members)
} }
pub async fn select_user_profile_view_by_uuid( pub async fn select_user_profile_view_by_uuid(
pool: &PgPool, pool: &PgPool,
user_uuid: &Uuid, user_uuid: &Uuid,
) -> Result<Option<AFUserProfileView>, sqlx::Error> { ) -> Result<Option<AFUserProfileView>, DatabaseError> {
sqlx::query_as!( let user_profile = sqlx::query_as!(
AFUserProfileView, AFUserProfileView,
r#" r#"
SELECT * SELECT *
@ -122,5 +125,6 @@ pub async fn select_user_profile_view_by_uuid(
user_uuid user_uuid
) )
.fetch_optional(pool) .fetch_optional(pool)
.await .await?;
Ok(user_profile)
} }

View File

@ -45,9 +45,9 @@ impl actix_web::error::ResponseError for AppError {
// //
impl From<anyhow::Error> for AppError { impl From<anyhow::Error> for AppError {
fn from(err: anyhow::Error) -> Self { fn from(err: anyhow::Error) -> Self {
match err.downcast_ref::<AppError>() { match err.downcast::<AppError>() {
None => AppError::new(ErrorCode::Unhandled, err.to_string()), Err(err) => AppError::new(ErrorCode::Unhandled, err.to_string()),
Some(err) => err.clone(), Ok(err) => err,
} }
} }
} }

View File

@ -41,7 +41,10 @@ http {
# PgAdmin # PgAdmin
location /pgadmin/ { location /pgadmin/ {
proxy_set_header X-Script-Name /pgadmin; proxy_set_header X-Script-Name /pgadmin;
proxy_set_header X-Scheme $scheme;
proxy_set_header Host $host;
proxy_pass http://pgadmin:80/; proxy_pass http://pgadmin:80/;
proxy_redirect off;
} }
# Admin Frontend # Admin Frontend

View File

@ -18,6 +18,7 @@ use actix_web::web::{Data, Json};
use actix_web::HttpRequest; use actix_web::HttpRequest;
use actix_web::Result; use actix_web::Result;
use actix_web::{web, HttpResponse, Scope}; use actix_web::{web, HttpResponse, Scope};
use tracing_actix_web::RequestId;
pub fn user_scope() -> Scope { pub fn user_scope() -> Scope {
web::scope("/api/user") web::scope("/api/user")
@ -33,9 +34,11 @@ pub fn user_scope() -> Scope {
.service(web::resource("/password").route(web::post().to(change_password_handler))) .service(web::resource("/password").route(web::post().to(change_password_handler)))
} }
#[tracing::instrument(skip(state, path), err)]
async fn verify_handler( async fn verify_handler(
path: web::Path<String>, path: web::Path<String>,
state: Data<AppState>, state: Data<AppState>,
required_id: RequestId,
) -> Result<JsonAppResponse<SignInTokenResponse>> { ) -> Result<JsonAppResponse<SignInTokenResponse>> {
let access_token = path.into_inner(); let access_token = path.into_inner();
let is_new = biz::user::token_verify(&state.pg_pool, &state.gotrue_client, &access_token).await?; let is_new = biz::user::token_verify(&state.pg_pool, &state.gotrue_client, &access_token).await?;
@ -43,19 +46,22 @@ async fn verify_handler(
Ok(AppResponse::Ok().with_data(resp).into()) Ok(AppResponse::Ok().with_data(resp).into())
} }
#[tracing::instrument(level = "debug", skip(state))] #[tracing::instrument(skip(state), err)]
async fn profile_handler( async fn profile_handler(
uuid: UserUuid, uuid: UserUuid,
state: Data<AppState>, state: Data<AppState>,
required_id: RequestId,
) -> Result<JsonAppResponse<AFUserProfileView>> { ) -> Result<JsonAppResponse<AFUserProfileView>> {
let profile = biz::user::get_profile(&state.pg_pool, &uuid).await?; let profile = biz::user::get_profile(&state.pg_pool, &uuid).await?;
Ok(AppResponse::Ok().with_data(profile).into()) Ok(AppResponse::Ok().with_data(profile).into())
} }
#[tracing::instrument(skip(state, auth, req), err)]
async fn update_handler( async fn update_handler(
auth: Authorization, auth: Authorization,
req: Json<UpdateUsernameParams>, req: Json<UpdateUsernameParams>,
state: Data<AppState>, state: Data<AppState>,
required_id: RequestId,
) -> Result<JsonAppResponse<()>> { ) -> Result<JsonAppResponse<()>> {
let params = req.into_inner(); let params = req.into_inner();
biz::user::update_user(&state.pg_pool, &auth.uuid()?, &params.new_name).await?; biz::user::update_user(&state.pg_pool, &auth.uuid()?, &params.new_name).await?;

View File

@ -18,6 +18,7 @@ use sqlx::{PgPool, Postgres, Transaction};
use std::sync::Arc; use std::sync::Arc;
use token::{create_token, parse_token, TokenError}; use token::{create_token, parse_token, TokenError};
use tokio::sync::RwLock; use tokio::sync::RwLock;
use tracing::instrument;
pub async fn login( pub async fn login(
email: String, email: String,
@ -51,6 +52,7 @@ pub async fn logout(logged_user: LoggedUser, cache: Arc<RwLock<UserCache>>) {
cache.write().await.unauthorized(logged_user); cache.write().await.unauthorized(logged_user);
} }
#[instrument(skip_all, err)]
pub async fn register( pub async fn register(
username: String, username: String,
email: String, email: String,