Add deploy.sh endpoint and one-command installation flow

This commit is contained in:
thakares committed 2026-06-17 20:17:19 +05:30
1 parent 900f72a299
commit 1a9bf096f3
4 files changed
+101 -28

No files matched your search

+19 -26
View File
@@ -1,67 +1,60 @@
name: app-bzod name: app-bzod
services: services:
bzod: bzod:
image: nx9-url-shortener:v0.1.0
build: build:
context: . context: /DATA/AppData/bzod
dockerfile: Dockerfile dockerfile: Dockerfile
cpu_shares: 90
command: []
container_name: bzod container_name: bzod
deploy:
resources:
limits:
memory: 31940M
environment: environment:
- COOKIE_SECURE=false - COOKIE_SECURE=false
- DATA_DIR=/app/data - DATA_DIR=/app/data
- HOST=0.0.0.0 - HOST=0.0.0.0
- PORT=8654 - PORT=8654
- RUST_LOG=info - RUST_LOG=info
hostname: bzod
image: nx9-url-shortener:v0.4.0
ports: ports:
- mode: ingress - mode: ingress
target: 8654 target: 8654
published: "8654" published: "8654"
protocol: tcp protocol: tcp
restart: unless-stopped restart: unless-stopped
volumes: volumes:
- type: bind - type: bind
source: /DATA/AppData/bzod/data source: /DATA/AppData/bzod/data
target: /app/data target: /app/data
bind: bind:
create_host_path: true create_host_path: true
- type: bind - type: bind
source: /DATA/AppData/bzod/config source: /DATA/AppData/bzod/config
target: /app/config target: /app/config
bind: bind:
create_host_path: true create_host_path: true
- type: bind
source: /DATA/AppData/bzod/www
target: /app/www
devices: []
cap_add: []
networks: networks:
- default - default
hostname: bzod
privileged: false privileged: false
cpu_shares: 90
deploy:
resources:
limits:
memory: 31940M
networks: networks:
default: default:
name: app_default name: app_default
x-casaos: x-casaos:
hostname: ""
scheme: http
index: /
port_map: "8654"
author: self author: self
category: self category: self
hostname: ""
icon: "" icon: ""
index: /
is_uncontrolled: false
port_map: "8654"
scheme: http
title: title:
custom: nx9-url-shortener custom: nx9-url-shortener
+13
View File
@@ -120,3 +120,16 @@ pub async fn root_landing() -> Response {
Err(_) => (StatusCode::NOT_FOUND, "Not Found").into_response(), Err(_) => (StatusCode::NOT_FOUND, "Not Found").into_response(),
} }
} }
pub async fn deploy_script() -> Response {
match tokio::fs::read("www/deploy.sh").await {
Ok(content) => (
StatusCode::OK,
[("content-type", "text/plain; charset=utf-8")],
content,
)
.into_response(),
Err(_) => (StatusCode::NOT_FOUND, "deploy.sh not found").into_response(),
}
}
+1
View File
@@ -38,6 +38,7 @@ pub fn create_router(state: AppState) -> Router {
.route("/admin/pages/create", post(admin::pages_create)) .route("/admin/pages/create", post(admin::pages_create))
.route("/admin/pages/delete/:id", post(admin::pages_delete)) .route("/admin/pages/delete/:id", post(admin::pages_delete))
.route("/admin/analytics/url/:id", get(admin::url_analytics_get)) .route("/admin/analytics/url/:id", get(admin::url_analytics_get))
.route("/deploy.sh", get(pages::deploy_script))
.route( .route(
"/admin/analytics/url/:id/export/csv", "/admin/analytics/url/:id/export/csv",
get(admin::url_analytics_csv_export), get(admin::url_analytics_csv_export),
+68 -2
View File
@@ -15,7 +15,6 @@
</style> </style>
</head> </head>
<body class="bg-zinc-950 text-zinc-200"> <body class="bg-zinc-950 text-zinc-200">
<!-- Hero --> <!-- Hero -->
<section class="hero-bg py-24"> <section class="hero-bg py-24">
<div class="max-w-5xl mx-auto px-6 text-center"> <div class="max-w-5xl mx-auto px-6 text-center">
@@ -51,6 +50,23 @@
</div> </div>
</div> </div>
</section> </section>
<div class="flex flex-wrap justify-center gap-3 mt-8 text-sm">
<span class="bg-zinc-900 px-3 py-1 rounded-full">
Single Binary
</span>
<span class="bg-zinc-900 px-3 py-1 rounded-full">
SQLite
</span>
<span class="bg-zinc-900 px-3 py-1 rounded-full">
REST API
</span>
<span class="bg-zinc-900 px-3 py-1 rounded-full">
Self Hosted
</span>
<span class="bg-zinc-900 px-3 py-1 rounded-full">
MIT License
</span>
</div>
<!-- Features --> <!-- Features -->
<section class="py-20 bg-zinc-900"> <section class="py-20 bg-zinc-900">
@@ -99,6 +115,56 @@
</div> </div>
</section> </section>
<!-- Install -->
<section class="py-20 bg-zinc-950 border-t border-zinc-800 border-b border-zinc-800">
<div class="max-w-4xl mx-auto px-6 text-center">
<h2 class="text-4xl font-bold mb-4">
Install BZOD
</h2>
<p class="text-zinc-400 mb-10">
Deploy on any Debian/Ubuntu VPS, homelab, mini-PC, or Raspberry Pi.
</p>
<div class="bg-black border border-zinc-800 rounded-3xl overflow-hidden">
<div class="flex items-center justify-between px-5 py-3 border-b border-zinc-800">
<span class="text-zinc-500 text-sm">
Linux Installation
</span>
<button
onclick="copyInstallCommand()"
class="text-sm bg-violet-600 hover:bg-violet-700 px-4 py-2 rounded-lg transition">
Copy
</button>
</div>
<pre class="text-left overflow-x-auto p-6 text-emerald-400 font-mono text-sm"><code id="install-command">curl -fsSL https://bzo.in/deploy.sh | sudo bash</code></pre>
</div>
<p class="text-zinc-500 text-sm mt-4">
Downloads the latest release and installs BZOD as a systemd service.
</p>
</div>
</section>
<script>
function copyInstallCommand() {
const text =
"curl -fsSL https://bzo.in/deploy.sh | sudo bash";
navigator.clipboard.writeText(text);
const btn = event.target;
const old = btn.innerText;
btn.innerText = "Copied!";
setTimeout(() => {
btn.innerText = old;
}, 2000);
}
</script>
<!-- CTA --> <!-- CTA -->
<section class="py-20 bg-black border-t border-zinc-800"> <section class="py-20 bg-black border-t border-zinc-800">
<div class="max-w-2xl mx-auto text-center px-6"> <div class="max-w-2xl mx-auto text-center px-6">
@@ -121,7 +187,7 @@
<footer class="bg-zinc-950 py-12 border-t border-zinc-800"> <footer class="bg-zinc-950 py-12 border-t border-zinc-800">
<div class="max-w-5xl mx-auto px-6 text-center text-zinc-500 text-sm"> <div class="max-w-5xl mx-auto px-6 text-center text-zinc-500 text-sm">
© 2026 BZOD • Made with ❤️ in Rust • Running on <span class="font-mono">bzo.in</span> © 2026 BZOD • Made with ❤️ in Rust by Sunil Purushottam Thakare • Running on <span class="font-mono">bzo.in</span>
</div> </div>
</footer> </footer>
</body> </body>