mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-17 21:15:32 +09:00
fix(app-proxy): tell the web client its release channel (#3448)
This commit is contained in:
1 parent
83bb9d89c8
commit
d514501d57
3 files changed
+81
-1
No files matched your search
@@ -362,6 +362,7 @@ pub struct AppProxyConfig {
|
||||
pub csp: CspConfig,
|
||||
pub same_origin_hosts: Vec<String>,
|
||||
pub manifest_scope_extensions: Vec<String>,
|
||||
pub release_channel: Option<&'static str>,
|
||||
pub self_hosted: bool,
|
||||
}
|
||||
|
||||
@@ -477,11 +478,31 @@ impl AppProxyConfig {
|
||||
"FLUXER_APP_PROXY_MANIFEST_SCOPE_EXTENSIONS",
|
||||
&cfg::read_env("FLUXER_APP_PROXY_MANIFEST_SCOPE_EXTENSIONS", ""),
|
||||
),
|
||||
release_channel: parse_release_channel(
|
||||
"RELEASE_CHANNEL",
|
||||
&cfg::read_env("RELEASE_CHANNEL", ""),
|
||||
),
|
||||
self_hosted: cfg::read_bool_env("FLUXER_SELF_HOSTED", false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_release_channel(name: &'static str, raw: &str) -> Option<&'static str> {
|
||||
match raw.trim().to_ascii_lowercase().as_str() {
|
||||
"" => None,
|
||||
"stable" => Some("stable"),
|
||||
"canary" => Some("canary"),
|
||||
_ => {
|
||||
warn_invalid(&InvalidAppProxyEnvironmentError::new(
|
||||
name,
|
||||
raw,
|
||||
"stable or canary",
|
||||
));
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_manifest_scope_extensions(name: &'static str, raw: &str) -> Vec<String> {
|
||||
let mut origins: Vec<String> = Vec::new();
|
||||
for value in raw
|
||||
@@ -591,6 +612,21 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn release_channel_accepts_only_stable_or_canary() {
|
||||
assert_eq!(
|
||||
parse_release_channel("TEST_CHANNEL", " Stable "),
|
||||
Some("stable")
|
||||
);
|
||||
assert_eq!(
|
||||
parse_release_channel("TEST_CHANNEL", "canary"),
|
||||
Some("canary")
|
||||
);
|
||||
assert_eq!(parse_release_channel("TEST_CHANNEL", ""), None);
|
||||
assert_eq!(parse_release_channel("TEST_CHANNEL", "development"), None);
|
||||
assert_eq!(parse_release_channel("TEST_CHANNEL", "\"><script>"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn manifest_scope_extensions_are_normalised_and_deduplicated() {
|
||||
assert_eq!(
|
||||
|
||||
@@ -27,6 +27,7 @@ const SHARED_SHELL_CACHE_CONTROL: &str = "public, max-age=0, s-maxage=1";
|
||||
const CSP_NONCE_PLACEHOLDER_ATTRIBUTE: &str = r#" nonce="{{CSP_NONCE_PLACEHOLDER}}""#;
|
||||
const MEDIA_PRECONNECT_TAG: &str = r#"<link rel="preconnect" href="{{MEDIA_ENDPOINT}}">"#;
|
||||
const SAME_ORIGIN_API_META_TAG: &str = r#"<meta name="fluxer-api-origin" content="self">"#;
|
||||
const RELEASE_CHANNEL_META_NAME: &str = "fluxer-release-channel";
|
||||
const HEAD_CLOSE_TAG: &str = "</head>";
|
||||
const TITLE_START: &str = "<title";
|
||||
const TITLE_END: &str = "</title>";
|
||||
@@ -204,6 +205,10 @@ async fn serve_spa_index(state: &AppState, headers: &HeaderMap, request_path: &s
|
||||
} else {
|
||||
raw_html
|
||||
};
|
||||
let raw_html = match state.config.release_channel {
|
||||
Some(channel) => mark_release_channel(&raw_html, channel),
|
||||
None => raw_html,
|
||||
};
|
||||
|
||||
let dev_buster = should_bust_dev_assets.then(current_dev_asset_cache_buster);
|
||||
let html = match render_spa_document(
|
||||
@@ -260,6 +265,17 @@ fn mark_same_origin_api(html: &str) -> String {
|
||||
)
|
||||
}
|
||||
|
||||
fn mark_release_channel(html: &str, channel: &str) -> String {
|
||||
html.replacen(
|
||||
HEAD_CLOSE_TAG,
|
||||
&format!(
|
||||
r#"<meta name="{RELEASE_CHANNEL_META_NAME}" content="{channel}">
|
||||
{HEAD_CLOSE_TAG}"#
|
||||
),
|
||||
1,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SpaDocumentSizeLimitError {
|
||||
attempted_bytes: usize,
|
||||
@@ -1166,6 +1182,24 @@ mod tests {
|
||||
assert!(!served.contains(SAME_ORIGIN_API_META_TAG));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn the_configured_release_channel_is_marked_in_the_document() {
|
||||
let mut state = spa_state_serving(SHIPPED_APP_SHELL);
|
||||
let served = read_document(serve_spa_index(&state, &HeaderMap::new(), "/").await).await;
|
||||
assert!(!served.contains(RELEASE_CHANNEL_META_NAME));
|
||||
|
||||
for channel in ["stable", "canary"] {
|
||||
Arc::make_mut(&mut state.config).release_channel = Some(channel);
|
||||
let response = serve_spa_index(&state, &HeaderMap::new(), "/").await;
|
||||
let policy = policy_of(&response);
|
||||
let served = read_document(response).await;
|
||||
let tag = format!(r#"<meta name="fluxer-release-channel" content="{channel}">"#);
|
||||
assert_eq!(served.matches(&tag).count(), 1);
|
||||
assert!(served.find(&tag).unwrap() < served.find("</head>").unwrap());
|
||||
assert_every_inline_script_is_granted(&served, &policy);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn the_configured_media_endpoint_is_preconnected() {
|
||||
let mut state = assemble_spa_state(
|
||||
|
||||
Reference in new issue
Block a user