1+ #! /usr/bin/env bash
2+ # RustAPI Cloud — production install on Ubuntu/Debian
3+ # Usage: DOMAIN=api.example.com JWT_SECRET=... GITHUB_CLIENT_ID=... GITHUB_CLIENT_SECRET=... ./install.sh
4+ set -euo pipefail
5+
6+ DOMAIN=" ${DOMAIN:- rustapi.tunayinbayramharciligi.com} "
7+ APP_ROOT=" ${APP_ROOT:-/ opt/ rustapi} "
8+ CLOUD_DIR=" ${APP_ROOT} /RustAPI-Cloud"
9+ SERVICE_USER=" ${SERVICE_USER:- rustapi} "
10+ STORAGE_ROOT=" /var/lib/rustapi-cloud/storage"
11+ NGINX_MAP_DIR=" /etc/nginx/rustapi-deploy-map.d"
12+
13+ echo " ==> RustAPI Cloud install (domain: ${DOMAIN} )"
14+
15+ if [[ " $( id -u) " -ne 0 ]]; then
16+ echo " Run as root." >&2
17+ exit 1
18+ fi
19+
20+ echo " ==> Remove previous release"
21+ systemctl stop rustapi-cloud 2> /dev/null || true
22+ systemctl disable rustapi-cloud 2> /dev/null || true
23+ rm -f /etc/systemd/system/rustapi-cloud.service
24+ rm -f /etc/nginx/sites-enabled/rustapi-cloud /etc/nginx/sites-available/rustapi-cloud
25+ rm -f /etc/nginx/sites-enabled/rustapi-apps /etc/nginx/sites-available/rustapi-apps
26+ rm -rf " ${NGINX_MAP_DIR} "
27+ rm -rf /opt/rustapi-cloud /var/www/rustapi-cloud /srv/rustapi-cloud
28+ docker rm -f rustapi-cloud-db 2> /dev/null || true
29+
30+ echo " ==> Install system packages"
31+ export DEBIAN_FRONTEND=noninteractive
32+ apt-get update -qq
33+ apt-get install -y -qq \
34+ ca-certificates curl git build-essential pkg-config libssl-dev \
35+ nginx certbot python3-certbot-nginx \
36+ docker.io docker-compose-plugin postgresql-client
37+
38+ systemctl enable --now docker
39+ systemctl enable --now nginx
40+
41+ if ! id " ${SERVICE_USER} " & > /dev/null; then
42+ useradd --system --home " ${APP_ROOT} " --shell /usr/sbin/nologin " ${SERVICE_USER} "
43+ fi
44+
45+ if ! command -v cargo & > /dev/null; then
46+ echo " ==> Install Rust toolchain"
47+ su - " ${SERVICE_USER} " -c ' curl --proto "=https" --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y'
48+ fi
49+
50+ if [[ ! -f " ${CLOUD_DIR} /Cargo.toml" ]]; then
51+ echo " ==> Clone repository (or upload RustAPI tree to ${APP_ROOT} first)"
52+ mkdir -p " ${APP_ROOT} "
53+ git clone --depth 1 https://github.com/Tuntii/RustAPI.git " ${APP_ROOT} "
54+ fi
55+ chown -R " ${SERVICE_USER} :${SERVICE_USER} " " ${APP_ROOT} "
56+
57+ cd " ${CLOUD_DIR} "
58+
59+ echo " ==> Start Postgres"
60+ docker compose up -d
61+ for i in $( seq 1 30) ; do
62+ if docker compose exec -T db pg_isready -U rustapi -d rustapi_cloud & > /dev/null; then
63+ break
64+ fi
65+ sleep 2
66+ done
67+
68+ echo " ==> Apply migrations"
69+ for f in migrations/* .sql; do
70+ echo " - $( basename " $f " ) "
71+ docker compose exec -T db psql -U rustapi -d rustapi_cloud -v ON_ERROR_STOP=1 < " $f "
72+ done
73+
74+ JWT_SECRET=" ${JWT_SECRET:- $(openssl rand -hex 32)} "
75+ GITHUB_CLIENT_ID=" ${GITHUB_CLIENT_ID:- REPLACE_ME} "
76+ GITHUB_CLIENT_SECRET=" ${GITHUB_CLIENT_SECRET:- REPLACE_ME} "
77+
78+ mkdir -p " ${STORAGE_ROOT} "
79+ chown -R " ${SERVICE_USER} :${SERVICE_USER} " " ${STORAGE_ROOT} "
80+
81+ cat > " ${CLOUD_DIR} /.env" << EOF
82+ DATABASE_URL=postgres://rustapi:rustapi@localhost:5432/rustapi_cloud
83+ HOST=127.0.0.1
84+ PORT=3000
85+ JWT_SECRET=${JWT_SECRET}
86+ GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID}
87+ GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET}
88+ GITHUB_REDIRECT_URI=https://${DOMAIN} /auth/callback
89+ STORAGE_ROOT=${STORAGE_ROOT}
90+ DEPLOY_PUBLIC_HOST=${DOMAIN}
91+ DEPLOY_URL_SCHEME=https
92+ NGINX_DEPLOY_MAP_DIR=${NGINX_MAP_DIR}
93+ RUST_LOG=rustapi_cloud=info,info
94+ EOF
95+ chmod 600 " ${CLOUD_DIR} /.env"
96+ chown " ${SERVICE_USER} :${SERVICE_USER} " " ${CLOUD_DIR} /.env"
97+
98+ echo " ==> Build rustapi-cloud (release)"
99+ su - " ${SERVICE_USER} " -c " cd ${CLOUD_DIR} && source \$ HOME/.cargo/env && cargo build --release"
100+
101+ install -m 0644 " ${CLOUD_DIR} /deploy/rustapi-cloud.service" /etc/systemd/system/rustapi-cloud.service
102+ sed -i " s|__APP_ROOT__|${APP_ROOT} |g" /etc/systemd/system/rustapi-cloud.service
103+ sed -i " s|__SERVICE_USER__|${SERVICE_USER} |g" /etc/systemd/system/rustapi-cloud.service
104+
105+ mkdir -p " ${NGINX_MAP_DIR} "
106+ chown " ${SERVICE_USER} :${SERVICE_USER} " " ${NGINX_MAP_DIR} "
107+ chmod 0755 " ${NGINX_MAP_DIR} "
108+ echo " ${SERVICE_USER} ALL=(root) NOPASSWD: /usr/sbin/nginx -s reload" > /etc/sudoers.d/rustapi-nginx-reload
109+ chmod 0440 /etc/sudoers.d/rustapi-nginx-reload
110+
111+ install -m 0644 " ${CLOUD_DIR} /deploy/nginx-rustapi-cloud.conf" /etc/nginx/sites-available/rustapi-cloud
112+ sed -i " s|__DOMAIN__|${DOMAIN} |g" /etc/nginx/sites-available/rustapi-cloud
113+ ln -sf /etc/nginx/sites-available/rustapi-cloud /etc/nginx/sites-enabled/rustapi-cloud
114+
115+ install -m 0644 " ${CLOUD_DIR} /deploy/nginx-rustapi-apps.conf" /etc/nginx/sites-available/rustapi-apps
116+ sed -i " s|__DOMAIN__|${DOMAIN} |g" /etc/nginx/sites-available/rustapi-apps
117+ sed -i " s|__NGINX_MAP_DIR__|${NGINX_MAP_DIR} |g" /etc/nginx/sites-available/rustapi-apps
118+ ln -sf /etc/nginx/sites-available/rustapi-apps /etc/nginx/sites-enabled/rustapi-apps
119+ rm -f /etc/nginx/sites-enabled/default
120+ nginx -t
121+ systemctl reload nginx
122+
123+ systemctl daemon-reload
124+ systemctl enable --now rustapi-cloud
125+
126+ if [[ " ${GITHUB_CLIENT_ID} " == " REPLACE_ME" ]]; then
127+ echo " WARN: Set real GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET in ${CLOUD_DIR} /.env then: systemctl restart rustapi-cloud"
128+ fi
129+
130+ if certbot certificates 2> /dev/null | grep -q " ${DOMAIN} " ; then
131+ certbot renew --quiet || true
132+ else
133+ echo " ==> Obtain TLS certificate (requires DNS A record -> this server)"
134+ certbot --nginx -d " ${DOMAIN} " --non-interactive --agree-tos -m " admin@${DOMAIN#* .} " --redirect || \
135+ echo " WARN: certbot failed — point ${DOMAIN} A record to this server, then run: certbot --nginx -d ${DOMAIN} "
136+ certbot --nginx -d " *.${DOMAIN} " --non-interactive --agree-tos -m " admin@${DOMAIN#* .} " --redirect 2> /dev/null || \
137+ echo " WARN: wildcard TLS for *.${DOMAIN} needs DNS-01 — run: certbot certonly --manual --preferred-challenges dns -d '*.${DOMAIN} '"
138+ fi
139+
140+ echo " ==> Health check"
141+ sleep 3
142+ curl -fsS " http://127.0.0.1:3000/health" && echo
143+ echo " DONE: https://${DOMAIN} /health"
144+ echo " User apps: https://{project}-{user8}.${DOMAIN} "
145+ echo " DNS: A ${DOMAIN} -> server IP, A *.${DOMAIN} -> server IP (wildcard)"
146+ echo " CLI login: cargo rustapi login --cloud-url https://${DOMAIN} "
0 commit comments