fix: accept 7zz binary (brew sevenzip), install LibreOffice with --no-quarantine, ngrok authtoken fallback in pm2 config
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 3 changed files with +34 and −5
modified
deploy/ecosystem.config.cjs
+16 −0
@@ -14,6 +14,21 @@ const APP_DIR = '/srv/drive/app'; | ||
| 14 | 14 | const DATA_DIR = '/srv/drive'; |
| 15 | 15 | const LOG_DIR = '/srv/drive/logs'; |
| 16 | 16 | |
| 17 | +// deploy/ngrok.yml expands ${NGROK_AUTHTOKEN}; when the env var isn't set, | |
| 18 | +// fall back to the token already stored in the agent's default config. | |
| 19 | +function ngrokAuthtoken() { | |
| 20 | + if (process.env.NGROK_AUTHTOKEN) return process.env.NGROK_AUTHTOKEN; | |
| 21 | + try { | |
| 22 | + const fs = require('node:fs'); | |
| 23 | + const os = require('node:os'); | |
| 24 | + const agentCfg = `${os.homedir()}/Library/Application Support/ngrok/ngrok.yml`; | |
| 25 | + const match = fs.readFileSync(agentCfg, 'utf8').match(/authtoken:\s*(\S+)/); | |
| 26 | + return match ? match[1] : ''; | |
| 27 | + } catch { | |
| 28 | + return ''; | |
| 29 | + } | |
| 30 | +} | |
| 31 | + | |
| 17 | 32 | module.exports = { |
| 18 | 33 | apps: [ |
| 19 | 34 | { |
@@ -43,6 +58,7 @@ module.exports = { | ||
| 43 | 58 | script: 'ngrok', |
| 44 | 59 | args: ['start', 'spbdrive', '--config', `${APP_DIR}/deploy/ngrok.yml`], |
| 45 | 60 | interpreter: 'none', |
| 61 | + env: { NGROK_AUTHTOKEN: ngrokAuthtoken() }, | |
| 46 | 62 | autorestart: true, |
| 47 | 63 | restart_delay: 5000, |
| 48 | 64 | out_file: `${LOG_DIR}/tunnel.out.log`, |
modified
deploy/setup-m3u96b.sh
+5 −1
@@ -28,7 +28,11 @@ log "Installing system packages (ffmpeg, libreoffice, poppler, 7z, ngrok, pm2)" | ||
| 28 | 28 | "$BREW" list ffmpeg >/dev/null 2>&1 || "$BREW" install ffmpeg |
| 29 | 29 | "$BREW" list poppler >/dev/null 2>&1 || "$BREW" install poppler # pdftotext + pdftoppm |
| 30 | 30 | "$BREW" list sevenzip >/dev/null 2>&1 || "$BREW" install sevenzip # 7z |
| 31 | −"$BREW" list --cask libreoffice >/dev/null 2>&1 || "$BREW" install --cask libreoffice | |
| 31 | +if ! "$BREW" list --cask libreoffice >/dev/null 2>&1 && [ ! -d /Applications/LibreOffice.app ]; then | |
| 32 | + # --no-quarantine avoids the hdiutil Gatekeeper prompt on headless installs. | |
| 33 | + "$BREW" install --cask --no-quarantine libreoffice || \ | |
| 34 | + echo "⚠ LibreOffice install failed — office previews will fall back to download cards" >&2 | |
| 35 | +fi | |
| 32 | 36 | command -v ngrok >/dev/null 2>&1 || "$BREW" install ngrok |
| 33 | 37 | command -v node >/dev/null 2>&1 || "$BREW" install node@20 |
| 34 | 38 | command -v pm2 >/dev/null 2>&1 || npm install -g pm2 |
modified
src/preview/archive.mjs
+13 −4
@@ -22,6 +22,13 @@ import { hasBin } from './thumbs.mjs'; | ||
| 22 | 22 | |
| 23 | 23 | const execFileP = promisify(execFile); |
| 24 | 24 | const MAX_ENTRIES = 5000; |
| 25 | + | |
| 26 | +/** Homebrew's sevenzip ships `7zz`; p7zip ships `7z`. Accept either. */ | |
| 27 | +async function sevenZipBin() { | |
| 28 | + if (await hasBin('7z')) return '7z'; | |
| 29 | + if (await hasBin('7zz')) return '7zz'; | |
| 30 | + return null; | |
| 31 | +} | |
| 25 | 32 | const MAX_MEMBER_BYTES = 200 * 1024 * 1024; |
| 26 | 33 | |
| 27 | 34 | /** Archive kind from filename ('zip' | 'tar' | 'tgz' | '7z' | 'rar' | null). */ |
@@ -95,8 +102,9 @@ async function listTar(file, kind) { | ||
| 95 | 102 | } |
| 96 | 103 | |
| 97 | 104 | async function listWith7z(file) { |
| 98 | − if (!(await hasBin('7z'))) return null; | |
| 99 | − const { stdout } = await execFileP('7z', ['l', '-slt', '-ba', file], { | |
| 105 | + const bin = await sevenZipBin(); | |
| 106 | + if (!bin) return null; | |
| 107 | + const { stdout } = await execFileP(bin, ['l', '-slt', '-ba', file], { | |
| 100 | 108 | timeout: 60_000, maxBuffer: 32 * 1024 * 1024, |
| 101 | 109 | }); |
| 102 | 110 | const entries = []; |
@@ -177,8 +185,9 @@ export async function extractMember(sha, name, memberPath) { | ||
| 177 | 185 | } |
| 178 | 186 | |
| 179 | 187 | if (kind === '7z' || kind === 'rar') { |
| 180 | − if (!(await hasBin('7z'))) return null; | |
| 181 | − const child = execFile('7z', ['x', '-so', file, memberPath], { | |
| 188 | + const bin = await sevenZipBin(); | |
| 189 | + if (!bin) return null; | |
| 190 | + const child = execFile(bin, ['x', '-so', file, memberPath], { | |
| 182 | 191 | timeout: 5 * 60_000, maxBuffer: MAX_MEMBER_BYTES, |
| 183 | 192 | }); |
| 184 | 193 | const out = new PassThrough(); |
| 185 | 194 | |