Compare commits

..
8 changed files with 205 additions and 117 deletions
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## v1.7.82-alpha (2026-05-22)
- Saleor storefront proxying now forwards `X-Forwarded-Host`, fixing Next.js Server Actions requests that compared the browser origin with the internal `storefront-app:3000` upstream host.
- Saleor storefront media now routes `/thumbnail/` and `/media/` through the same `9011` proxy to the Saleor API, fixing product image optimizer failures caused by `localhost:8000` media URLs.
- The Saleor storefront container receives an explicit internal media origin so rewritten media URLs resolve inside the Podman network without exposing private API ports to browsers.
- Validation passed with `cargo fmt --all --check --manifest-path core/Cargo.toml`, `cargo check -p archipelago --manifest-path core/Cargo.toml`, and live checks on `100.114.134.21` for storefront HTML, static assets, GraphQL, media redirects, and optimized product images.
## v1.7.81-alpha (2026-05-21)
- Saleor storefront installs now use the prebuilt registry image instead of building the Next.js app on-device, avoiding Podman build failures during stack installation.
- Existing Saleor stacks are repaired on adoption by recreating missing storefront containers, forcing the storefront app to bind `0.0.0.0:3000`, and resolving nginx upstreams dynamically after container restarts.
- The shipped Saleor storefront image now includes public assets and omits Vercel-only Speed Insights injection, fixing broken static asset responses and the local `/_vercel/speed-insights/script.js` browser warning.
- Validation passed with `cargo fmt --all --check --manifest-path core/Cargo.toml`, `cargo check -p archipelago --manifest-path core/Cargo.toml`, and live checks on `100.114.134.21` for `9011` storefront, static assets, and proxied GraphQL.
## v1.7.80-alpha (2026-05-21)
- Saleor storefront proxying now falls back to the direct request scheme when no forwarded protocol header is present, fixing direct `http://node:9011` launches that could generate an invalid same-origin GraphQL URL.
+1 -1
View File
@@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "archipelago"
version = "1.7.79-alpha"
version = "1.7.81-alpha"
dependencies = [
"anyhow",
"archipelago-container",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "archipelago"
version = "1.7.80-alpha"
version = "1.7.82-alpha"
edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"]
+152 -80
View File
@@ -76,6 +76,10 @@ async fn adopt_stack_if_exists(
async fn repair_stack_before_adopt(stack_name: &str) {
match stack_name {
"saleor" => {
repair_saleor_network_aliases().await;
let _ = start_saleor_storefront_containers().await;
}
"btcpay" | "btcpay-server" => {
let _ = tokio::process::Command::new("sudo")
.args([
@@ -99,7 +103,6 @@ async fn repair_stack_before_adopt(stack_name: &str) {
}
"indeedhub" => repair_indeedhub_network_aliases().await,
"netbird" => repair_netbird_unified_origin().await,
"saleor" => repair_saleor_network_aliases().await,
_ => {}
}
}
@@ -275,6 +278,53 @@ async fn repair_saleor_network_aliases() {
}
}
async fn start_saleor_storefront_containers() -> Result<()> {
let names = podman_container_names().await?;
if !names.iter().any(|name| name == "saleor-storefront-app") {
pull_image_with_retry(SALEOR_STOREFRONT_IMAGE).await?;
let mut storefront_cmd = saleor_storefront_app_command();
run_required_stack_command("saleor", "create storefront app", &mut storefront_cmd).await?;
} else {
let _ = tokio::process::Command::new("podman")
.args(["start", "saleor-storefront-app"])
.output()
.await;
}
write_saleor_storefront_proxy_config().await?;
if !names.iter().any(|name| name == "saleor-storefront") {
let mut proxy_cmd = saleor_storefront_proxy_command();
run_required_stack_command("saleor", "create storefront proxy", &mut proxy_cmd).await?;
} else {
let _ = tokio::process::Command::new("podman")
.args(["start", "saleor-storefront"])
.output()
.await;
}
wait_for_stack_containers(
"saleor",
&["saleor-storefront-app", "saleor-storefront"],
60,
)
.await
}
async fn podman_container_names() -> Result<Vec<String>> {
let output = tokio::process::Command::new("podman")
.args(["ps", "-a", "--format", "{{.Names}}"])
.output()
.await
.context("Failed to list containers")?;
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.map(str::trim)
.filter(|name| !name.is_empty())
.map(ToOwned::to_owned)
.collect())
}
async fn run_required_stack_command(
stack_name: &str,
label: &str,
@@ -447,9 +497,7 @@ const NETBIRD_SERVER_IMAGE: &str = "docker.io/netbirdio/netbird-server:0.71.2";
const NETBIRD_PROXY_IMAGE: &str = "docker.io/library/nginx:1.27-alpine";
const SALEOR_API_IMAGE: &str = "ghcr.io/saleor/saleor:3.23";
const SALEOR_DASHBOARD_IMAGE: &str = "ghcr.io/saleor/saleor-dashboard:3.23";
const SALEOR_STOREFRONT_IMAGE: &str = "localhost/archipelago/saleor-storefront:6eb0b97";
const SALEOR_STOREFRONT_CONTEXT: &str =
"https://github.com/saleor/storefront.git#6eb0b97b25bd4344d8139515a1cabf763d703b39";
const SALEOR_STOREFRONT_IMAGE: &str = "146.59.87.168:3000/lfg2025/saleor-storefront:6eb0b97";
const SALEOR_POSTGRES_IMAGE: &str = "docker.io/library/postgres:15-alpine";
const SALEOR_VALKEY_IMAGE: &str = "docker.io/valkey/valkey:8.1-alpine";
const SALEOR_JAEGER_IMAGE: &str = "docker.io/jaegertracing/jaeger:latest";
@@ -457,6 +505,14 @@ const SALEOR_MAILPIT_IMAGE: &str = "docker.io/axllent/mailpit:latest";
/// Pull an image with retry and exponential backoff (3 attempts).
async fn pull_image_with_retry(image: &str) -> Result<()> {
let exists = tokio::process::Command::new("podman")
.args(["image", "exists", image])
.status()
.await;
if matches!(exists, Ok(status) if status.success()) {
return Ok(());
}
const MAX_ATTEMPTS: u32 = 3;
const BACKOFF_SECS: [u64; 3] = [5, 15, 45];
@@ -501,6 +557,75 @@ async fn pull_image_with_retry(image: &str) -> Result<()> {
unreachable!()
}
fn saleor_storefront_app_command() -> tokio::process::Command {
let mut cmd = tokio::process::Command::new("podman");
cmd.args([
"run",
"-d",
"--name",
"saleor-storefront-app",
"--network",
"saleor-net",
"--network-alias",
"storefront-app",
"--restart=unless-stopped",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=DAC_OVERRIDE",
"--cap-add=FOWNER",
"--cap-add=SETGID",
"--cap-add=SETUID",
"--security-opt=no-new-privileges:true",
"--memory=512m",
"--pids-limit=2048",
"-e",
"NEXT_PUBLIC_SALEOR_API_URL=http://api:8000/graphql/",
"-e",
"NEXT_PUBLIC_SALEOR_MEDIA_URL=http://api:8000/",
"-e",
"NEXT_PUBLIC_STOREFRONT_URL=http://localhost:9011",
"-e",
"NEXT_PUBLIC_DEFAULT_CHANNEL=default-channel",
"-e",
"HOSTNAME=0.0.0.0",
"-e",
"PORT=3000",
SALEOR_STOREFRONT_IMAGE,
]);
cmd
}
fn saleor_storefront_proxy_command() -> tokio::process::Command {
let mut cmd = tokio::process::Command::new("podman");
cmd.args([
"run",
"-d",
"--name",
"saleor-storefront",
"--network",
"saleor-net",
"--network-alias",
"storefront",
"--restart=unless-stopped",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=DAC_OVERRIDE",
"--cap-add=FOWNER",
"--cap-add=NET_BIND_SERVICE",
"--cap-add=SETGID",
"--cap-add=SETUID",
"--security-opt=no-new-privileges:true",
"--memory=128m",
"--pids-limit=1024",
"-p",
"9011:80",
"-v",
"/var/lib/archipelago/saleor-storefront/nginx.conf:/etc/nginx/conf.d/default.conf:ro",
NETBIRD_PROXY_IMAGE,
]);
cmd
}
impl RpcHandler {
/// Install Immich stack (postgres + redis + server).
pub(super) async fn install_immich_stack(&self) -> Result<serde_json::Value> {
@@ -1678,6 +1803,7 @@ impl RpcHandler {
SALEOR_VALKEY_IMAGE,
SALEOR_API_IMAGE,
SALEOR_DASHBOARD_IMAGE,
SALEOR_STOREFRONT_IMAGE,
SALEOR_JAEGER_IMAGE,
SALEOR_MAILPIT_IMAGE,
];
@@ -1754,7 +1880,6 @@ impl RpcHandler {
let dashboard_origin = format!("http://{}:9010", host_ip);
let dashboard_url = format!("{}/", dashboard_origin);
let api_url = format!("http://{}:8000/graphql/", host_ip);
let internal_api_url = "http://api:8000/graphql/";
let storefront_origin = format!("http://{}:9011", host_ip);
let allowed_hosts = format!("localhost,127.0.0.1,api,saleor-api,{}", host_ip);
let allowed_client_hosts = format!(
@@ -2079,82 +2204,11 @@ user.save()
]);
run_required_stack_command("saleor", "create dashboard", &mut dashboard_cmd).await?;
let mut storefront_build_cmd = tokio::process::Command::new("podman");
storefront_build_cmd.args([
"build",
"--network",
"saleor-net",
"--pull=always",
"-t",
SALEOR_STOREFRONT_IMAGE,
"--build-arg",
&format!("NEXT_PUBLIC_SALEOR_API_URL={}", internal_api_url),
"--build-arg",
&format!("NEXT_PUBLIC_STOREFRONT_URL={}", storefront_origin),
"--build-arg",
"NEXT_PUBLIC_DEFAULT_CHANNEL=default-channel",
SALEOR_STOREFRONT_CONTEXT,
]);
run_required_stack_command("saleor", "build storefront", &mut storefront_build_cmd).await?;
let mut storefront_cmd = tokio::process::Command::new("podman");
storefront_cmd.args([
"run",
"-d",
"--name",
"saleor-storefront-app",
"--network",
"saleor-net",
"--network-alias",
"storefront-app",
"--restart=unless-stopped",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=DAC_OVERRIDE",
"--cap-add=FOWNER",
"--cap-add=SETGID",
"--cap-add=SETUID",
"--security-opt=no-new-privileges:true",
"--memory=512m",
"--pids-limit=2048",
"-e",
&format!("NEXT_PUBLIC_SALEOR_API_URL={}", internal_api_url),
"-e",
&format!("NEXT_PUBLIC_STOREFRONT_URL={}", storefront_origin),
"-e",
"NEXT_PUBLIC_DEFAULT_CHANNEL=default-channel",
SALEOR_STOREFRONT_IMAGE,
]);
let mut storefront_cmd = saleor_storefront_app_command();
run_required_stack_command("saleor", "create storefront app", &mut storefront_cmd).await?;
write_saleor_storefront_proxy_config().await?;
let mut storefront_proxy_cmd = tokio::process::Command::new("podman");
storefront_proxy_cmd.args([
"run",
"-d",
"--name",
"saleor-storefront",
"--network",
"saleor-net",
"--network-alias",
"storefront",
"--restart=unless-stopped",
"--cap-drop=ALL",
"--cap-add=CHOWN",
"--cap-add=DAC_OVERRIDE",
"--cap-add=FOWNER",
"--cap-add=NET_BIND_SERVICE",
"--cap-add=SETGID",
"--cap-add=SETUID",
"--security-opt=no-new-privileges:true",
"--memory=128m",
"--pids-limit=1024",
"-p",
"9011:80",
"-v",
"/var/lib/archipelago/saleor-storefront/nginx.conf:/etc/nginx/conf.d/default.conf:ro",
NETBIRD_PROXY_IMAGE,
]);
let mut storefront_proxy_cmd = saleor_storefront_proxy_command();
run_required_stack_command(
"saleor",
"create storefront proxy",
@@ -2242,21 +2296,39 @@ async fn write_saleor_storefront_proxy_config() -> Result<()> {
server {
listen 80;
server_name _;
resolver 10.89.4.1 valid=10s ipv6=off;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
location ^~ /graphql/ {
proxy_pass http://api:8000/graphql/;
set $saleor_api http://api:8000/graphql/;
proxy_pass $saleor_api;
proxy_set_header Host api;
proxy_set_header Origin "";
}
location ^~ /thumbnail/ {
set $saleor_media http://api:8000$request_uri;
proxy_pass $saleor_media;
proxy_set_header Host api;
proxy_set_header Origin "";
}
location ^~ /media/ {
set $saleor_media http://api:8000$request_uri;
proxy_pass $saleor_media;
proxy_set_header Host api;
proxy_set_header Origin "";
}
location / {
proxy_pass http://storefront-app:3000;
set $saleor_storefront_app http://storefront-app:3000;
proxy_pass $saleor_storefront_app;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Accept-Encoding "";
+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "neode-ui",
"version": "1.7.80-alpha",
"version": "1.7.82-alpha",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "neode-ui",
"version": "1.7.80-alpha",
"version": "1.7.82-alpha",
"dependencies": {
"@types/dompurify": "^3.0.5",
"@vue-leaflet/vue-leaflet": "^0.10.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "neode-ui",
"private": true,
"version": "1.7.80-alpha",
"version": "1.7.82-alpha",
"type": "module",
"scripts": {
"start": "./start-dev.sh",
+17 -16
View File
@@ -1,27 +1,28 @@
{
"version": "1.7.80-alpha",
"release_date": "2026-05-21",
"version": "1.7.82-alpha",
"release_date": "2026-05-22",
"changelog": [
"Saleor storefront proxying now falls back to the direct request scheme when no forwarded protocol header is present, fixing direct `http://node:9011` launches that could generate an invalid same-origin GraphQL URL.",
"The Saleor storefront release path keeps public proxy support intact by still honoring forwarded HTTPS headers for Nginx Proxy Manager domains while repairing local/direct port launches.",
"Validation passed with `cargo fmt --check` and `cargo check` for the Archipelago backend before release staging."
"Saleor storefront proxying now forwards `X-Forwarded-Host`, fixing Next.js Server Actions requests that compared the browser origin with the internal `storefront-app:3000` upstream host.",
"Saleor storefront media now routes `/thumbnail/` and `/media/` through the same `9011` proxy to the Saleor API, fixing product image optimizer failures caused by `localhost:8000` media URLs.",
"The Saleor storefront container receives an explicit internal media origin so rewritten media URLs resolve inside the Podman network without exposing private API ports to browsers.",
"Validation passed with `cargo fmt --all --check --manifest-path core/Cargo.toml`, `cargo check -p archipelago --manifest-path core/Cargo.toml`, and live checks on `100.114.134.21` for storefront HTML, static assets, GraphQL, media redirects, and optimized product images."
],
"components": [
{
"name": "archipelago",
"current_version": "1.7.80-alpha",
"new_version": "1.7.80-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.80-alpha/archipelago",
"sha256": "6e2d796e9395b578944991970b7f07d27b4185d3aae2beb0af10e47d212f5ea7",
"size_bytes": 43071632
"current_version": "1.7.82-alpha",
"new_version": "1.7.82-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.82-alpha/archipelago",
"sha256": "d03ef4ab8a2c01fa5c6e98e4f1acf0e39fecfa260bb0c47cbe01405c5ed5a70d",
"size_bytes": 43118248
},
{
"name": "archipelago-frontend-1.7.80-alpha.tar.gz",
"current_version": "1.7.80-alpha",
"new_version": "1.7.80-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.80-alpha/archipelago-frontend-1.7.80-alpha.tar.gz",
"sha256": "65a75fbe732635ebc658eba1f6d36a7e158de3b88ef49db0a23202d0564e30c0",
"size_bytes": 166481159
"name": "archipelago-frontend-1.7.82-alpha.tar.gz",
"current_version": "1.7.82-alpha",
"new_version": "1.7.82-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.82-alpha/archipelago-frontend-1.7.82-alpha.tar.gz",
"sha256": "be53e82383c3a8480ae9d45870ebe8ab47f6b07e90b16202cba1ee97530364b0",
"size_bytes": 166485959
}
]
}
+17 -16
View File
@@ -1,27 +1,28 @@
{
"version": "1.7.80-alpha",
"release_date": "2026-05-21",
"version": "1.7.82-alpha",
"release_date": "2026-05-22",
"changelog": [
"Saleor storefront proxying now falls back to the direct request scheme when no forwarded protocol header is present, fixing direct `http://node:9011` launches that could generate an invalid same-origin GraphQL URL.",
"The Saleor storefront release path keeps public proxy support intact by still honoring forwarded HTTPS headers for Nginx Proxy Manager domains while repairing local/direct port launches.",
"Validation passed with `cargo fmt --check` and `cargo check` for the Archipelago backend before release staging."
"Saleor storefront proxying now forwards `X-Forwarded-Host`, fixing Next.js Server Actions requests that compared the browser origin with the internal `storefront-app:3000` upstream host.",
"Saleor storefront media now routes `/thumbnail/` and `/media/` through the same `9011` proxy to the Saleor API, fixing product image optimizer failures caused by `localhost:8000` media URLs.",
"The Saleor storefront container receives an explicit internal media origin so rewritten media URLs resolve inside the Podman network without exposing private API ports to browsers.",
"Validation passed with `cargo fmt --all --check --manifest-path core/Cargo.toml`, `cargo check -p archipelago --manifest-path core/Cargo.toml`, and live checks on `100.114.134.21` for storefront HTML, static assets, GraphQL, media redirects, and optimized product images."
],
"components": [
{
"name": "archipelago",
"current_version": "1.7.80-alpha",
"new_version": "1.7.80-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.80-alpha/archipelago",
"sha256": "6e2d796e9395b578944991970b7f07d27b4185d3aae2beb0af10e47d212f5ea7",
"size_bytes": 43071632
"current_version": "1.7.82-alpha",
"new_version": "1.7.82-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.82-alpha/archipelago",
"sha256": "d03ef4ab8a2c01fa5c6e98e4f1acf0e39fecfa260bb0c47cbe01405c5ed5a70d",
"size_bytes": 43118248
},
{
"name": "archipelago-frontend-1.7.80-alpha.tar.gz",
"current_version": "1.7.80-alpha",
"new_version": "1.7.80-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.80-alpha/archipelago-frontend-1.7.80-alpha.tar.gz",
"sha256": "65a75fbe732635ebc658eba1f6d36a7e158de3b88ef49db0a23202d0564e30c0",
"size_bytes": 166481159
"name": "archipelago-frontend-1.7.82-alpha.tar.gz",
"current_version": "1.7.82-alpha",
"new_version": "1.7.82-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.82-alpha/archipelago-frontend-1.7.82-alpha.tar.gz",
"sha256": "be53e82383c3a8480ae9d45870ebe8ab47f6b07e90b16202cba1ee97530364b0",
"size_bytes": 166485959
}
]
}