feat(apps): add saleor and harden netbird repair
This commit is contained in:
parent
56f956973e
commit
522c046525
@ -64,6 +64,23 @@
|
|||||||
"bitcoin-knots"
|
"bitcoin-knots"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "saleor",
|
||||||
|
"title": "Saleor",
|
||||||
|
"version": "3.23",
|
||||||
|
"description": "Composable commerce platform with GraphQL API, dashboard, worker, mail testing, and tracing.",
|
||||||
|
"icon": "/assets/img/app-icons/saleor.svg",
|
||||||
|
"author": "Saleor",
|
||||||
|
"category": "commerce",
|
||||||
|
"tier": "recommended",
|
||||||
|
"dockerImage": "ghcr.io/saleor/saleor:3.23",
|
||||||
|
"repoUrl": "https://github.com/saleor/saleor",
|
||||||
|
"containerConfig": {
|
||||||
|
"ports": ["9000:80", "8000:8000", "8025:8025", "16686:16686"],
|
||||||
|
"volumes": ["/var/lib/archipelago/saleor:/app/media", "/var/lib/archipelago/saleor-db:/var/lib/postgresql/data"],
|
||||||
|
"notes": "Installed as a Saleor stack: dashboard on 9000, API on 8000, Mailpit on 8025, and Jaeger on 16686. Supporting containers include PostgreSQL, Valkey, Celery worker, and services required by Saleor."
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "mempool",
|
"id": "mempool",
|
||||||
"title": "Mempool Explorer",
|
"title": "Mempool Explorer",
|
||||||
|
|||||||
@ -31,6 +31,7 @@ fn is_platform_managed_app(app_id: &str) -> bool {
|
|||||||
| "fedimint"
|
| "fedimint"
|
||||||
| "fedimint-gateway"
|
| "fedimint-gateway"
|
||||||
| "indeedhub"
|
| "indeedhub"
|
||||||
|
| "saleor"
|
||||||
| "immich"
|
| "immich"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -500,6 +501,15 @@ pub(super) fn all_container_names(package_id: &str) -> Vec<String> {
|
|||||||
"netbird-dashboard".into(),
|
"netbird-dashboard".into(),
|
||||||
"netbird-server".into(),
|
"netbird-server".into(),
|
||||||
],
|
],
|
||||||
|
"saleor" => vec![
|
||||||
|
"saleor-db".into(),
|
||||||
|
"saleor-cache".into(),
|
||||||
|
"saleor-api".into(),
|
||||||
|
"saleor-worker".into(),
|
||||||
|
"saleor-jaeger".into(),
|
||||||
|
"saleor-mailpit".into(),
|
||||||
|
"saleor".into(),
|
||||||
|
],
|
||||||
"nostr-vpn" => vec![
|
"nostr-vpn" => vec![
|
||||||
"nostr-vpn".into(),
|
"nostr-vpn".into(),
|
||||||
"archy-nostr-vpn".into(),
|
"archy-nostr-vpn".into(),
|
||||||
@ -589,6 +599,7 @@ pub(super) fn get_data_dirs_for_app(package_id: &str) -> Vec<String> {
|
|||||||
format!("{}/penpot-postgres", base),
|
format!("{}/penpot-postgres", base),
|
||||||
],
|
],
|
||||||
"netbird" => vec![format!("{}/netbird", base)],
|
"netbird" => vec![format!("{}/netbird", base)],
|
||||||
|
"saleor" => vec![format!("{}/saleor", base), format!("{}/saleor-db", base)],
|
||||||
_ => vec![format!("{}/{}", base, package_id)],
|
_ => vec![format!("{}/{}", base, package_id)],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1068,6 +1079,13 @@ pub(super) async fn get_app_config(
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
),
|
),
|
||||||
|
"saleor" => (
|
||||||
|
vec!["9000:80".to_string(), "8000:8000".to_string()],
|
||||||
|
vec!["/var/lib/archipelago/saleor:/app/media".to_string()],
|
||||||
|
vec![],
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
),
|
||||||
"nostr-rs-relay" => (
|
"nostr-rs-relay" => (
|
||||||
vec!["18081:8080".to_string()],
|
vec!["18081:8080".to_string()],
|
||||||
vec!["/var/lib/archipelago/nostr-rs-relay:/usr/src/app/db".to_string()],
|
vec!["/var/lib/archipelago/nostr-rs-relay:/usr/src/app/db".to_string()],
|
||||||
|
|||||||
@ -289,6 +289,15 @@ pub(super) fn startup_order(package_id: &str) -> &'static [&'static str] {
|
|||||||
&["archy-btcpay-db", "archy-nbxplorer", "btcpay-server"]
|
&["archy-btcpay-db", "archy-nbxplorer", "btcpay-server"]
|
||||||
}
|
}
|
||||||
"netbird" => &["netbird-server", "netbird-dashboard", "netbird"],
|
"netbird" => &["netbird-server", "netbird-dashboard", "netbird"],
|
||||||
|
"saleor" => &[
|
||||||
|
"saleor-db",
|
||||||
|
"saleor-cache",
|
||||||
|
"saleor-jaeger",
|
||||||
|
"saleor-mailpit",
|
||||||
|
"saleor-api",
|
||||||
|
"saleor-worker",
|
||||||
|
"saleor",
|
||||||
|
],
|
||||||
"penpot" | "penpot-frontend" => &[
|
"penpot" | "penpot-frontend" => &[
|
||||||
"penpot-postgres",
|
"penpot-postgres",
|
||||||
"penpot-valkey",
|
"penpot-valkey",
|
||||||
|
|||||||
@ -244,6 +244,9 @@ impl RpcHandler {
|
|||||||
if package_id == "netbird" {
|
if package_id == "netbird" {
|
||||||
return self.install_netbird_stack().await;
|
return self.install_netbird_stack().await;
|
||||||
}
|
}
|
||||||
|
if package_id == "saleor" {
|
||||||
|
return self.install_saleor_stack().await;
|
||||||
|
}
|
||||||
|
|
||||||
// Dependency checks. Prefer the scanner's cached package state so a
|
// Dependency checks. Prefer the scanner's cached package state so a
|
||||||
// congested Podman API does not turn an already-running dependency into
|
// congested Podman API does not turn an already-running dependency into
|
||||||
|
|||||||
@ -99,6 +99,7 @@ async fn repair_stack_before_adopt(stack_name: &str) {
|
|||||||
}
|
}
|
||||||
"indeedhub" => repair_indeedhub_network_aliases().await,
|
"indeedhub" => repair_indeedhub_network_aliases().await,
|
||||||
"netbird" => repair_netbird_unified_origin().await,
|
"netbird" => repair_netbird_unified_origin().await,
|
||||||
|
"saleor" => repair_saleor_network_aliases().await,
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,23 +152,9 @@ async fn repair_netbird_unified_origin() {
|
|||||||
.unwrap_or_else(|| "127.0.0.1".to_string());
|
.unwrap_or_else(|| "127.0.0.1".to_string());
|
||||||
let _ = write_netbird_config_files(&host_ip).await;
|
let _ = write_netbird_config_files(&host_ip).await;
|
||||||
|
|
||||||
let names = tokio::process::Command::new("podman")
|
for container in ["netbird", "netbird-dashboard"] {
|
||||||
.args(["ps", "-a", "--format", "{{.Names}}"])
|
|
||||||
.output()
|
|
||||||
.await
|
|
||||||
.ok()
|
|
||||||
.map(|o| String::from_utf8_lossy(&o.stdout).to_string())
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let has_proxy = names.lines().any(|n| n.trim() == "netbird");
|
|
||||||
let has_dashboard = names.lines().any(|n| n.trim() == "netbird-dashboard");
|
|
||||||
if has_proxy && has_dashboard {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_proxy && !has_dashboard {
|
|
||||||
let _ = tokio::process::Command::new("podman")
|
let _ = tokio::process::Command::new("podman")
|
||||||
.args(["rm", "-f", "netbird"])
|
.args(["rm", "-f", container])
|
||||||
.output()
|
.output()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
@ -177,6 +164,9 @@ async fn repair_netbird_unified_origin() {
|
|||||||
.output()
|
.output()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let _ = pull_image_with_retry(NETBIRD_DASHBOARD_IMAGE).await;
|
||||||
|
let _ = pull_image_with_retry(NETBIRD_PROXY_IMAGE).await;
|
||||||
|
|
||||||
let _ = tokio::process::Command::new("podman")
|
let _ = tokio::process::Command::new("podman")
|
||||||
.args([
|
.args([
|
||||||
"run",
|
"run",
|
||||||
@ -210,6 +200,75 @@ async fn repair_netbird_unified_origin() {
|
|||||||
])
|
])
|
||||||
.output()
|
.output()
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args([
|
||||||
|
"network",
|
||||||
|
"disconnect",
|
||||||
|
"-f",
|
||||||
|
"netbird-net",
|
||||||
|
"netbird-server",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args([
|
||||||
|
"network",
|
||||||
|
"connect",
|
||||||
|
"--alias",
|
||||||
|
"netbird-server",
|
||||||
|
"netbird-net",
|
||||||
|
"netbird-server",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args(["restart", "netbird-server"])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn repair_saleor_network_aliases() {
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args(["network", "create", "saleor-net"])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
for (container, alias) in [
|
||||||
|
("saleor-db", "db"),
|
||||||
|
("saleor-cache", "cache"),
|
||||||
|
("saleor-jaeger", "jaeger"),
|
||||||
|
("saleor-mailpit", "mailpit"),
|
||||||
|
("saleor-api", "api"),
|
||||||
|
("saleor-worker", "worker"),
|
||||||
|
("saleor", "saleor"),
|
||||||
|
] {
|
||||||
|
let exists = tokio::process::Command::new("podman")
|
||||||
|
.args(["container", "exists", container])
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.map(|s| s.success())
|
||||||
|
.unwrap_or(false);
|
||||||
|
if !exists {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args(["network", "disconnect", "-f", "saleor-net", container])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args([
|
||||||
|
"network",
|
||||||
|
"connect",
|
||||||
|
"--alias",
|
||||||
|
alias,
|
||||||
|
"saleor-net",
|
||||||
|
container,
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_required_stack_command(
|
async fn run_required_stack_command(
|
||||||
@ -382,6 +441,12 @@ const REGISTRY: &str = "146.59.87.168:3000/lfg2025";
|
|||||||
const NETBIRD_DASHBOARD_IMAGE: &str = "docker.io/netbirdio/dashboard:v2.38.0";
|
const NETBIRD_DASHBOARD_IMAGE: &str = "docker.io/netbirdio/dashboard:v2.38.0";
|
||||||
const NETBIRD_SERVER_IMAGE: &str = "docker.io/netbirdio/netbird-server:0.71.2";
|
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 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_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";
|
||||||
|
const SALEOR_MAILPIT_IMAGE: &str = "docker.io/axllent/mailpit:latest";
|
||||||
|
|
||||||
/// Pull an image with retry and exponential backoff (3 attempts).
|
/// Pull an image with retry and exponential backoff (3 attempts).
|
||||||
async fn pull_image_with_retry(image: &str) -> Result<()> {
|
async fn pull_image_with_retry(image: &str) -> Result<()> {
|
||||||
@ -1574,6 +1639,415 @@ impl RpcHandler {
|
|||||||
"message": "NetBird self-hosted stack installed",
|
"message": "NetBird self-hosted stack installed",
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Install Saleor stack (PostgreSQL + Valkey + API + worker + dashboard + Jaeger + Mailpit).
|
||||||
|
pub(super) async fn install_saleor_stack(&self) -> Result<serde_json::Value> {
|
||||||
|
if let Some(adopted) = adopt_stack_if_exists(
|
||||||
|
"saleor",
|
||||||
|
"saleor",
|
||||||
|
&[
|
||||||
|
"saleor-db",
|
||||||
|
"saleor-cache",
|
||||||
|
"saleor-jaeger",
|
||||||
|
"saleor-mailpit",
|
||||||
|
"saleor-api",
|
||||||
|
"saleor-worker",
|
||||||
|
"saleor",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
{
|
||||||
|
return Ok(adopted);
|
||||||
|
}
|
||||||
|
|
||||||
|
install_log("INSTALL START: saleor stack (postgres + valkey + api + worker + dashboard)")
|
||||||
|
.await;
|
||||||
|
info!("Installing Saleor stack");
|
||||||
|
|
||||||
|
let images = [
|
||||||
|
SALEOR_POSTGRES_IMAGE,
|
||||||
|
SALEOR_VALKEY_IMAGE,
|
||||||
|
SALEOR_API_IMAGE,
|
||||||
|
SALEOR_DASHBOARD_IMAGE,
|
||||||
|
SALEOR_JAEGER_IMAGE,
|
||||||
|
SALEOR_MAILPIT_IMAGE,
|
||||||
|
];
|
||||||
|
self.set_install_phase("saleor", InstallPhase::PullingImage)
|
||||||
|
.await;
|
||||||
|
let n_images = images.len() as u64;
|
||||||
|
for (i, image) in images.iter().enumerate() {
|
||||||
|
self.set_install_progress("saleor", i as u64, n_images)
|
||||||
|
.await;
|
||||||
|
pull_image_with_retry(image)
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("Failed to pull Saleor image: {}", image))?;
|
||||||
|
}
|
||||||
|
self.set_install_progress("saleor", n_images, n_images)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
for name in [
|
||||||
|
"saleor",
|
||||||
|
"saleor-api",
|
||||||
|
"saleor-worker",
|
||||||
|
"saleor-db",
|
||||||
|
"saleor-cache",
|
||||||
|
"saleor-jaeger",
|
||||||
|
"saleor-mailpit",
|
||||||
|
] {
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args(["rm", "-f", name])
|
||||||
|
.status()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args(["network", "rm", "-f", "saleor-net"])
|
||||||
|
.status()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
self.set_install_phase("saleor", InstallPhase::CreatingContainer)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let _ = tokio::process::Command::new("sudo")
|
||||||
|
.args([
|
||||||
|
"mkdir",
|
||||||
|
"-p",
|
||||||
|
"/var/lib/archipelago/saleor",
|
||||||
|
"/var/lib/archipelago/saleor-db",
|
||||||
|
"/var/lib/archipelago/saleor-cache",
|
||||||
|
])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
let user = std::env::var("USER").unwrap_or_else(|_| "archipelago".to_string());
|
||||||
|
for dir in [
|
||||||
|
"/var/lib/archipelago/saleor",
|
||||||
|
"/var/lib/archipelago/saleor-db",
|
||||||
|
"/var/lib/archipelago/saleor-cache",
|
||||||
|
] {
|
||||||
|
let _ = tokio::process::Command::new("sudo")
|
||||||
|
.args(["chown", "-R", &format!("{}:{}", user, user), dir])
|
||||||
|
.output()
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = tokio::process::Command::new("podman")
|
||||||
|
.args(["network", "create", "saleor-net"])
|
||||||
|
.status()
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let db_pass = super::config::read_or_generate_secret("saleor-db-password").await;
|
||||||
|
let secret_key = super::config::read_or_generate_secret("saleor-secret-key").await;
|
||||||
|
let host_ip = &self.config.host_ip;
|
||||||
|
let dashboard_url = format!("http://{}:9000/", 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 database_url = format!("postgres://saleor:{}@db/saleor", db_pass);
|
||||||
|
|
||||||
|
let mut db_cmd = tokio::process::Command::new("podman");
|
||||||
|
db_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor-db",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--network-alias",
|
||||||
|
"db",
|
||||||
|
"--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=4096",
|
||||||
|
"--health-cmd=pg_isready -U saleor || exit 1",
|
||||||
|
"--health-interval=30s",
|
||||||
|
"--health-retries=3",
|
||||||
|
"-v",
|
||||||
|
"/var/lib/archipelago/saleor-db:/var/lib/postgresql/data",
|
||||||
|
"-e",
|
||||||
|
"POSTGRES_USER=saleor",
|
||||||
|
"-e",
|
||||||
|
&format!("POSTGRES_PASSWORD={}", db_pass),
|
||||||
|
"-e",
|
||||||
|
"POSTGRES_DB=saleor",
|
||||||
|
SALEOR_POSTGRES_IMAGE,
|
||||||
|
]);
|
||||||
|
run_required_stack_command("saleor", "create postgres", &mut db_cmd).await?;
|
||||||
|
|
||||||
|
let mut cache_cmd = tokio::process::Command::new("podman");
|
||||||
|
cache_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor-cache",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--network-alias",
|
||||||
|
"cache",
|
||||||
|
"--restart=unless-stopped",
|
||||||
|
"--cap-drop=ALL",
|
||||||
|
"--cap-add=SETGID",
|
||||||
|
"--cap-add=SETUID",
|
||||||
|
"--security-opt=no-new-privileges:true",
|
||||||
|
"--memory=128m",
|
||||||
|
"--pids-limit=2048",
|
||||||
|
"--health-cmd=valkey-cli ping || exit 1",
|
||||||
|
"--health-interval=30s",
|
||||||
|
"--health-retries=3",
|
||||||
|
"-v",
|
||||||
|
"/var/lib/archipelago/saleor-cache:/data",
|
||||||
|
SALEOR_VALKEY_IMAGE,
|
||||||
|
]);
|
||||||
|
run_required_stack_command("saleor", "create cache", &mut cache_cmd).await?;
|
||||||
|
|
||||||
|
let mut jaeger_cmd = tokio::process::Command::new("podman");
|
||||||
|
jaeger_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor-jaeger",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--network-alias",
|
||||||
|
"jaeger",
|
||||||
|
"--restart=unless-stopped",
|
||||||
|
"--cap-drop=ALL",
|
||||||
|
"--security-opt=no-new-privileges:true",
|
||||||
|
"--memory=512m",
|
||||||
|
"--pids-limit=4096",
|
||||||
|
"-p",
|
||||||
|
"16686:16686",
|
||||||
|
"-p",
|
||||||
|
"4317:4317",
|
||||||
|
"-p",
|
||||||
|
"4318:4318",
|
||||||
|
"--tmpfs",
|
||||||
|
"/tmp:rw,nosuid,nodev,size=128m",
|
||||||
|
SALEOR_JAEGER_IMAGE,
|
||||||
|
]);
|
||||||
|
run_required_stack_command("saleor", "create jaeger", &mut jaeger_cmd).await?;
|
||||||
|
|
||||||
|
let mut mailpit_cmd = tokio::process::Command::new("podman");
|
||||||
|
mailpit_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor-mailpit",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--network-alias",
|
||||||
|
"mailpit",
|
||||||
|
"--restart=unless-stopped",
|
||||||
|
"--cap-drop=ALL",
|
||||||
|
"--security-opt=no-new-privileges:true",
|
||||||
|
"--memory=128m",
|
||||||
|
"--pids-limit=2048",
|
||||||
|
"-p",
|
||||||
|
"1025:1025",
|
||||||
|
"-p",
|
||||||
|
"8025:8025",
|
||||||
|
SALEOR_MAILPIT_IMAGE,
|
||||||
|
]);
|
||||||
|
run_required_stack_command("saleor", "create mailpit", &mut mailpit_cmd).await?;
|
||||||
|
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(8)).await;
|
||||||
|
|
||||||
|
let saleor_env = vec![
|
||||||
|
"-e".to_string(),
|
||||||
|
"CACHE_URL=redis://cache:6379/0".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"CELERY_BROKER_URL=redis://cache:6379/1".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
format!("DATABASE_URL={}", database_url),
|
||||||
|
"-e".to_string(),
|
||||||
|
"DEFAULT_CHANNEL_SLUG=default-channel".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"DEFAULT_FROM_EMAIL=noreply@example.com".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"EMAIL_URL=smtp://mailpit:1025".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
format!("SECRET_KEY={}", secret_key),
|
||||||
|
"-e".to_string(),
|
||||||
|
"OTEL_SERVICE_NAME=saleor".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"OTEL_TRACES_EXPORTER=otlp".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"OTEL_EXPORTER_OTLP_ENDPOINT=http://jaeger:4317".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"HTTP_IP_FILTER_ALLOW_LOOPBACK_IPS=True".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
"HTTP_IP_FILTER_ENABLED=False".to_string(),
|
||||||
|
"-e".to_string(),
|
||||||
|
format!("DASHBOARD_URL={}", dashboard_url),
|
||||||
|
"-e".to_string(),
|
||||||
|
format!("ALLOWED_HOSTS={}", allowed_hosts),
|
||||||
|
];
|
||||||
|
|
||||||
|
let mut migrate_cmd = tokio::process::Command::new("podman");
|
||||||
|
migrate_cmd.args([
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"-v",
|
||||||
|
"/var/lib/archipelago/saleor:/app/media",
|
||||||
|
]);
|
||||||
|
migrate_cmd.args(&saleor_env);
|
||||||
|
migrate_cmd.args([SALEOR_API_IMAGE, "python3", "manage.py", "migrate"]);
|
||||||
|
run_required_stack_command("saleor", "run migrations", &mut migrate_cmd).await?;
|
||||||
|
|
||||||
|
let mut populate_cmd = tokio::process::Command::new("podman");
|
||||||
|
populate_cmd.args([
|
||||||
|
"run",
|
||||||
|
"--rm",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"-v",
|
||||||
|
"/var/lib/archipelago/saleor:/app/media",
|
||||||
|
]);
|
||||||
|
populate_cmd.args(&saleor_env);
|
||||||
|
populate_cmd.args([
|
||||||
|
SALEOR_API_IMAGE,
|
||||||
|
"python3",
|
||||||
|
"manage.py",
|
||||||
|
"populatedb",
|
||||||
|
"--createsuperuser",
|
||||||
|
]);
|
||||||
|
let populate = populate_cmd.output().await;
|
||||||
|
if let Ok(output) = populate {
|
||||||
|
if !output.status.success() {
|
||||||
|
install_log(&format!(
|
||||||
|
"INSTALL WARN: saleor - populate sample data skipped: {}{}",
|
||||||
|
String::from_utf8_lossy(&output.stdout),
|
||||||
|
String::from_utf8_lossy(&output.stderr)
|
||||||
|
))
|
||||||
|
.await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut api_cmd = tokio::process::Command::new("podman");
|
||||||
|
api_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor-api",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--network-alias",
|
||||||
|
"api",
|
||||||
|
"--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=1g",
|
||||||
|
"--pids-limit=4096",
|
||||||
|
"-p",
|
||||||
|
"8000:8000",
|
||||||
|
"-v",
|
||||||
|
"/var/lib/archipelago/saleor:/app/media",
|
||||||
|
]);
|
||||||
|
api_cmd.args(&saleor_env);
|
||||||
|
api_cmd.arg(SALEOR_API_IMAGE);
|
||||||
|
run_required_stack_command("saleor", "create api", &mut api_cmd).await?;
|
||||||
|
|
||||||
|
let mut worker_cmd = tokio::process::Command::new("podman");
|
||||||
|
worker_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor-worker",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--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=1g",
|
||||||
|
"--pids-limit=4096",
|
||||||
|
"-v",
|
||||||
|
"/var/lib/archipelago/saleor:/app/media",
|
||||||
|
]);
|
||||||
|
worker_cmd.args(&saleor_env);
|
||||||
|
worker_cmd.args([
|
||||||
|
SALEOR_API_IMAGE,
|
||||||
|
"celery",
|
||||||
|
"-A",
|
||||||
|
"saleor",
|
||||||
|
"--app=saleor.celeryconf:app",
|
||||||
|
"worker",
|
||||||
|
"--loglevel=info",
|
||||||
|
"-B",
|
||||||
|
]);
|
||||||
|
run_required_stack_command("saleor", "create worker", &mut worker_cmd).await?;
|
||||||
|
|
||||||
|
self.set_install_phase("saleor", InstallPhase::StartingContainer)
|
||||||
|
.await;
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(5)).await;
|
||||||
|
|
||||||
|
let mut dashboard_cmd = tokio::process::Command::new("podman");
|
||||||
|
dashboard_cmd.args([
|
||||||
|
"run",
|
||||||
|
"-d",
|
||||||
|
"--name",
|
||||||
|
"saleor",
|
||||||
|
"--network",
|
||||||
|
"saleor-net",
|
||||||
|
"--restart=unless-stopped",
|
||||||
|
"--cap-drop=ALL",
|
||||||
|
"--security-opt=no-new-privileges:true",
|
||||||
|
"--memory=256m",
|
||||||
|
"--pids-limit=2048",
|
||||||
|
"-p",
|
||||||
|
"9000:80",
|
||||||
|
"-e",
|
||||||
|
&format!("API_URL={}", api_url),
|
||||||
|
"-e",
|
||||||
|
&format!("APP_MOUNT_URI={}", dashboard_url),
|
||||||
|
SALEOR_DASHBOARD_IMAGE,
|
||||||
|
]);
|
||||||
|
run_required_stack_command("saleor", "create dashboard", &mut dashboard_cmd).await?;
|
||||||
|
|
||||||
|
wait_for_stack_containers(
|
||||||
|
"saleor",
|
||||||
|
&[
|
||||||
|
"saleor-db",
|
||||||
|
"saleor-cache",
|
||||||
|
"saleor-jaeger",
|
||||||
|
"saleor-mailpit",
|
||||||
|
"saleor-api",
|
||||||
|
"saleor-worker",
|
||||||
|
"saleor",
|
||||||
|
],
|
||||||
|
120,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
self.set_install_phase("saleor", InstallPhase::WaitingHealthy)
|
||||||
|
.await;
|
||||||
|
self.set_install_phase("saleor", InstallPhase::PostInstall)
|
||||||
|
.await;
|
||||||
|
self.set_install_phase("saleor", InstallPhase::Done).await;
|
||||||
|
self.clear_install_progress("saleor").await;
|
||||||
|
|
||||||
|
install_log("INSTALL OK: saleor stack").await;
|
||||||
|
info!("Saleor stack installed");
|
||||||
|
Ok(serde_json::json!({
|
||||||
|
"success": true,
|
||||||
|
"package_id": "saleor",
|
||||||
|
"message": "Saleor stack installed (7 containers)",
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read_or_generate_b64_secret(name: &str) -> String {
|
async fn read_or_generate_b64_secret(name: &str) -> String {
|
||||||
@ -1616,6 +2090,8 @@ async fn write_netbird_config_files(host_ip: &str) -> Result<()> {
|
|||||||
dashboardRedirectURIs:
|
dashboardRedirectURIs:
|
||||||
- "{public_origin}/nb-auth"
|
- "{public_origin}/nb-auth"
|
||||||
- "{public_origin}/nb-silent-auth"
|
- "{public_origin}/nb-silent-auth"
|
||||||
|
dashboardPostLogoutRedirectURIs:
|
||||||
|
- "{public_origin}/"
|
||||||
cliRedirectURIs:
|
cliRedirectURIs:
|
||||||
- "http://localhost:53000/"
|
- "http://localhost:53000/"
|
||||||
store:
|
store:
|
||||||
@ -1665,7 +2141,7 @@ LETSENCRYPT_DOMAIN=none
|
|||||||
proxy_read_timeout 1d;
|
proxy_read_timeout 1d;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
location ~ ^/(api|oauth2)/ {{
|
location ~ ^/(api|oauth2)(/|$) {{
|
||||||
proxy_pass http://netbird-server:80;
|
proxy_pass http://netbird-server:80;
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
|||||||
@ -63,6 +63,12 @@ impl DockerPackageScanner {
|
|||||||
"indeedhub-build_ffmpeg-worker_1",
|
"indeedhub-build_ffmpeg-worker_1",
|
||||||
"netbird-server",
|
"netbird-server",
|
||||||
"netbird-dashboard",
|
"netbird-dashboard",
|
||||||
|
"saleor-api",
|
||||||
|
"saleor-worker",
|
||||||
|
"saleor-db",
|
||||||
|
"saleor-cache",
|
||||||
|
"saleor-jaeger",
|
||||||
|
"saleor-mailpit",
|
||||||
"buildx_buildkit_default",
|
"buildx_buildkit_default",
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -283,6 +289,7 @@ fn get_app_tier(app_id: &str) -> &'static str {
|
|||||||
"uptime-kuma" => "recommended",
|
"uptime-kuma" => "recommended",
|
||||||
"grafana" => "recommended",
|
"grafana" => "recommended",
|
||||||
"searxng" => "recommended",
|
"searxng" => "recommended",
|
||||||
|
"saleor" => "recommended",
|
||||||
"tailscale" | "netbird" => "recommended",
|
"tailscale" | "netbird" => "recommended",
|
||||||
"portainer" => "recommended",
|
"portainer" => "recommended",
|
||||||
// Optional: everything else
|
// Optional: everything else
|
||||||
@ -488,6 +495,13 @@ fn get_app_metadata(app_id: &str) -> AppMetadata {
|
|||||||
repo: "https://github.com/netbirdio/netbird".to_string(),
|
repo: "https://github.com/netbirdio/netbird".to_string(),
|
||||||
tier: "",
|
tier: "",
|
||||||
},
|
},
|
||||||
|
"saleor" => AppMetadata {
|
||||||
|
title: "Saleor".to_string(),
|
||||||
|
description: "Composable commerce platform with GraphQL API and dashboard".to_string(),
|
||||||
|
icon: "/assets/img/app-icons/saleor.svg".to_string(),
|
||||||
|
repo: "https://github.com/saleor/saleor".to_string(),
|
||||||
|
tier: "",
|
||||||
|
},
|
||||||
"gitea" => AppMetadata {
|
"gitea" => AppMetadata {
|
||||||
title: "Gitea".to_string(),
|
title: "Gitea".to_string(),
|
||||||
description: "Self-hosted Git service with repository and package hosting".to_string(),
|
description: "Self-hosted Git service with repository and package hosting".to_string(),
|
||||||
@ -716,6 +730,7 @@ fn requires_reachable_launch(app_id: &str) -> bool {
|
|||||||
| "tailscale"
|
| "tailscale"
|
||||||
| "immich"
|
| "immich"
|
||||||
| "searxng"
|
| "searxng"
|
||||||
|
| "saleor"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -171,6 +171,12 @@ fn image_var_for_app(app_id: &str) -> Option<&'static str> {
|
|||||||
"netbird" => Some("NETBIRD_DASHBOARD_IMAGE"),
|
"netbird" => Some("NETBIRD_DASHBOARD_IMAGE"),
|
||||||
"netbird-dashboard" => Some("NETBIRD_DASHBOARD_IMAGE"),
|
"netbird-dashboard" => Some("NETBIRD_DASHBOARD_IMAGE"),
|
||||||
"netbird-server" => Some("NETBIRD_SERVER_IMAGE"),
|
"netbird-server" => Some("NETBIRD_SERVER_IMAGE"),
|
||||||
|
"saleor" => Some("SALEOR_DASHBOARD_IMAGE"),
|
||||||
|
"saleor-api" | "saleor-worker" => Some("SALEOR_API_IMAGE"),
|
||||||
|
"saleor-db" => Some("SALEOR_POSTGRES_IMAGE"),
|
||||||
|
"saleor-cache" => Some("SALEOR_VALKEY_IMAGE"),
|
||||||
|
"saleor-jaeger" => Some("SALEOR_JAEGER_IMAGE"),
|
||||||
|
"saleor-mailpit" => Some("SALEOR_MAILPIT_IMAGE"),
|
||||||
|
|
||||||
// Fedimint
|
// Fedimint
|
||||||
"fedimint" | "fedimintd" => Some("FEDIMINT_IMAGE"),
|
"fedimint" | "fedimintd" => Some("FEDIMINT_IMAGE"),
|
||||||
@ -307,6 +313,15 @@ pub fn containers_for_stack(app_id: &str) -> Vec<(&'static str, &'static str)> {
|
|||||||
("netbird-dashboard", "NETBIRD_DASHBOARD_IMAGE"),
|
("netbird-dashboard", "NETBIRD_DASHBOARD_IMAGE"),
|
||||||
("netbird-server", "NETBIRD_SERVER_IMAGE"),
|
("netbird-server", "NETBIRD_SERVER_IMAGE"),
|
||||||
],
|
],
|
||||||
|
"saleor" => vec![
|
||||||
|
("saleor-db", "SALEOR_POSTGRES_IMAGE"),
|
||||||
|
("saleor-cache", "SALEOR_VALKEY_IMAGE"),
|
||||||
|
("saleor-api", "SALEOR_API_IMAGE"),
|
||||||
|
("saleor-worker", "SALEOR_API_IMAGE"),
|
||||||
|
("saleor-jaeger", "SALEOR_JAEGER_IMAGE"),
|
||||||
|
("saleor-mailpit", "SALEOR_MAILPIT_IMAGE"),
|
||||||
|
("saleor", "SALEOR_DASHBOARD_IMAGE"),
|
||||||
|
],
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
neode-ui/public/assets/img/app-icons/saleor.svg
Normal file
5
neode-ui/public/assets/img/app-icons/saleor.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 128 128" role="img" aria-label="Saleor">
|
||||||
|
<rect width="128" height="128" rx="30" fill="#111827"/>
|
||||||
|
<path d="M34 42c0-10 9-18 22-18h38v16H56c-5 0-8 2-8 5 0 4 4 5 13 7l15 3c15 3 24 11 24 24 0 15-12 25-31 25H31V88h39c8 0 13-3 13-8 0-4-4-6-12-8l-16-3C41 66 34 57 34 42Z" fill="#fff"/>
|
||||||
|
<path d="M29 103h70v8H29z" fill="#7C3AED"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 389 B |
@ -64,6 +64,23 @@
|
|||||||
"bitcoin-knots"
|
"bitcoin-knots"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"id": "saleor",
|
||||||
|
"title": "Saleor",
|
||||||
|
"version": "3.23",
|
||||||
|
"description": "Composable commerce platform with GraphQL API, dashboard, worker, mail testing, and tracing.",
|
||||||
|
"icon": "/assets/img/app-icons/saleor.svg",
|
||||||
|
"author": "Saleor",
|
||||||
|
"category": "commerce",
|
||||||
|
"tier": "recommended",
|
||||||
|
"dockerImage": "ghcr.io/saleor/saleor:3.23",
|
||||||
|
"repoUrl": "https://github.com/saleor/saleor",
|
||||||
|
"containerConfig": {
|
||||||
|
"ports": ["9000:80", "8000:8000", "8025:8025", "16686:16686"],
|
||||||
|
"volumes": ["/var/lib/archipelago/saleor:/app/media", "/var/lib/archipelago/saleor-db:/var/lib/postgresql/data"],
|
||||||
|
"notes": "Installed as a Saleor stack: dashboard on 9000, API on 8000, Mailpit on 8025, and Jaeger on 16686. Supporting containers include PostgreSQL, Valkey, Celery worker, and services required by Saleor."
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id": "mempool",
|
"id": "mempool",
|
||||||
"title": "Mempool Explorer",
|
"title": "Mempool Explorer",
|
||||||
|
|||||||
@ -67,6 +67,9 @@
|
|||||||
data-controller-zone="main"
|
data-controller-zone="main"
|
||||||
class="flex-1 overflow-hidden relative pb-0 glass-piece z-10"
|
class="flex-1 overflow-hidden relative pb-0 glass-piece z-10"
|
||||||
:class="{ 'glass-throw-main': showZoomIn }"
|
:class="{ 'glass-throw-main': showZoomIn }"
|
||||||
|
tabindex="-1"
|
||||||
|
@pointerenter="activateMainScroll"
|
||||||
|
@wheel.capture="activateMainScroll"
|
||||||
>
|
>
|
||||||
<div data-controller-main-entry class="absolute top-4 right-4 md:top-6 md:right-8 z-20">
|
<div data-controller-main-entry class="absolute top-4 right-4 md:top-6 md:right-8 z-20">
|
||||||
<!-- Controller zone entry point - no switcher -->
|
<!-- Controller zone entry point - no switcher -->
|
||||||
@ -234,6 +237,14 @@ function restoreScroll(path: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function activateMainScroll() {
|
||||||
|
const active = document.activeElement as HTMLElement | null
|
||||||
|
if (active?.closest?.('[data-controller-zone="sidebar"]')) {
|
||||||
|
active.blur()
|
||||||
|
document.getElementById('main-content')?.focus({ preventScroll: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
watch(() => route.path, (newPath) => {
|
watch(() => route.path, (newPath) => {
|
||||||
const isAppDetails = isDetailRoute(newPath)
|
const isAppDetails = isDetailRoute(newPath)
|
||||||
const wasAppDetails = showAltBackground.value
|
const wasAppDetails = showAltBackground.value
|
||||||
|
|||||||
@ -14,6 +14,7 @@ export const APP_PORTS: Record<string, number> = {
|
|||||||
'archy-electrs-ui': 50002,
|
'archy-electrs-ui': 50002,
|
||||||
'mempool-electrs': 50002,
|
'mempool-electrs': 50002,
|
||||||
'btcpay-server': 23000,
|
'btcpay-server': 23000,
|
||||||
|
'saleor': 9000,
|
||||||
'lnd': 18083,
|
'lnd': 18083,
|
||||||
'archy-lnd-ui': 18083,
|
'archy-lnd-ui': 18083,
|
||||||
'mempool': 4080,
|
'mempool': 4080,
|
||||||
@ -71,7 +72,7 @@ export const EXTERNAL_URLS: Record<string, string> = {
|
|||||||
|
|
||||||
export const APP_TITLES: Record<string, string> = {
|
export const APP_TITLES: Record<string, string> = {
|
||||||
'bitcoin-knots': 'Bitcoin Knots', 'bitcoin-core': 'Bitcoin Core',
|
'bitcoin-knots': 'Bitcoin Knots', 'bitcoin-core': 'Bitcoin Core',
|
||||||
'btcpay-server': 'BTCPay Server', 'indeedhub': 'Indeehub',
|
'btcpay-server': 'BTCPay Server', 'saleor': 'Saleor', 'indeedhub': 'Indeehub',
|
||||||
'botfights': 'BotFights', 'gitea': 'Gitea', '484-kitchen': '484 Kitchen', 'arch-presentation': 'Presentation',
|
'botfights': 'BotFights', 'gitea': 'Gitea', '484-kitchen': '484 Kitchen', 'arch-presentation': 'Presentation',
|
||||||
'homeassistant': 'Home Assistant', 'uptime-kuma': 'Uptime Kuma',
|
'homeassistant': 'Home Assistant', 'uptime-kuma': 'Uptime Kuma',
|
||||||
'nginx-proxy-manager': 'Nginx Proxy Manager',
|
'nginx-proxy-manager': 'Nginx Proxy Manager',
|
||||||
|
|||||||
@ -79,6 +79,7 @@ export function getCuratedAppList(): MarketplaceApp[] {
|
|||||||
{ id: 'bitcoin-knots', title: 'Bitcoin Knots', version: '28.1.0', description: 'Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network.', icon: '/assets/img/app-icons/bitcoin-knots.webp', author: 'Bitcoin Knots', dockerImage: `${R}/bitcoin-knots:latest`, repoUrl: 'https://github.com/bitcoinknots/bitcoin' },
|
{ id: 'bitcoin-knots', title: 'Bitcoin Knots', version: '28.1.0', description: 'Run a full Bitcoin node. Validate and relay blocks and transactions on the Bitcoin network.', icon: '/assets/img/app-icons/bitcoin-knots.webp', author: 'Bitcoin Knots', dockerImage: `${R}/bitcoin-knots:latest`, repoUrl: 'https://github.com/bitcoinknots/bitcoin' },
|
||||||
{ id: 'bitcoin-core', title: 'Bitcoin Core', version: '28.4', description: 'Reference implementation of the Bitcoin protocol. Run a full node validating and relaying blocks on the Bitcoin network.', icon: '/assets/img/app-icons/bitcoin-core.svg', author: 'Bitcoin Core contributors', dockerImage: 'docker.io/bitcoin/bitcoin:28.4', repoUrl: 'https://github.com/bitcoin/bitcoin' },
|
{ id: 'bitcoin-core', title: 'Bitcoin Core', version: '28.4', description: 'Reference implementation of the Bitcoin protocol. Run a full node validating and relaying blocks on the Bitcoin network.', icon: '/assets/img/app-icons/bitcoin-core.svg', author: 'Bitcoin Core contributors', dockerImage: 'docker.io/bitcoin/bitcoin:28.4', repoUrl: 'https://github.com/bitcoin/bitcoin' },
|
||||||
{ id: 'btcpay-server', title: 'BTCPay Server', version: '2.3.9', description: 'Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries or fees.', icon: '/assets/img/app-icons/btcpay-server.png', author: 'BTCPay Server Foundation', dockerImage: 'docker.io/btcpayserver/btcpayserver:2.3.9', repoUrl: 'https://github.com/btcpayserver/btcpayserver' },
|
{ id: 'btcpay-server', title: 'BTCPay Server', version: '2.3.9', description: 'Self-hosted Bitcoin payment processor. Accept Bitcoin payments without intermediaries or fees.', icon: '/assets/img/app-icons/btcpay-server.png', author: 'BTCPay Server Foundation', dockerImage: 'docker.io/btcpayserver/btcpayserver:2.3.9', repoUrl: 'https://github.com/btcpayserver/btcpayserver' },
|
||||||
|
{ id: 'saleor', title: 'Saleor', version: '3.23', category: 'commerce', description: 'Composable commerce platform with GraphQL API, dashboard, worker, mail testing, and tracing.', icon: '/assets/img/app-icons/saleor.svg', author: 'Saleor', dockerImage: 'ghcr.io/saleor/saleor:3.23', repoUrl: 'https://github.com/saleor/saleor' },
|
||||||
{ id: 'lnd', title: 'LND', version: '0.18.4', description: 'Lightning Network Daemon. Fast and cheap Bitcoin payments through the Lightning Network.', icon: '/assets/img/app-icons/lnd.svg', author: 'Lightning Labs', dockerImage: `${R}/lnd:v0.18.4-beta`, repoUrl: 'https://github.com/lightningnetwork/lnd' },
|
{ id: 'lnd', title: 'LND', version: '0.18.4', description: 'Lightning Network Daemon. Fast and cheap Bitcoin payments through the Lightning Network.', icon: '/assets/img/app-icons/lnd.svg', author: 'Lightning Labs', dockerImage: `${R}/lnd:v0.18.4-beta`, repoUrl: 'https://github.com/lightningnetwork/lnd' },
|
||||||
{ id: 'mempool', title: 'Mempool Explorer', version: '3.0.0', description: 'Self-hosted Bitcoin blockchain and mempool visualizer. Monitor transactions without revealing your addresses to third parties.', icon: '/assets/img/app-icons/mempool.webp', author: 'Mempool', dockerImage: `${R}/mempool-frontend:v3.0.0`, repoUrl: 'https://github.com/mempool/mempool' },
|
{ id: 'mempool', title: 'Mempool Explorer', version: '3.0.0', description: 'Self-hosted Bitcoin blockchain and mempool visualizer. Monitor transactions without revealing your addresses to third parties.', icon: '/assets/img/app-icons/mempool.webp', author: 'Mempool', dockerImage: `${R}/mempool-frontend:v3.0.0`, repoUrl: 'https://github.com/mempool/mempool' },
|
||||||
{ id: 'homeassistant', title: 'Home Assistant', version: '2024.1', description: 'Open-source home automation. Control smart home devices privately, on your own hardware.', icon: '/assets/img/app-icons/homeassistant.png', author: 'Home Assistant', dockerImage: `${R}/home-assistant:2024.1`, repoUrl: 'https://github.com/home-assistant/core' },
|
{ id: 'homeassistant', title: 'Home Assistant', version: '2024.1', description: 'Open-source home automation. Control smart home devices privately, on your own hardware.', icon: '/assets/img/app-icons/homeassistant.png', author: 'Home Assistant', dockerImage: `${R}/home-assistant:2024.1`, repoUrl: 'https://github.com/home-assistant/core' },
|
||||||
@ -120,6 +121,7 @@ export const INSTALLED_ALIASES: Record<string, string[]> = {
|
|||||||
mempool: ['mempool', 'mempool-web', 'archy-mempool-web'],
|
mempool: ['mempool', 'mempool-web', 'archy-mempool-web'],
|
||||||
bitcoin: ['bitcoin-knots'],
|
bitcoin: ['bitcoin-knots'],
|
||||||
btcpay: ['btcpay-server'],
|
btcpay: ['btcpay-server'],
|
||||||
|
saleor: ['saleor'],
|
||||||
immich: ['immich-server', 'immich-app', 'immich_server'],
|
immich: ['immich-server', 'immich-app', 'immich_server'],
|
||||||
nextcloud: ['nextcloud-aio', 'nextcloud-server'],
|
nextcloud: ['nextcloud-aio', 'nextcloud-server'],
|
||||||
fedimint: ['fedimint-gateway'],
|
fedimint: ['fedimint-gateway'],
|
||||||
@ -189,7 +191,7 @@ export function categorizeCommunityApp(app: MarketplaceApp): string {
|
|||||||
const combined = `${id} ${title} ${description}`
|
const combined = `${id} ${title} ${description}`
|
||||||
|
|
||||||
if (id.includes('bitcoin') || id.includes('btc') || id.includes('lightning') || id.includes('lnd') || id.includes('electr') || id.includes('fedimint') || id.includes('cashu') || combined.includes('wallet')) return 'money'
|
if (id.includes('bitcoin') || id.includes('btc') || id.includes('lightning') || id.includes('lnd') || id.includes('electr') || id.includes('fedimint') || id.includes('cashu') || combined.includes('wallet')) return 'money'
|
||||||
if (id.includes('btcpay') || id.includes('commerce') || id.includes('shop') || id.includes('pos') || combined.includes('merchant')) return 'commerce'
|
if (id.includes('btcpay') || id.includes('saleor') || id.includes('commerce') || id.includes('shop') || id.includes('pos') || combined.includes('merchant')) return 'commerce'
|
||||||
if (id.includes('cloud') || id.includes('nextcloud') || id.includes('storage') || id.includes('file') || id.includes('photo') || id.includes('immich') || id.includes('jellyfin') || id.includes('media') || id.includes('vault') || combined.includes('password manager')) return 'data'
|
if (id.includes('cloud') || id.includes('nextcloud') || id.includes('storage') || id.includes('file') || id.includes('photo') || id.includes('immich') || id.includes('jellyfin') || id.includes('media') || id.includes('vault') || combined.includes('password manager')) return 'data'
|
||||||
if (id.includes('home-assistant') || id.includes('homeassistant') || combined.includes('home automation')) return 'home'
|
if (id.includes('home-assistant') || id.includes('homeassistant') || combined.includes('home automation')) return 'home'
|
||||||
if (id.includes('nostr') || combined.includes('nostr relay')) return 'nostr'
|
if (id.includes('nostr') || combined.includes('nostr relay')) return 'nostr'
|
||||||
|
|||||||
@ -47,6 +47,7 @@ export const INSTALLED_ALIASES: Record<string, string[]> = {
|
|||||||
mempool: ['mempool-web', 'mempool-api', 'archy-mempool-web', 'archy-mempool-db'],
|
mempool: ['mempool-web', 'mempool-api', 'archy-mempool-web', 'archy-mempool-db'],
|
||||||
bitcoin: ['bitcoin-knots'],
|
bitcoin: ['bitcoin-knots'],
|
||||||
btcpay: ['btcpay-server', 'archy-btcpay-db', 'archy-nbxplorer'],
|
btcpay: ['btcpay-server', 'archy-btcpay-db', 'archy-nbxplorer'],
|
||||||
|
saleor: ['saleor'],
|
||||||
immich: ['immich-server', 'immich-app', 'immich_server', 'immich_postgres', 'immich_redis'],
|
immich: ['immich-server', 'immich-app', 'immich_server', 'immich_postgres', 'immich_redis'],
|
||||||
nextcloud: ['nextcloud-aio', 'nextcloud-server'],
|
nextcloud: ['nextcloud-aio', 'nextcloud-server'],
|
||||||
fedimint: ['fedimint-gateway'],
|
fedimint: ['fedimint-gateway'],
|
||||||
@ -67,7 +68,7 @@ export const INSTALLED_ALIASES: Record<string, string[]> = {
|
|||||||
/** Get app tier classification (matches backend get_app_tier) */
|
/** Get app tier classification (matches backend get_app_tier) */
|
||||||
export function getAppTier(appId: string): string {
|
export function getAppTier(appId: string): string {
|
||||||
const core = ['bitcoin-knots', 'bitcoin', 'lnd', 'mempool', 'btcpay-server', 'dwn', 'filebrowser']
|
const core = ['bitcoin-knots', 'bitcoin', 'lnd', 'mempool', 'btcpay-server', 'dwn', 'filebrowser']
|
||||||
const recommended = ['fedimint', 'thunderhub', 'vaultwarden', 'uptime-kuma', 'grafana', 'searxng', 'tailscale', 'netbird', 'portainer']
|
const recommended = ['fedimint', 'thunderhub', 'vaultwarden', 'uptime-kuma', 'grafana', 'searxng', 'tailscale', 'netbird', 'portainer', 'saleor']
|
||||||
if (core.includes(appId)) return 'core'
|
if (core.includes(appId)) return 'core'
|
||||||
if (recommended.includes(appId)) return 'recommended'
|
if (recommended.includes(appId)) return 'recommended'
|
||||||
return 'optional'
|
return 'optional'
|
||||||
@ -89,7 +90,7 @@ export function categorizeCommunityApp(app: MarketplaceApp): string {
|
|||||||
return 'money'
|
return 'money'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id.includes('btcpay') || id.includes('commerce') || id.includes('shop') ||
|
if (id.includes('btcpay') || id.includes('saleor') || id.includes('commerce') || id.includes('shop') ||
|
||||||
id.includes('store') || id.includes('pos') || id.includes('payment') ||
|
id.includes('store') || id.includes('pos') || id.includes('payment') ||
|
||||||
combined.includes('merchant') || combined.includes('invoice')) {
|
combined.includes('merchant') || combined.includes('invoice')) {
|
||||||
return 'commerce'
|
return 'commerce'
|
||||||
@ -157,6 +158,18 @@ export function getCuratedAppList(): MarketplaceApp[] {
|
|||||||
manifestUrl: undefined,
|
manifestUrl: undefined,
|
||||||
repoUrl: 'https://github.com/btcpayserver/btcpayserver'
|
repoUrl: 'https://github.com/btcpayserver/btcpayserver'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'saleor',
|
||||||
|
title: 'Saleor',
|
||||||
|
version: '3.23',
|
||||||
|
category: 'commerce',
|
||||||
|
description: 'Composable commerce platform with GraphQL API, dashboard, worker, mail testing, and tracing.',
|
||||||
|
icon: '/assets/img/app-icons/saleor.svg',
|
||||||
|
author: 'Saleor',
|
||||||
|
dockerImage: 'ghcr.io/saleor/saleor:3.23',
|
||||||
|
manifestUrl: undefined,
|
||||||
|
repoUrl: 'https://github.com/saleor/saleor'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'lnd',
|
id: 'lnd',
|
||||||
title: 'LND',
|
title: 'LND',
|
||||||
|
|||||||
@ -54,6 +54,14 @@ NETBIRD_PROXY_IMAGE="docker.io/library/nginx:1.27-alpine"
|
|||||||
ALPINE_TOR_IMAGE="$ARCHY_REGISTRY/alpine-tor:0.4.8.13"
|
ALPINE_TOR_IMAGE="$ARCHY_REGISTRY/alpine-tor:0.4.8.13"
|
||||||
ADGUARDHOME_IMAGE="$ARCHY_REGISTRY/adguardhome:v0.107.55"
|
ADGUARDHOME_IMAGE="$ARCHY_REGISTRY/adguardhome:v0.107.55"
|
||||||
|
|
||||||
|
# Saleor stack
|
||||||
|
SALEOR_API_IMAGE="ghcr.io/saleor/saleor:3.23"
|
||||||
|
SALEOR_DASHBOARD_IMAGE="ghcr.io/saleor/saleor-dashboard:3.23"
|
||||||
|
SALEOR_POSTGRES_IMAGE="docker.io/library/postgres:15-alpine"
|
||||||
|
SALEOR_VALKEY_IMAGE="docker.io/valkey/valkey:8.1-alpine"
|
||||||
|
SALEOR_JAEGER_IMAGE="docker.io/jaegertracing/jaeger:latest"
|
||||||
|
SALEOR_MAILPIT_IMAGE="docker.io/axllent/mailpit:latest"
|
||||||
|
|
||||||
# Fedimint
|
# Fedimint
|
||||||
FEDIMINT_IMAGE="$ARCHY_REGISTRY/fedimintd:v0.10.0"
|
FEDIMINT_IMAGE="$ARCHY_REGISTRY/fedimintd:v0.10.0"
|
||||||
FEDIMINT_GATEWAY_IMAGE="$ARCHY_REGISTRY/gatewayd:v0.10.0"
|
FEDIMINT_GATEWAY_IMAGE="$ARCHY_REGISTRY/gatewayd:v0.10.0"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user