Skip to content

Commit

Permalink
feat: add backend login
Browse files Browse the repository at this point in the history
  • Loading branch information
luisfbl committed Jan 15, 2024
1 parent d331336 commit 5f50560
Show file tree
Hide file tree
Showing 21 changed files with 645 additions and 34 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ resolver = "2"
edition = "2021"

[workspace.dependencies]
sqlx = { version = "0.7.3", default-features = false, features = [ "runtime-tokio", "tls-rustls", "postgres" ] }
sqlx = { version = "0.7.3", default-features = false, features = [ "runtime-tokio", "tls-rustls", "postgres", "uuid" ] }
tokio = { version = "1.35.0", default-features = false, features = ["macros", "rt-multi-thread"] }
axum = { version = "0.7.2", default-features = false, features = ["http2", "json", "tokio", "tower-log", "query"] }
redis = { version = "0.24.0", default-features = false, features = ["ahash", "tokio-comp"] }
Expand Down
2 changes: 1 addition & 1 deletion cdn-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use axum::Router;
use core::database::SqlxManager;
use app_core::database::SqlxManager;
use common::config::Settings;

#[tokio::main]
Expand Down
8 changes: 6 additions & 2 deletions common/src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,27 @@
use serde::{Deserialize, Serialize};
use crate::config::cdn_server::CdnServer;
use crate::config::web::WebApp;

use self::{
rabbit::RabbitSettings, redis::RedisSettings, secrets::Secrets, webserver::WebServerSettings,
rabbit::RabbitSettings, redis::RedisSettings, secrets::Secrets, web::WebServerSettings,
};

pub mod database;
pub mod rabbit;
pub mod redis;
pub mod secrets;
pub mod settings;
pub mod webserver;
pub mod web;
pub mod cdn_server;

