Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial v0.3 draft #83

Draft
wants to merge 21 commits into
base: main
Choose a base branch
from
Draft
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
proxy domain
Alw3ys committed Apr 20, 2024
commit 96f2bebd754d9554e7cbab3b962ad52473601c14
7 changes: 5 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions doseid/migrations/20240414162551_init.sql
Original file line number Diff line number Diff line change
@@ -102,3 +102,16 @@ CREATE TABLE IF NOT EXISTS env (
FOREIGN KEY (service_id) REFERENCES service(id),
FOREIGN KEY (deployment_id) REFERENCES deployment(id)
);

CREATE TABLE IF NOT EXISTS domain (
id UUID NOT NULL,
name TEXT NOT NULL,
service_id UUID NOT NULL,
owner_id UUID NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (owner_id) REFERENCES account(id),
FOREIGN KEY (service_id) REFERENCES service(id),
UNIQUE (name, owner_id)
);
3 changes: 3 additions & 0 deletions proxy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -7,13 +7,16 @@ authors.workspace = true
edition.workspace = true

[dependencies]
hyper = { version = "1.3.1", features = ["full"] }
hyper-util = { version = "0.1.3", features = ["client-legacy"] }
tokio = { version = "1.37.0", features = ["full"] }
axum = "0.7.5"
anyhow = "1.0.82"
cached = "0.49.3"
tracing = "0.1.40"
tracing-subscriber = { version = "0.3.18", features = ["json"] }
serde = { version = "1.0.198", features = ["derive"] }
serde_json = "1.0.115"
dotenv = "0.15.0"
sqlx = { version = "0.7.3", features = [
"runtime-tokio",
45 changes: 45 additions & 0 deletions proxy/src/server/domain.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
use sqlx::{Error, Pool, Postgres};
use std::sync::Arc;

pub async fn get_domain(host: String, pool: Arc<Pool<Postgres>>) -> Option<i16> {
// Reserved ones
let wildcard_domain = ".dosei.app";
if host.ends_with(&wildcard_domain) {
let host_split: Vec<&str> = host.split(wildcard_domain).collect();
if let Some(subdomain) = host_split.first() {
let subdomain_split: Vec<&str> = subdomain.split('-').collect();
if subdomain_split.len() >= 2 {
let account_name = subdomain_split.first().unwrap();
let service_name = subdomain.replace(&format!("{}-", account_name), "");
match sqlx::query!(
"
SELECT
account.name AS account_name,
service.name AS service_name,
deployment.host_port AS service_port
FROM account
JOIN service ON account.id = service.owner_id
JOIN deployment ON account.id = deployment.owner_id
WHERE account.name = $1 AND service.name = $2 AND deployment.status = 'running'
",
account_name,
service_name
)
.fetch_one(&*pool)
.await
{
Ok(record) => {
return record.service_port;
}
Err(error) => match &error {
Error::RowNotFound => {}
_ => {
return None;
}
},
};
}
}
}
None
}
67 changes: 66 additions & 1 deletion proxy/src/server/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
mod domain;
mod ping;

use crate::config::Config;
use anyhow::{anyhow, Context};
use axum::{routing, Extension, Router};
use axum::body::Body;
use axum::extract::{Request, State};
use axum::http::{StatusCode, Uri};
use axum::response::{IntoResponse, Response};
use axum::routing::any;
use axum::{routing, Extension, Json, Router};
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::rt::TokioExecutor;
use serde_json::json;
use sqlx::{Pool, Postgres};
use std::sync::Arc;
use tokio::net::TcpListener;
use tokio::signal;
use tracing::info;

type Client = hyper_util::client::legacy::Client<HttpConnector, Body>;

pub async fn start_server(config: &'static Config) -> anyhow::Result<()> {
let pool = Pool::<Postgres>::connect(&config.database_url)
.await
.context("Failed to connect to Postgres")?;
let shared_pool = Arc::new(pool);

let client: Client = hyper_util::client::legacy::Client::<(), ()>::builder(TokioExecutor::new())
.build(HttpConnector::new());

let app = Router::new()
.route("/ping", routing::get(ping::ping))
.route("/", any(handler))
.route("/*path", any(handler))
.with_state(client)
.layer(Extension(Arc::clone(&shared_pool)))
.layer(Extension(config));

@@ -39,3 +56,51 @@ pub async fn start_server(config: &'static Config) -> anyhow::Result<()> {
info!("Gracefully stopping... (Press Ctrl+C again to force)");
Ok(())
}

async fn handler(
pool: Extension<Arc<Pool<Postgres>>>,
State(client): State<Client>,
mut req: Request,
) -> Result<Response, StatusCode> {
let headers = req.headers();
let host = match headers.get("host") {
Some(host_header) => host_header.to_str().unwrap_or_default(),
None => {
return Ok(
(
StatusCode::NOT_FOUND,
Json(json!({"message": "Deployment not found."})),
)
.into_response(),
)
}
};
let path = req.uri().path();
let path_query = req
.uri()
.path_and_query()
.map(|v| v.as_str())
.unwrap_or(path);

match domain::get_domain(host.to_string(), Arc::clone(&pool)).await {
None => Ok(
(
StatusCode::NOT_FOUND,
Json(json!({"message": "Deployment not found."})),
)
.into_response(),
),
Some(target_port) => {
let target_service = format!("http://127.0.0.1:{}{}", target_port, path_query);
info!("Forwarding: {} -> {}", host, target_service);
*req.uri_mut() = Uri::try_from(target_service).unwrap();
Ok(
client
.request(req)
.await
.map_err(|_| StatusCode::BAD_REQUEST)?
.into_response(),
)
}
}
}