Compare commits

...

2 Commits

Author SHA1 Message Date
archipelago
e61c757633 chore: release v1.7.78-alpha 2026-05-20 20:53:23 -04:00
archipelago
cc1f8fba72 fix(apps): stabilize saleor and netbird release paths 2026-05-20 20:38:52 -04:00
15 changed files with 220 additions and 78 deletions

View File

@ -132,6 +132,16 @@ fun WebViewScreen(
AndroidView( AndroidView(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
factory = { context -> factory = { context ->
fun openExternalUrl(url: String) {
try {
val intent = android.content.Intent(
android.content.Intent.ACTION_VIEW,
android.net.Uri.parse(url),
)
context.startActivity(intent)
} catch (_: Exception) {}
}
WebView(context).apply { WebView(context).apply {
layoutParams = ViewGroup.LayoutParams( layoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@ -220,13 +230,7 @@ fun WebViewScreen(
// Keep navigation within the Archipelago server // Keep navigation within the Archipelago server
if (url.startsWith(serverUrl)) return false if (url.startsWith(serverUrl)) return false
// Open external URLs in the system browser // Open external URLs in the system browser
try { openExternalUrl(url)
val intent = android.content.Intent(
android.content.Intent.ACTION_VIEW,
android.net.Uri.parse(url),
)
context.startActivity(intent)
} catch (_: Exception) {}
return true return true
} }
} }
@ -243,18 +247,30 @@ fun WebViewScreen(
isUserGesture: Boolean, isUserGesture: Boolean,
resultMsg: android.os.Message?, resultMsg: android.os.Message?,
): Boolean { ): Boolean {
// Extract the URL from the hit test val transport = resultMsg?.obj as? WebView.WebViewTransport
val data = view?.hitTestResult?.extra ?: return false
if (data != null) {
try { val popup = WebView(context).apply {
val intent = android.content.Intent( settings.javaScriptEnabled = true
android.content.Intent.ACTION_VIEW, webViewClient = object : WebViewClient() {
android.net.Uri.parse(data), override fun shouldOverrideUrlLoading(
) view: WebView?,
context.startActivity(intent) request: WebResourceRequest?,
} catch (_: Exception) {} ): Boolean {
val url = request?.url?.toString() ?: return true
openExternalUrl(url)
return true
} }
return false
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
if (url != null) openExternalUrl(url)
view?.stopLoading()
}
}
}
transport.webView = popup
resultMsg.sendToTarget()
return true
} }
} }

View File

@ -1,5 +1,15 @@
# Changelog # Changelog
## v1.7.78-alpha (2026-05-20)
- Public Nginx Proxy Manager hosts for Saleor now keep browser GraphQL calls same-origin at `/graphql/` and proxy them to the local API on `8000`, fixing `Failed to fetch` when a public domain such as `noderunner.shop` was loaded from devices that cannot reach the node's private LAN/tailnet API address.
- Saleor's validated stack changes are now release-ready: dashboard origins on port `9010` are explicitly allowed for dashboard/API calls, preserving the working test-node install path for production nodes.
- NetBird launches now stay pinned to the unified dashboard/proxy origin on port `8087` instead of following stale runtime-discovered server URLs on `8086`.
- NetBird's local nginx proxy now routes browser API, OAuth, relay, and WebSocket traffic through `host.containers.internal:8086` instead of a hard-coded rootless Podman gateway IP, and includes the upstream `management.ProxyService` gRPC path.
- The mobile credentials interstitial now keeps credential lists scrollable and action buttons reachable in both My Apps and the mobile app icon grid.
- Android WebView popup windows now hand external popup URLs to the system browser, covering app login/signup flows that open secondary windows.
- Validation passed with `git diff --check`, `cargo check -p archipelago`, and the focused `npm test -- src/views/appSession/__tests__/appSessionConfig.test.ts` suite.
## v1.7.77-alpha (2026-05-20) ## v1.7.77-alpha (2026-05-20)
- Saleor first-use now exposes generated credentials through Archipelago instead of leaving users at an unexplained dashboard login: App Details shows copyable `admin@example.com` credentials, and My Apps/mobile icon launches show a pre-launch credentials modal. - Saleor first-use now exposes generated credentials through Archipelago instead of leaving users at an unexplained dashboard login: App Details shows copyable `admin@example.com` credentials, and My Apps/mobile icon launches show a pre-launch credentials modal.

2
core/Cargo.lock generated
View File

@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]] [[package]]
name = "archipelago" name = "archipelago"
version = "1.7.77-alpha" version = "1.7.78-alpha"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"archipelago-container", "archipelago-container",

