From bfc27c5d26f02795bb73ef84b47089f34535cf14 Mon Sep 17 00:00:00 2001 From: yanyu Date: Wed, 8 Jul 2026 14:43:07 +0800 Subject: [PATCH] add miiot/install.sh --- miiot/install.sh | 141 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 miiot/install.sh diff --git a/miiot/install.sh b/miiot/install.sh new file mode 100644 index 0000000..9a3bf83 --- /dev/null +++ b/miiot/install.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash +# miiot 一键安装/升级脚本(用户侧,Linux)。 +# +# curl -fsSL https://git.lingyu.org.cn/lingyu-note/releases/raw/branch/main/miiot/install.sh | sudo bash +# +# 行为: +# - 自动检测架构(amd64/arm64),从公开发布仓下载最新版,SHA256 校验 +# - 首次:装到 /opt/miiot,生成 .env(随机 token),注册 systemd 并启动 +# - 再次运行:原地升级(保留 .env 与数据;旧二进制自动备份,健康检查失败自动回滚) +# +# 可选: +# VERSION=1.2.0 bash install.sh # 装指定版本(默认最新) +# bash install.sh --uninstall # 卸载(保留 /opt/miiot/data 与 .env) +set -euo pipefail + +APP="miiot" +GITEA="https://git.lingyu.org.cn" +REL_REPO="lingyu-note/releases" +APP_DIR="/opt/${APP}" +SERVICE="${APP}" +PORT=8677 +HEALTH_URL="http://127.0.0.1:${PORT}/healthz" +KEEP_BACKUPS=5 + +[[ "$(id -u)" == "0" ]] || { echo "❌ 请用 root / sudo 运行" >&2; exit 1; } +[[ "$(uname -s)" == "Linux" ]] || { echo "❌ 仅支持 Linux" >&2; exit 1; } +case "$(uname -m)" in + x86_64) ARCH=amd64 ;; + aarch64|arm64) ARCH=arm64 ;; + *) echo "❌ 不支持的架构: $(uname -m)" >&2; exit 1 ;; +esac +command -v curl >/dev/null || { echo "❌ 需要 curl" >&2; exit 1; } + +if [[ "${1:-}" == "--uninstall" ]]; then + systemctl disable --now "${SERVICE}" 2>/dev/null || true + rm -f "/etc/systemd/system/${SERVICE}.service"; systemctl daemon-reload + rm -rf "${APP_DIR}/bin" + echo "✓ 已卸载(${APP_DIR}/data 与 .env 保留,需要请手动删除)" + exit 0 +fi + +# ---- 解析版本:VERSION 环境变量,或发布仓里 tag 前缀 miiot-v 的最新 release ---- +if [[ -n "${VERSION:-}" ]]; then + VER="${VERSION#v}" +else + VER=$(curl -fsSL "${GITEA}/api/v1/repos/${REL_REPO}/releases?limit=50" \ + | python3 -c " +import json,sys +rels=[r for r in json.load(sys.stdin) if r['tag_name'].startswith('${APP}-v')] +print(rels[0]['tag_name'].removeprefix('${APP}-v') if rels else '')" 2>/dev/null) || VER="" + [[ -n "$VER" ]] || { echo "❌ 查询最新版本失败(发布仓暂无 ${APP} release?)" >&2; exit 1; } +fi +TAG="${APP}-v${VER}" +PKG="${APP}_${VER}_linux_${ARCH}.tar.gz" +BASE="${GITEA}/${REL_REPO}/releases/download/${TAG}" + +# 已是该版本且服务健康 → 幂等退出 +if [[ -x "${APP_DIR}/bin/${APP}" && -f "${APP_DIR}/.version" ]] \ + && [[ "$(cat "${APP_DIR}/.version")" == "${VER}" ]] \ + && curl -sf -o /dev/null "${HEALTH_URL}" 2>/dev/null; then + echo "✓ 已是最新版本 v${VER},无需升级"; exit 0 +fi + +echo "▶ 下载 ${PKG} ..." +TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT +curl -fsSL -o "${TMP}/${PKG}" "${BASE}/${PKG}" +curl -fsSL -o "${TMP}/SHA256SUMS" "${BASE}/SHA256SUMS" +(cd "${TMP}" && grep " ${PKG}\$" SHA256SUMS | sha256sum -c - >/dev/null) \ + || { echo "❌ SHA256 校验失败" >&2; exit 1; } +tar -xzf "${TMP}/${PKG}" -C "${TMP}" + +mkdir -p "${APP_DIR}/bin/backup" "${APP_DIR}/data" + +# ---- 首次:生成 .env + systemd unit ---- +if [[ ! -f "${APP_DIR}/.env" ]]; then + echo "▶ 首次安装:生成 ${APP_DIR}/.env(请按需修改后 systemctl restart ${SERVICE})" + rnd() { head -c 32 /dev/urandom | od -An -tx1 | tr -d ' \n'; } + cat > "${APP_DIR}/.env" < "/etc/systemd/system/${SERVICE}.service" </dev/null +fi + +# ---- 替换二进制(旧的备份,保留最近 N 个)---- +if [[ -f "${APP_DIR}/bin/${APP}" ]]; then + cp "${APP_DIR}/bin/${APP}" "${APP_DIR}/bin/backup/${APP}.$(date +%Y%m%d%H%M%S)" + ls -t "${APP_DIR}/bin/backup/" | tail -n +$((KEEP_BACKUPS+1)) \ + | xargs -r -I{} rm -f "${APP_DIR}/bin/backup/{}" +fi +install -m 755 "${TMP}/${APP}" "${APP_DIR}/bin/${APP}" +echo "${VER}" > "${APP_DIR}/.version" + +echo "▶ 重启 + 健康检查 ..." +systemctl restart "${SERVICE}" +ok="" +for i in $(seq 1 10); do + sleep 1 + curl -sf -o /dev/null "${HEALTH_URL}" && { ok=1; break; } +done +if [[ -z "$ok" ]]; then + echo "✗ 健康检查失败" >&2 + journalctl -u "${SERVICE}" -n 20 --no-pager || true + latest=$(ls -t "${APP_DIR}/bin/backup/" 2>/dev/null | head -1 || true) + if [[ -n "$latest" ]]; then + echo " 回滚到 ${latest}" >&2 + cp "${APP_DIR}/bin/backup/${latest}" "${APP_DIR}/bin/${APP}" + systemctl restart "${SERVICE}" + fi + exit 1 +fi +echo "✅ ${APP} v${VER} 运行中 — 端口 ${PORT},配置 ${APP_DIR}/.env" +echo " 日志:journalctl -u ${SERVICE} -f 升级:重跑本脚本即可"