#[derive(Serialize, Deserialize, Default, Clone)]
pub struct Settings {
#[serde(rename="web-server")]
pub webserver: WebServerSettings,
#[serde(rename="cdn-server")]
pub cdn_server: CdnServer,
#[serde(rename="web-app")]
pub webapp: WebApp,
pub redis: RedisSettings,
pub rabbitmq: RabbitSettings,
pub secrets: Secrets,
Expand Down
33 changes: 20 additions & 13 deletions common/src/config/secrets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,34 @@ use serde::{Deserialize, Serialize};

use super::env_var;

#[derive(Serialize, Deserialize, Clone)]
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct Secrets {
pub jwt: String,
pub google: OauthSecret,
pub facebook: OauthSecret,
pub linkedin: OauthSecret
}

impl Secrets {
pub(super) fn new_from_env() -> Self {
Self {
jwt: env_var("JWT_KEY"),
google: OauthSecret {
client_id: env_var("GOOGLE_CLIENT_ID"),
client_secret: env_var("GOOGLE_CLIENT_SECRET")
},
facebook: OauthSecret {
client_id: env_var("FACEBOOK_CLIENT_ID"),
client_secret: env_var("FACEBOOK_CLIENT_SECRET")
},
linkedin: OauthSecret {
client_id: env_var("LINKEDIN_CLIENT_ID"),
client_secret: env_var("LINKEDIN_CLIENT_SECRET")
},
}
}

pub(super) fn echo_env(&self) {
std::env::set_var("JWT_KEY", &self.jwt)
}
}

impl Default for Secrets {
fn default() -> Self {
Self {
jwt: String::default(),
}
}
#[derive(Serialize, Deserialize, Clone, Default)]
pub struct OauthSecret {
pub client_id: String,
pub client_secret: String,
}
5 changes: 3 additions & 2 deletions common/src/config/settings.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use std::{fs, path::Path, process};

use log::{error, info, warn};
use crate::config::web::WebApp;

use super::{
rabbit::RabbitSettings, redis::RedisSettings, secrets::Secrets, webserver::WebServerSettings,
rabbit::RabbitSettings, redis::RedisSettings, secrets::Secrets, web::WebServerSettings,
cdn_server::CdnServer, Settings,
};

Expand Down Expand Up @@ -57,6 +58,7 @@ impl Settings {
Self {
webserver: WebServerSettings::new_from_env(),
cdn_server: CdnServer::new_from_env(),
webapp: WebApp::new_from_env(),
redis: RedisSettings::new_from_env(),
rabbitmq: RabbitSettings::new_from_env(),
secrets: Secrets::new_from_env(),
Expand All @@ -65,7 +67,6 @@ impl Settings {

fn echo_env(&self) {
self.webserver.echo_env();
self.secrets.echo_env();
self.cdn_server.echo_env();
}
}
40 changes: 40 additions & 0 deletions common/src/config/webserver.rs → common/src/config/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ pub struct WebServerSettings {
pub database: DatabaseSettings,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct WebApp {
pub host: String,
pub port: u16,
}

impl WebServerSettings {
pub(super) fn new_from_env() -> Self {
Self {
Expand All @@ -22,11 +28,36 @@ impl WebServerSettings {
format!("{}:{}", self.host, self.port)
}

pub fn protocol_url(&self) -> String {
let protocol = if &self.host == "localhost" { "http" } else { "https" };

format!("{}://{}", protocol, self.url())
}

pub(super) fn echo_env(&self) {
std::env::set_var("API_SERVER_URL", self.url())
}
}

impl WebApp {
pub(super) fn new_from_env() -> Self {
Self {
host: env_var("WEB_APP_HOST"),
port: env_var("WEB_APP_PORT").parse::<u16>().unwrap(),
}
}

pub fn url(&self) -> String {
format!("{}:{}", self.host, self.port)
}

pub fn protocol_url(&self) -> String {
let protocol = if &self.host == "localhost" { "http" } else { "https" };

format!("{}://{}", protocol, self.url())
}
}

impl Default for WebServerSettings {
fn default() -> Self {
Self {
Expand All @@ -36,3 +67,12 @@ impl Default for WebServerSettings {
}
}
}

impl Default for WebApp {
fn default() -> Self {
Self {
host: "localhost".to_string(),
port: 5173,
}
}
}
5 changes: 4 additions & 1 deletion rest-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ axum-login = { workspace = true }
tower-sessions = { workspace = true }

async-trait = { version = "0.1.77" }
password-auth = { version = "1.0.0" }
password-auth = { version = "1.0.0" }
oauth2 = { version = "4.4.2" }
reqwest = { version = "0.11.23", features = ["json"] }
thiserror = "1.0.56"
Empty file.
24 changes: 24 additions & 0 deletions rest-server/migrations/20231224161433_init.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

CREATE TABLE IF NOT EXISTS
"users" (
id UUID DEFAULT uuid_generate_v4() NOT NULL PRIMARY KEY,
name VARCHAR(64),
email VARCHAR(255) NOT NULL UNIQUE,
phone INTEGER UNIQUE,
account_type INTEGER DEFAULT 2,
password VARCHAR(255),
address varchar(255),
avatar varchar(64),
bio varchar(511),
access_token text
);

CREATE SCHEMA IF NOT EXISTS "tower_sessions";

CREATE TABLE IF NOT EXISTS
"tower_sessions"."sessions" (
id text PRIMARY KEY NOT NULL,
data bytea NOT NULL,
expiry_date timestamptz NOT NULL
);
79 changes: 79 additions & 0 deletions rest-server/src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use axum::{Extension, Router};
use axum_login::{AuthManagerLayer, AuthManagerLayerBuilder};
use sqlx::{Pool, Postgres};
use tower_sessions::{Expiry, PostgresStore, SessionManagerLayer};
use tower_sessions::cookie::time::Duration;
use app_core::database::SqlxManager;
use common::config::Settings;
use crate::provider::AuthProviders;
use crate::routes;
use crate::routes::auth::backend::Backend;

pub struct WebApp {
settings: Settings,
database: SqlxManager
}

#[derive(Clone)]
pub struct AppState {
pub pool: Pool<Postgres>,
pub settings: Settings,
}

impl WebApp {
pub async fn new() -> Self {
let settings = Settings::new("application.toml");
let database = SqlxManager::new(&settings.webserver.database)
.await;

sqlx::migrate!().run(&database.pool).await.expect("Failed to register tables");

Self {
settings,
database
}
}

fn router(&self) -> Router {
let state = AppState {
pool: self.database.pool.clone(),
settings: self.settings.clone(),
};

Router::new()
.nest("/api",
routes::auth::router()
)
.with_state(state)
.layer(self.auth_layer())
.layer(Extension(AuthProviders::new(&self.settings)))
}

fn session(&self) -> SessionManagerLayer<PostgresStore> {
let session_store = PostgresStore::new(self.database.pool.clone())
.with_table_name("sessions")
.unwrap();

SessionManagerLayer::new(session_store)
.with_secure(false)
.with_name("session_id")
.with_expiry(Expiry::OnInactivity(Duration::days(10)))
}

fn auth_layer(&self) -> AuthManagerLayer<Backend, PostgresStore> {
let backend = Backend::new(
self.database.pool.clone()
);

AuthManagerLayerBuilder::new(backend, self.session()).build()
}

pub async fn serve(&self) {
let listener = tokio::net::TcpListener::bind(self.settings.webserver.url())
.await
.unwrap();
axum::serve(listener, self.router())
.await
.expect("Failed to start axum server");
}
}
41 changes: 41 additions & 0 deletions rest-server/src/json/auth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use oauth2::CsrfToken;
use serde::Deserialize;

#[derive(Debug, Clone, Deserialize)]
pub enum Credentials {
Password(LoginCreds),
OAuth(OAuthCreds),
}

#[derive(Debug, Clone, Deserialize)]
pub struct OAuthCreds {
pub user: UserInfo,
pub token: String,
}

#[derive(Debug, Clone, Deserialize)]
pub struct LoginCreds {
pub email: String,
pub password: String,
}

#[derive(Debug, Clone, Deserialize)]
pub struct SignCreds {
pub email: String,
pub name: String,
pub phone: String,
pub password: String,
pub next: String,
}

#[derive(Debug, Clone, Deserialize)]
pub struct AuthzResp {
pub code: String,
pub state: CsrfToken,
}

#[derive(Debug, Deserialize, Clone)]
pub struct UserInfo {
pub email: String,
pub name: String
}
1 change: 1 addition & 0 deletions rest-server/src/json/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod auth;
24 changes: 10 additions & 14 deletions rest-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,16 @@
use axum::Router;
use core::database::SqlxManager;
use common::config::Settings;
use crate::app::WebApp;

mod routes;
mod models;
mod app;
pub mod json;
mod provider;

#[tokio::main]
async fn main() {
let settings = Settings::new("application.toml");
let database = SqlxManager::new(&settings.webserver.database)
.await;

let router = Router::new()
.with_state(database.pool);
env_logger::init();

let listener = tokio::net::TcpListener::bind(settings.webserver.url())
.await
.unwrap();
axum::serve(listener, router)
WebApp::new().await
.serve()
.await
.expect("Failed to start axum server");
}
1 change: 1 addition & 0 deletions rest-server/src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod users;
Loading

0 comments on commit 5f50560

Please sign in to comment.