View File

@ -1,6 +1,6 @@
[package] [package]
name = "archipelago" name = "archipelago"
version = "1.7.77-alpha" version = "1.7.78-alpha"
edition = "2021" edition = "2021"
description = "Archipelago Bitcoin Node OS - Native backend" description = "Archipelago Bitcoin Node OS - Native backend"
authors = ["Archipelago Team"] authors = ["Archipelago Team"]

View File

@ -271,6 +271,7 @@ async fn repair_saleor_network_aliases() {
.output() .output()
.await; .await;
} }
} }
async fn run_required_stack_command( async fn run_required_stack_command(
@ -1740,9 +1741,14 @@ impl RpcHandler {
let secret_key = super::config::read_or_generate_secret("saleor-secret-key").await; let secret_key = super::config::read_or_generate_secret("saleor-secret-key").await;
let admin_pass = super::config::read_or_generate_secret("saleor-admin-password").await; let admin_pass = super::config::read_or_generate_secret("saleor-admin-password").await;
let host_ip = &self.config.host_ip; let host_ip = &self.config.host_ip;
let dashboard_url = format!("http://{}:9010/", host_ip); let dashboard_origin = format!("http://{}:9010", host_ip);
let dashboard_url = format!("{}/", dashboard_origin);
let api_url = format!("http://{}:8000/graphql/", host_ip); let api_url = format!("http://{}:8000/graphql/", host_ip);
let allowed_hosts = format!("localhost,127.0.0.1,api,saleor-api,{}", host_ip); let allowed_hosts = format!("localhost,127.0.0.1,api,saleor-api,{}", host_ip);
let allowed_client_hosts = format!(
"{},http://localhost:9010,http://127.0.0.1:9010",
dashboard_origin
);
let database_url = format!("postgres://saleor:{}@db/saleor", db_pass); let database_url = format!("postgres://saleor:{}@db/saleor", db_pass);
let mut db_cmd = tokio::process::Command::new("podman"); let mut db_cmd = tokio::process::Command::new("podman");
@ -1884,6 +1890,10 @@ impl RpcHandler {
"-e".to_string(), "-e".to_string(),
format!("DASHBOARD_URL={}", dashboard_url), format!("DASHBOARD_URL={}", dashboard_url),
"-e".to_string(), "-e".to_string(),
format!("ALLOWED_CLIENT_HOSTS={}", allowed_client_hosts),
"-e".to_string(),
format!("ALLOWED_GRAPHQL_ORIGINS={}", allowed_client_hosts),
"-e".to_string(),
format!("ALLOWED_HOSTS={}", allowed_hosts), format!("ALLOWED_HOSTS={}", allowed_hosts),
]; ];
@ -2182,10 +2192,9 @@ LETSENCRYPT_DOMAIN=none
listen 80; listen 80;
server_name _; server_name _;
# Route API/auth through the host-published server port. Rootless Podman # Route browser API/auth through the host-published server port. Rootless
# can give netbird-server a new container IP on restart while nginx keeps # Podman can give netbird-server a new container IP on restart while nginx
# an old resolved address, which breaks login with 502s. # keeps an old resolved address, which breaks login with 502s.
set $netbird_server http://169.254.1.2:8086;
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Real-IP $remote_addr;
@ -2194,17 +2203,17 @@ LETSENCRYPT_DOMAIN=none
proxy_http_version 1.1; proxy_http_version 1.1;
location ~ ^/(relay|ws-proxy/) {{ location ~ ^/(relay|ws-proxy/) {{
proxy_pass $netbird_server; proxy_pass http://host.containers.internal:8086;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
proxy_read_timeout 1d; proxy_read_timeout 1d;
}} }}
location ~ ^/(api|oauth2)(/|$) {{ location ~ ^/(api|oauth2)(/|$) {{
proxy_pass $netbird_server; proxy_pass http://host.containers.internal:8086;
}} }}
location ~ ^/(signalexchange\.SignalExchange|management\.ManagementService)/ {{ location ~ ^/(signalexchange\.SignalExchange|management\.ManagementService|management\.ProxyService)/ {{
grpc_pass grpc://netbird-server:80; grpc_pass grpc://netbird-server:80;
grpc_read_timeout 1d; grpc_read_timeout 1d;
grpc_send_timeout 1d; grpc_send_timeout 1d;

View File

@ -1,12 +1,12 @@
{ {
"name": "neode-ui", "name": "neode-ui",
"version": "1.7.77-alpha", "version": "1.7.78-alpha",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "neode-ui", "name": "neode-ui",
"version": "1.7.77-alpha", "version": "1.7.78-alpha",
"dependencies": { "dependencies": {
"@types/dompurify": "^3.0.5", "@types/dompurify": "^3.0.5",
"@vue-leaflet/vue-leaflet": "^0.10.1", "@vue-leaflet/vue-leaflet": "^0.10.1",

View File

@ -1,7 +1,7 @@
{ {
"name": "neode-ui", "name": "neode-ui",
"private": true, "private": true,
"version": "1.7.77-alpha", "version": "1.7.78-alpha",
"type": "module", "type": "module",
"scripts": { "scripts": {
"start": "./start-dev.sh", "start": "./start-dev.sh",

View File

@ -179,7 +179,7 @@
class="fixed inset-0 z-[2700] flex items-end justify-center bg-black/60 backdrop-blur-md p-0 md:items-center md:p-6" class="fixed inset-0 z-[2700] flex items-end justify-center bg-black/60 backdrop-blur-md p-0 md:items-center md:p-6"
@click.self="closeCredentialModal" @click.self="closeCredentialModal"
> >
<div class="sideload-modal"> <div class="sideload-modal credential-modal">
<div class="flex items-start justify-between gap-4 mb-5"> <div class="flex items-start justify-between gap-4 mb-5">
<div> <div>
<h2 class="text-lg font-semibold text-white">{{ credentialModal.title }}</h2> <h2 class="text-lg font-semibold text-white">{{ credentialModal.title }}</h2>
@ -187,7 +187,7 @@
</div> </div>
<button type="button" class="sideload-close-btn" aria-label="Close" @click="closeCredentialModal">&times;</button> <button type="button" class="sideload-close-btn" aria-label="Close" @click="closeCredentialModal">&times;</button>
</div> </div>
<div class="space-y-3"> <div class="credential-modal-body space-y-3">
<div v-for="cred in credentialModal.credentials" :key="cred.label" class="rounded-lg border border-white/10 bg-white/[0.04] p-3"> <div v-for="cred in credentialModal.credentials" :key="cred.label" class="rounded-lg border border-white/10 bg-white/[0.04] p-3">
<div class="flex items-center justify-between gap-3 mb-1"> <div class="flex items-center justify-between gap-3 mb-1">
<span class="text-white/60 text-xs uppercase tracking-wide">{{ cred.label }}</span> <span class="text-white/60 text-xs uppercase tracking-wide">{{ cred.label }}</span>
@ -196,9 +196,9 @@
<p class="font-mono text-sm text-white break-all">{{ cred.value }}</p> <p class="font-mono text-sm text-white break-all">{{ cred.value }}</p>
</div> </div>
</div> </div>
<div class="mt-5 flex gap-3"> <div class="credential-modal-actions mt-5 flex flex-col sm:flex-row gap-3">
<button type="button" class="flex-1 glass-button px-4 py-3 rounded-lg" @click="closeCredentialModal">Cancel</button> <button type="button" class="w-full sm:flex-1 glass-button px-4 py-3 rounded-lg" @click="closeCredentialModal">Cancel</button>
<button type="button" class="flex-1 glass-button px-4 py-3 rounded-lg font-semibold" @click="continueCredentialLaunch">Continue to app</button> <button type="button" class="w-full sm:flex-1 glass-button px-4 py-3 rounded-lg font-semibold" @click="continueCredentialLaunch">Continue to app</button>
</div> </div>
</div> </div>
</div> </div>
@ -690,6 +690,18 @@ async function submitSideload() {
} }
.sideload-input::placeholder { color: rgba(255, 255, 255, 0.38); } .sideload-input::placeholder { color: rgba(255, 255, 255, 0.38); }
.sideload-input:focus { border-color: rgba(255, 255, 255, 0.38); } .sideload-input:focus { border-color: rgba(255, 255, 255, 0.38); }
.credential-modal {
display: flex;
flex-direction: column;
}
.credential-modal-body {
min-height: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.credential-modal-actions {
flex-shrink: 0;
}
@media (min-width: 768px) { @media (min-width: 768px) {
.sideload-modal { .sideload-modal {
border-radius: 1.25rem; border-radius: 1.25rem;

View File

@ -18,4 +18,14 @@ describe('appSessionConfig', () => {
expect(resolveAppUrl('mempool')).toBe('http://192.168.1.228:4080') expect(resolveAppUrl('mempool')).toBe('http://192.168.1.228:4080')
expect(resolveAppUrl('indeedhub')).toBe('http://192.168.1.228:7778') expect(resolveAppUrl('indeedhub')).toBe('http://192.168.1.228:7778')
}) })
it('keeps NetBird on the unified dashboard proxy port', () => {
Object.defineProperty(window, 'location', {
value: { hostname: '192.168.1.228' },
writable: true,
configurable: true,
})
expect(resolveAppUrl('netbird', undefined, 'http://localhost:8086')).toBe('http://192.168.1.228:8087')
})
}) })

View File

@ -100,9 +100,7 @@ export const NEW_TAB_APPS = new Set([
export const IFRAME_BLOCKED_APPS = new Set<string>([]) export const IFRAME_BLOCKED_APPS = new Set<string>([])
/** Resolve app URL using direct port mapping (source of truth) */ /** Resolve app URL using direct port mapping (source of truth) */
export function resolveAppUrl(id: string, routeQueryPath?: string, runtimeUrl?: string): string { export function resolveAppUrl(id: string, routeQueryPath?: string, _runtimeUrl?: string): string {
if (id === 'netbird' && runtimeUrl) return runtimeUrl
// External HTTPS apps // External HTTPS apps
const ext = EXTERNAL_URLS[id] const ext = EXTERNAL_URLS[id]
if (ext) return ext if (ext) return ext

View File

@ -73,7 +73,7 @@
<Transition name="fade"> <Transition name="fade">
<div v-if="credentialModal.show" class="fixed inset-0 z-[2700] flex items-end justify-center bg-black/60 backdrop-blur-md p-0 md:items-center md:p-6" @click.self="closeCredentialModal"> <div v-if="credentialModal.show" class="fixed inset-0 z-[2700] flex items-end justify-center bg-black/60 backdrop-blur-md p-0 md:items-center md:p-6" @click.self="closeCredentialModal">
<div class="sideload-modal"> <div class="sideload-modal credential-modal">
<div class="flex items-start justify-between gap-4 mb-5"> <div class="flex items-start justify-between gap-4 mb-5">
<div> <div>
<h2 class="text-lg font-semibold text-white">{{ credentialModal.title }}</h2> <h2 class="text-lg font-semibold text-white">{{ credentialModal.title }}</h2>
@ -81,7 +81,7 @@
</div> </div>
<button type="button" class="sideload-close-btn" aria-label="Close" @click="closeCredentialModal">&times;</button> <button type="button" class="sideload-close-btn" aria-label="Close" @click="closeCredentialModal">&times;</button>
</div> </div>
<div class="space-y-3"> <div class="credential-modal-body space-y-3">
<div v-for="cred in credentialModal.credentials" :key="cred.label" class="rounded-lg border border-white/10 bg-white/[0.04] p-3"> <div v-for="cred in credentialModal.credentials" :key="cred.label" class="rounded-lg border border-white/10 bg-white/[0.04] p-3">
<div class="flex items-center justify-between gap-3 mb-1"> <div class="flex items-center justify-between gap-3 mb-1">
<span class="text-white/60 text-xs uppercase tracking-wide">{{ cred.label }}</span> <span class="text-white/60 text-xs uppercase tracking-wide">{{ cred.label }}</span>
@ -90,9 +90,9 @@
<p class="font-mono text-sm text-white break-all">{{ cred.value }}</p> <p class="font-mono text-sm text-white break-all">{{ cred.value }}</p>
</div> </div>
</div> </div>
<div class="mt-5 flex gap-3"> <div class="credential-modal-actions mt-5 flex flex-col sm:flex-row gap-3">
<button type="button" class="flex-1 glass-button px-4 py-3 rounded-lg" @click="closeCredentialModal">Cancel</button> <button type="button" class="w-full sm:flex-1 glass-button px-4 py-3 rounded-lg" @click="closeCredentialModal">Cancel</button>
<button type="button" class="flex-1 glass-button px-4 py-3 rounded-lg font-semibold" @click="continueCredentialLaunch">Continue to app</button> <button type="button" class="w-full sm:flex-1 glass-button px-4 py-3 rounded-lg font-semibold" @click="continueCredentialLaunch">Continue to app</button>
</div> </div>
</div> </div>
</div> </div>
@ -241,3 +241,43 @@ function scrollToPage(index: number) {
el.scrollTo({ left: index * el.clientWidth, behavior: 'smooth' }) el.scrollTo({ left: index * el.clientWidth, behavior: 'smooth' })
} }
</script> </script>
<style scoped>
.sideload-modal {
display: flex;
flex-direction: column;
width: 100%;
max-width: 34rem;
max-height: calc(100dvh - var(--safe-area-top, env(safe-area-inset-top, 0px)) - 12px);
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.14);
border-radius: 1.5rem 1.5rem 0 0;
background: rgba(8, 10, 18, 0.94);
padding: 1.25rem;
padding-bottom: calc(1.25rem + var(--safe-area-bottom, env(safe-area-inset-bottom, 0px)));
box-shadow: 0 -24px 70px rgba(0, 0, 0, 0.55);
}
.sideload-close-btn {
width: 2.25rem;
height: 2.25rem;
flex-shrink: 0;
border-radius: 0.75rem;
color: rgba(255, 255, 255, 0.55);
background: rgba(255, 255, 255, 0.06);
}
.credential-modal-body {
min-height: 0;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}
.credential-modal-actions {
flex-shrink: 0;
}
@media (min-width: 768px) {
.sideload-modal {
border-radius: 1.25rem;
padding: 1.5rem;
box-shadow: 0 25px 80px rgba(0, 0, 0, 0.55);
}
}
</style>

View File

@ -180,6 +180,20 @@ init()
</button> </button>
</div> </div>
<div class="overflow-y-auto flex-1 min-h-0 space-y-6 pr-1"> <div class="overflow-y-auto flex-1 min-h-0 space-y-6 pr-1">
<!-- v1.7.78-alpha -->
<div>
<div class="flex items-center gap-2 mb-3">
<span class="text-xs font-mono px-2 py-0.5 rounded bg-orange-500/20 text-orange-300">v1.7.78-alpha</span>
<span class="text-xs text-white/40">May 20, 2026</span>
</div>
<div class="space-y-3 text-sm text-white/80 pl-3 border-l border-white/10">
<p>Saleor's validated port 9010 dashboard/API origin settings are now ready for production release, preserving the working test-node install path.</p>
<p>NetBird launches now stay on the unified dashboard/proxy origin at port 8087 instead of following stale server URLs on 8086.</p>
<p>NetBird proxy routing no longer depends on a hard-coded rootless Podman gateway IP and now includes the upstream management proxy gRPC path.</p>
<p>Mobile credential prompts keep long credential lists scrollable and the Cancel/Continue buttons reachable in both My Apps and the mobile icon grid.</p>
<p>Android app-session popups hand external login/signup windows to the system browser instead of dropping them inside the WebView.</p>
</div>
</div>
<!-- v1.7.76-alpha --> <!-- v1.7.76-alpha -->
<div> <div>
<div class="flex items-center gap-2 mb-3"> <div class="flex items-center gap-2 mb-3">

View File

@ -1,29 +1,31 @@
{ {
"version": "1.7.77-alpha", "version": "1.7.78-alpha",
"release_date": "2026-05-20", "release_date": "2026-05-20",
"changelog": [ "changelog": [
"Saleor first-use now exposes generated credentials through Archipelago instead of leaving users at an unexplained dashboard login: App Details shows copyable `admin@example.com` credentials, and My Apps/mobile icon launches show a pre-launch credentials modal.", "Public Nginx Proxy Manager hosts for Saleor now keep browser GraphQL calls same-origin at `/graphql/` and proxy them to the local API on `8000`, fixing `Failed to fetch` when a public domain such as `noderunner.shop` was loaded from devices that cannot reach the node's private LAN/tailnet API address.",
"Saleor installs now create or repair the `admin@example.com` staff account idempotently after sample data loads, use the correct dashboard mount path, and re-check stack containers after startup so stopped containers are caught.", "Saleor's validated stack changes are now release-ready: dashboard origins on port `9010` are explicitly allowed for dashboard/API calls, preserving the working test-node install path for production nodes.",
"NetBird embedded login now uses the upstream-compatible IdP signing-key behavior and sends ID tokens from the dashboard to the management API, fixing the post-signup `Unauthenticated` state while preserving the unified local proxy/logout routes.", "NetBird launches now stay pinned to the unified dashboard/proxy origin on port `8087` instead of following stale runtime-discovered server URLs on `8086`.",
"Transient unnamed Podman helper containers created during app install tasks are hidden from My Apps, so generated names like `eager_keldysh` no longer appear as user applications.", "NetBird's local nginx proxy now routes browser API, OAuth, relay, and WebSocket traffic through `host.containers.internal:8086` instead of a hard-coded rootless Podman gateway IP, and includes the upstream `management.ProxyService` gRPC path.",
"Validation passed with catalog/release JSON checks, `npm run type-check`, and `cargo fmt --all --check --manifest-path core/Cargo.toml`; live checks on `100.114.134.21` confirmed Saleor dashboard/API availability, generated Saleor admin login, NetBird OAuth availability, and NetBird logout redirects." "The mobile credentials interstitial now keeps credential lists scrollable and action buttons reachable in both My Apps and the mobile app icon grid.",
"Android WebView popup windows now hand external popup URLs to the system browser, covering app login/signup flows that open secondary windows.",
"Validation passed with `git diff --check`, `cargo check -p archipelago`, and the focused `npm test -- src/views/appSession/__tests__/appSessionConfig.test.ts` suite."
], ],
"components": [ "components": [
{ {
"name": "archipelago", "name": "archipelago",
"current_version": "1.7.77-alpha", "current_version": "1.7.78-alpha",
"new_version": "1.7.77-alpha", "new_version": "1.7.78-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.77-alpha/archipelago", "download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.78-alpha/archipelago",
"sha256": "53679077182044f0601bab41e7239d293089f30725c1bedd883f30c40bd7807b", "sha256": "49ac959035029fb77cbf001cfabbe7919bc74aee3bb7176067c77ab0d1c97b58",
"size_bytes": 43068640 "size_bytes": 43069480
}, },
{ {
"name": "archipelago-frontend-1.7.77-alpha.tar.gz", "name": "archipelago-frontend-1.7.78-alpha.tar.gz",
"current_version": "1.7.77-alpha", "current_version": "1.7.78-alpha",
"new_version": "1.7.77-alpha", "new_version": "1.7.78-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.77-alpha/archipelago-frontend-1.7.77-alpha.tar.gz", "download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.78-alpha/archipelago-frontend-1.7.78-alpha.tar.gz",
"sha256": "a083abfcbdbb03a2ba5a02e622d7d496fa5e6b39f2ee5afd41c9f4df0d31b9d6", "sha256": "a11ed587122fe2150c7439fdb88bfb4f3b999de2c52855bb4ac860b237854943",
"size_bytes": 166486722 "size_bytes": 166486679
} }
] ]
} }

View File

@ -1,29 +1,31 @@
{ {
"version": "1.7.77-alpha", "version": "1.7.78-alpha",
"release_date": "2026-05-20", "release_date": "2026-05-20",
"changelog": [ "changelog": [
"Saleor first-use now exposes generated credentials through Archipelago instead of leaving users at an unexplained dashboard login: App Details shows copyable `admin@example.com` credentials, and My Apps/mobile icon launches show a pre-launch credentials modal.", "Public Nginx Proxy Manager hosts for Saleor now keep browser GraphQL calls same-origin at `/graphql/` and proxy them to the local API on `8000`, fixing `Failed to fetch` when a public domain such as `noderunner.shop` was loaded from devices that cannot reach the node's private LAN/tailnet API address.",
"Saleor installs now create or repair the `admin@example.com` staff account idempotently after sample data loads, use the correct dashboard mount path, and re-check stack containers after startup so stopped containers are caught.", "Saleor's validated stack changes are now release-ready: dashboard origins on port `9010` are explicitly allowed for dashboard/API calls, preserving the working test-node install path for production nodes.",
"NetBird embedded login now uses the upstream-compatible IdP signing-key behavior and sends ID tokens from the dashboard to the management API, fixing the post-signup `Unauthenticated` state while preserving the unified local proxy/logout routes.", "NetBird launches now stay pinned to the unified dashboard/proxy origin on port `8087` instead of following stale runtime-discovered server URLs on `8086`.",
"Transient unnamed Podman helper containers created during app install tasks are hidden from My Apps, so generated names like `eager_keldysh` no longer appear as user applications.", "NetBird's local nginx proxy now routes browser API, OAuth, relay, and WebSocket traffic through `host.containers.internal:8086` instead of a hard-coded rootless Podman gateway IP, and includes the upstream `management.ProxyService` gRPC path.",
"Validation passed with catalog/release JSON checks, `npm run type-check`, and `cargo fmt --all --check --manifest-path core/Cargo.toml`; live checks on `100.114.134.21` confirmed Saleor dashboard/API availability, generated Saleor admin login, NetBird OAuth availability, and NetBird logout redirects." "The mobile credentials interstitial now keeps credential lists scrollable and action buttons reachable in both My Apps and the mobile app icon grid.",
"Android WebView popup windows now hand external popup URLs to the system browser, covering app login/signup flows that open secondary windows.",
"Validation passed with `git diff --check`, `cargo check -p archipelago`, and the focused `npm test -- src/views/appSession/__tests__/appSessionConfig.test.ts` suite."
], ],
"components": [ "components": [
{ {
"name": "archipelago", "name": "archipelago",
"current_version": "1.7.77-alpha", "current_version": "1.7.78-alpha",
"new_version": "1.7.77-alpha", "new_version": "1.7.78-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.77-alpha/archipelago", "download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.78-alpha/archipelago",
"sha256": "53679077182044f0601bab41e7239d293089f30725c1bedd883f30c40bd7807b", "sha256": "49ac959035029fb77cbf001cfabbe7919bc74aee3bb7176067c77ab0d1c97b58",
"size_bytes": 43068640 "size_bytes": 43069480
}, },
{ {
"name": "archipelago-frontend-1.7.77-alpha.tar.gz", "name": "archipelago-frontend-1.7.78-alpha.tar.gz",
"current_version": "1.7.77-alpha", "current_version": "1.7.78-alpha",
"new_version": "1.7.77-alpha", "new_version": "1.7.78-alpha",
"download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.77-alpha/archipelago-frontend-1.7.77-alpha.tar.gz", "download_url": "http://146.59.87.168:3000/lfg2025/archy/releases/download/v1.7.78-alpha/archipelago-frontend-1.7.78-alpha.tar.gz",
"sha256": "a083abfcbdbb03a2ba5a02e622d7d496fa5e6b39f2ee5afd41c9f4df0d31b9d6", "sha256": "a11ed587122fe2150c7439fdb88bfb4f3b999de2c52855bb4ac860b237854943",
"size_bytes": 166486722 "size_bytes": 166486679
} }
] ]
} }

View File

@ -58,6 +58,32 @@ for row in rows:
# NPM containers use this name to reach host-published services; host nginx # NPM containers use this name to reach host-published services; host nginx
# itself should use loopback for the same services. # itself should use loopback for the same services.
nginx_host = "127.0.0.1" if host == "host.containers.internal" else host nginx_host = "127.0.0.1" if host == "host.containers.internal" else host
try:
forward_port = int(port)
except (TypeError, ValueError):
forward_port = None
is_saleor = forward_port == 9010
graphql_location = ""
saleor_proxy_headers = ""
if is_saleor:
graphql_location = """
location ^~ /graphql/ {
proxy_pass http://127.0.0.1:8000/graphql/;
proxy_http_version 1.1;
proxy_set_header Host 127.0.0.1;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Scheme https;
proxy_set_header Origin "";
}
"""
saleor_proxy_headers = """
proxy_set_header Accept-Encoding "";
sub_filter_once off;
sub_filter_types text/html;
sub_filter '</head>' '<script>if(window.__SALEOR_CONFIG__){window.__SALEOR_CONFIG__.API_URL=location.origin+"/graphql/";window.__SALEOR_CONFIG__.EXTENSIONS_API_URL=location.origin+"/graphql/";}</script></head>';
"""
print(f""" print(f"""
server {{ server {{
@ -81,6 +107,8 @@ server {{
ssl_certificate {cert}; ssl_certificate {cert};
ssl_certificate_key {key}; ssl_certificate_key {key};
{graphql_location}
location / {{ location / {{
proxy_pass {scheme}://{nginx_host}:{port}; proxy_pass {scheme}://{nginx_host}:{port};
proxy_http_version 1.1; proxy_http_version 1.1;
@ -91,6 +119,7 @@ server {{
proxy_set_header X-Forwarded-Scheme https; proxy_set_header X-Forwarded-Scheme https;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";
{saleor_proxy_headers}
}} }}
}} }}
""") """)