Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions linkup-cli/src/commands/status.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::Context;
use colored::{ColoredString, Colorize};
use crossterm::{cursor, execute, style::Print, terminal};
use linkup::{get_additional_headers, HeaderMap, StorableDomain, TargetService};
use linkup::{get_additional_headers, Domain, HeaderMap, TargetService};
use serde::{Deserialize, Serialize};
use std::{
io::stdout,
Expand Down Expand Up @@ -269,7 +269,7 @@ fn table_header(terminal_width: u16) -> String {
output
}

pub fn format_state_domains(session_name: &str, domains: &[StorableDomain]) -> Vec<String> {
pub fn format_state_domains(session_name: &str, domains: &[Domain]) -> Vec<String> {
// Filter out domains that are subdomains of other domains
let filtered_domains = domains
.iter()
Expand Down
59 changes: 34 additions & 25 deletions linkup-cli/src/local_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ use std::{

use anyhow::Context;
use rand::distr::{Alphanumeric, SampleString};
use regex::Regex;
use serde::{Deserialize, Serialize};
use url::Url;

use linkup::{
CreatePreviewRequest, StorableDomain, StorableRewrite, StorableService, StorableSession,
UpdateSessionRequest,
CreatePreviewRequest, Domain, Rewrite, Session, SessionService, UpdateSessionRequest,
};

use crate::{
Expand All @@ -20,10 +20,10 @@ use crate::{
Result, LINKUP_CONFIG_ENV, LINKUP_STATE_FILE,
};

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct LocalState {
pub linkup: LinkupState,
pub domains: Vec<StorableDomain>,
pub domains: Vec<Domain>,
pub services: Vec<LocalService>,
}

Expand Down Expand Up @@ -70,7 +70,7 @@ impl LocalState {
pub fn domain_strings(&self) -> Vec<String> {
self.domains
.iter()
.map(|storable_domain| storable_domain.domain.clone())
.map(|domain| domain.domain.clone())
.collect::<Vec<String>>()
}

Expand All @@ -79,15 +79,20 @@ impl LocalState {
}
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct LinkupState {
pub session_name: String,
pub session_token: String,
pub worker_url: Url,
pub worker_token: String,
pub config_path: String,
pub tunnel: Option<Url>,
pub cache_routes: Option<Vec<String>>,
#[serde(
default,
serialize_with = "linkup::serde_ext::serialize_opt_vec_regex",
deserialize_with = "linkup::serde_ext::deserialize_opt_vec_regex"
)]
pub cache_routes: Option<Vec<Regex>>,
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Default)]
Expand All @@ -96,14 +101,14 @@ pub struct HealthConfig {
pub statuses: Option<Vec<u16>>,
}

#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct LocalService {
pub name: String,
pub remote: Url,
pub local: Url,
pub current: ServiceTarget,
pub directory: Option<String>,
pub rewrites: Vec<StorableRewrite>,
pub rewrites: Vec<Rewrite>,
pub health: Option<HealthConfig>,
}

Expand Down Expand Up @@ -135,7 +140,7 @@ impl Display for ServiceTarget {
pub struct YamlLocalConfig {
pub linkup: LinkupConfig,
pub services: Vec<YamlLocalService>,
pub domains: Vec<StorableDomain>,
pub domains: Vec<Domain>,
}

impl YamlLocalConfig {
Expand All @@ -153,7 +158,7 @@ impl YamlLocalConfig {
}
}

StorableService {
SessionService {
name,
location,
rewrites: yaml_local_service.rewrites.clone(),
Expand All @@ -173,7 +178,11 @@ impl YamlLocalConfig {
pub struct LinkupConfig {
pub worker_url: Url,
pub worker_token: String,
cache_routes: Option<Vec<String>>,
#[serde(
default,
deserialize_with = "linkup::serde_ext::deserialize_opt_vec_regex"
)]
cache_routes: Option<Vec<Regex>>,
}

#[derive(Deserialize, Clone)]
Expand All @@ -182,14 +191,14 @@ pub struct YamlLocalService {
remote: Url,
local: Url,
directory: Option<String>,
rewrites: Option<Vec<StorableRewrite>>,
rewrites: Option<Vec<Rewrite>>,
health: Option<HealthConfig>,
}

#[derive(Debug)]
pub struct ServerConfig {
pub local: StorableSession,
pub remote: StorableSession,
pub local: Session,
pub remote: Session,
}

pub fn config_to_state(
Expand Down Expand Up @@ -307,7 +316,7 @@ async fn upload_config_to_server(
linkup_url: &Url,
worker_token: &str,
desired_name: &str,
config: StorableSession,
config: Session,
) -> Result<String, worker_client::Error> {
let session_update_req = UpdateSessionRequest {
session_token: config.session_token,
Expand All @@ -329,7 +338,7 @@ impl From<&LocalState> for ServerConfig {
let local_server_services = state
.services
.iter()
.map(|service| StorableService {
.map(|service| SessionService {
name: service.name.clone(),
location: if service.current == ServiceTarget::Remote {
service.remote.clone()
Expand All @@ -338,12 +347,12 @@ impl From<&LocalState> for ServerConfig {
},
rewrites: Some(service.rewrites.clone()),
})
.collect::<Vec<StorableService>>();
.collect::<Vec<SessionService>>();

let remote_server_services = state
.services
.iter()
.map(|service| StorableService {
.map(|service| SessionService {
name: service.name.clone(),
location: if service.current == ServiceTarget::Remote {
service.remote.clone()
Expand All @@ -352,25 +361,25 @@ impl From<&LocalState> for ServerConfig {
},
rewrites: Some(service.rewrites.clone()),
})
.collect::<Vec<StorableService>>();
.collect::<Vec<SessionService>>();

let local_storable_session = StorableSession {
let local_session = Session {
session_token: state.linkup.session_token.clone(),
services: local_server_services,
domains: state.domains.clone(),
cache_routes: state.linkup.cache_routes.clone(),
};

let remote_storable_session = StorableSession {
let remote_session = Session {
session_token: state.linkup.session_token.clone(),
services: remote_server_services,
domains: state.domains.clone(),
cache_routes: state.linkup.cache_routes.clone(),
};

ServerConfig {
local: local_storable_session,
remote: remote_storable_session,
local: local_session,
remote: remote_session,
}
}
}
Expand All @@ -382,7 +391,7 @@ pub fn managed_domains(state: Option<&LocalState>, cfg_path: &Option<String>) ->
config
.domains
.iter()
.map(|storable_domain| storable_domain.domain.clone())
.map(|domain| domain.domain.clone())
.collect::<Vec<String>>(),
),
Err(_) => None,
Expand Down
56 changes: 31 additions & 25 deletions linkup/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub mod serde_ext;

mod headers;
mod memory_session_store;
mod name_gen;
Expand Down Expand Up @@ -163,31 +165,31 @@ pub fn get_target_service(
// If there was a destination created in a previous linkup, we don't want to
// re-do path rewrites, so we use the destination service.
if let Some(destination_service) = headers.get(HeaderName::LinkupDestination) {
if let Some(service) = config.services.get(destination_service) {
let target = redirect(target.clone(), &service.origin, Some(path.to_string()));
if let Some(service) = config.get_service(destination_service) {
let target = redirect(target.clone(), &service.location, Some(path.to_string()));
return Some(TargetService {
name: destination_service.to_string(),
url: target.to_string(),
});
}
}

let url_target = config.domains.get(&get_target_domain(url, session_name));
let url_target = config.get_domain(&get_target_domain(url, session_name));

// Forwarded hosts persist over the tunnel
let forwarded_host_target = config.domains.get(&get_target_domain(
let forwarded_host_target = config.get_domain(&get_target_domain(
headers.get_or_default(HeaderName::ForwardedHost, "does-not-exist"),
session_name,
));

// This is more for e2e tests to work
let referer_target = config.domains.get(&get_target_domain(
let referer_target = config.get_domain(&get_target_domain(
headers.get_or_default(HeaderName::Referer, "does-not-exist"),
session_name,
));

// This one is for redirects, where the referer doesn't exist
let origin_target = config.domains.get(&get_target_domain(
let origin_target = config.get_domain(&get_target_domain(
headers.get_or_default(HeaderName::Origin, "does-not-exist"),
session_name,
));
Expand All @@ -203,30 +205,34 @@ pub fn get_target_service(
};

if let Some(domain) = target_domain {
let service_name = domain
.routes
.iter()
.find_map(|route| {
if route.path.is_match(path) {
Some(route.service.clone())
} else {
None
}
})
.unwrap_or_else(|| domain.default_service.clone());
let service_name = match &domain.routes {
Some(routes) => routes
.iter()
.find_map(|route| {
if route.path.is_match(path) {
Some(route.service.clone())
} else {
None
}
})
.unwrap_or_else(|| domain.default_service.clone()),
None => domain.default_service.clone(),
};

if let Some(service) = config.services.get(&service_name) {
if let Some(service) = config.get_service(&service_name) {
let mut new_path = path.to_string();
for modifier in &service.rewrites {
if modifier.source.is_match(&new_path) {
new_path = modifier
.source
.replace_all(&new_path, &modifier.target)
.to_string();
if let Some(rewrites) = &service.rewrites {
for modifier in rewrites {
if modifier.source.is_match(&new_path) {
new_path = modifier
.source
.replace_all(&new_path, &modifier.target)
.to_string();
}
}
}

let target = redirect(target, &service.origin, Some(new_path));
let target = redirect(target, &service.location, Some(new_path));
return Some(TargetService {
name: service_name,
url: target.to_string(),
Expand Down
Loading
Loading