feat(web): download the Android APK from Settings

Backend serves a staged APK so co-users self-serve the latest build:
- GET /api/apk streams APK_PATH (default /data/novasonic.apk) with the
  android package content-type + attachment disposition; 404 if absent.
- GET /api/apk/info reports availability, size, mtime, and an optional
  version from /data/novasonic.version.txt.
- Settings gets a "🤖 Android App" section that appears only when an APK is
  present and shows version/size/date.

Maintainer updates the APK with:
  docker cp app-release.apk novasonic-backend:/data/novasonic.apk

Android source is already tracked; only build artifacts stay out of git.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 4.8 committed 2026-08-25 18:00:36 +02:00
1 parent 7996971ba0
commit 908b50bcd9
4 files changed
+67

No files matched your search

+27
View File
@@ -466,6 +466,33 @@ app.get('/api/events-debug/:artist', (req, res) => {
}).on('error', e => res.json({ error: e.message }));
});
// ── NovaSonic Android APK distribution ──────────────────────────────────────
// The web app offers a "download the Android app" button that points here, so co-users always get
// the latest APK without the maintainer hand-delivering it. Drop the latest release build into the
// writable data volume (APK_PATH, default /data/novasonic.apk):
// docker cp app-release.apk novasonic-backend:/data/novasonic.apk
// Optional: a sidecar /data/novasonic.version.txt with a version string (e.g. "2.2") shown in the UI.
const APK_PATH = process.env.APK_PATH || path.join(CONFIG.dataPath, 'novasonic.apk');
const APK_VER_PATH = path.join(CONFIG.dataPath, 'novasonic.version.txt');
app.get('/api/apk/info', (req, res) => {
fs.stat(APK_PATH, (err, st) => {
if (err) return res.json({ available: false });
let version = null;
try { version = fs.readFileSync(APK_VER_PATH, 'utf8').trim() || null; } catch {}
res.json({ available: true, size: st.size, updatedAt: st.mtimeMs, version });
});
});
app.get('/api/apk', (req, res) => {
fs.stat(APK_PATH, (err) => {
if (err) return res.status(404).json({ error: 'Keine APK hinterlegt' });
res.setHeader('Content-Type', 'application/vnd.android.package-archive');
res.setHeader('Content-Disposition', 'attachment; filename="NovaSonic.apk"');
res.sendFile(APK_PATH);
});
});
app.get('/api/health', (req, res) => {
res.json({
status: 'ok', ytdlpVersion,