mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-13 13:53:12 -06:00
Three features from this session, full server suite green (535/535). TOTP 2FA (#100) — backend shipped without a UI; add it: - Login: mfa_required -> 6-digit challenge (recovery codes accepted) -> /totp/verify. - Settings > Account: enable (QR + confirm -> recovery codes once), regenerate, disable; SSO accounts see "managed by your identity provider". - /totp/setup returns a server-rendered qr_data_url (bundled qrcode dep). keyuri folds the request Host into the issuer so multi-instance accounts are distinguishable in the authenticator app. Email verification on signup — hosted HARD-block / self-host SOFT-nudge: - email_verified column; existing users asked on first login (SSO + platform admins grandfathered); single-use 24h tokens (SHA-256 hashed). - Gate engages only when email is configured (never locks out a no-mail instance). GET /verify-email + POST /resend-verification (generic, no account enumeration). - Client: "confirm your email" flow + resend, verified/error toasts, self-host banner; onAuthSuccess refuses a tokenless response (defensive). Tizen SSSP URL-Launcher install — Fusion-style one-URL native install: - Server hosts /tizen/sssp_config.xml (dynamic <size>, always matches the served .wgt) + /tizen/ScreenTinker.wgt + a human landing. lib/wgt-cache.js resolves the signed .wgt (/data mount wins, mirroring the APK). - build-wgt.sh also emits a static sssp_config.xml for CDN hosting. - Retail panels require a Samsung Partner cert; dev-mode is SDB self-signed only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
2.8 KiB
JavaScript
74 lines
2.8 KiB
JavaScript
'use strict';
|
|
|
|
// Resolve the Tizen .wgt for the SSSP URL-Launcher install flow (mirrors lib/apk-cache.js).
|
|
// The path/size/mtime are resolved once at boot and refreshed on an interval, so a panel
|
|
// polling /tizen/sssp_config.xml can never turn into a per-request statSync flood.
|
|
//
|
|
// The SIGNED .wgt is provided out-of-band (like the APK): container operators mount it at
|
|
// /data/ScreenTinker.wgt. The in-repo tizen/ copy (usually unsigned, inspection-only) is a
|
|
// last-resort fallback so a dev box still serves *something*.
|
|
//
|
|
// size is the load-bearing field — sssp_config.xml must report the EXACT byte length of the
|
|
// file the panel then downloads, or the install fails. We always derive it from the real file.
|
|
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const config = require('../config');
|
|
|
|
function candidates() {
|
|
return [
|
|
path.join(config.dataDir, 'ScreenTinker.wgt'), // operator mount (signed) — wins
|
|
path.join(__dirname, '..', '..', 'ScreenTinker.wgt'), // repo root (release artifact)
|
|
path.join(__dirname, '..', '..', 'tizen', 'ScreenTinker.wgt'), // in-repo build (usually unsigned)
|
|
];
|
|
}
|
|
|
|
// Version reported in sssp_config.xml <ver>. A panel re-installs when this changes, so it must
|
|
// bump on each release — hence the app version (single source: package.json), overridable via env
|
|
// when an operator hosts a differently-versioned signed build.
|
|
const VERSION = process.env.TIZEN_WGT_VER || (() => {
|
|
try { return require('../package.json').version; } catch (_) { return '1.0.0'; }
|
|
})();
|
|
|
|
let cache = { path: null, exists: false, size: 0, mtime: 0, version: VERSION };
|
|
|
|
function refresh() {
|
|
for (const p of candidates()) {
|
|
try {
|
|
const st = fs.statSync(p);
|
|
cache = { path: p, exists: true, size: st.size, mtime: st.mtimeMs, version: VERSION };
|
|
return cache;
|
|
} catch (_) { /* next candidate */ }
|
|
}
|
|
cache = { path: null, exists: false, size: 0, mtime: 0, version: VERSION };
|
|
return cache;
|
|
}
|
|
|
|
function get() { return cache; }
|
|
|
|
let timer = null;
|
|
function start() {
|
|
refresh();
|
|
if (!timer) {
|
|
timer = setInterval(refresh, config.otaApkRefreshMs); // reuse the APK refresh cadence
|
|
if (timer.unref) timer.unref();
|
|
}
|
|
return cache;
|
|
}
|
|
|
|
// The SSSP manifest the panel fetches at <entered-url>/sssp_config.xml. widgetname (no extension)
|
|
// tells the panel to download <widgetname>.wgt from the same directory — we serve it at
|
|
// /tizen/ScreenTinker.wgt. webtype=tizen marks it a Tizen web app.
|
|
function ssspConfigXml(wgt = cache) {
|
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
<widget>
|
|
\t<ver>${wgt.version}</ver>
|
|
\t<size>${wgt.size}</size>
|
|
\t<widgetname>ScreenTinker</widgetname>
|
|
\t<webtype>tizen</webtype>
|
|
</widget>
|
|
`;
|
|
}
|
|
|
|
module.exports = { start, refresh, get, ssspConfigXml, WIDGET_NAME: 'ScreenTinker' };
|