Add real first-time provisioning to install.sh (v2.14.0)
Until now ./install.sh only managed an already-installed kiosk; ubuntu-based-kiosk.sh was still the only path from a bare Ubuntu Server box to a running one. install.sh now provisions from scratch too: packages, kiosk user, Node.js/Electron, LightDM+Openbox autologin, audio/video/HDMI/power-button hardware setup, and the firewall, then hands off to the already-migrated Core Settings/Advanced menus for initial configuration instead of reimplementing that logic again. - lib/provision.sh: the new provisioning flow, built mostly by calling existing menus (core_settings_menu, emergency hotspot, virtual consoles) - cuts it to ~300 lines against the legacy script's ~4,000-line first_time_install(). - lib/electron.sh: electron_install_binary() extracted out of menus/advanced_electron.sh so provisioning and the existing "Fix blank screen" action share one implementation. - kiosk-app/ and provision/files/: the Electron app source and every system template file, extracted byte-for-byte out of ubuntu-based-kiosk.sh's heredocs into real files. - Found and fixed a bash set -e gotcha along the way: testing a multi-statement function as an if-condition (`if ! some_func; then`) silently exempts everything inside that function from set -e for the duration of the call. Fixed in the new provisioning code and in menus/advanced_electron.sh's pre-existing repair action, which had the same shape. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VfsFSoRqfbRG7XAg5RoE7e
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
background: #2c3e50;
|
||||
color: #ecf0f1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
.container { width: 100%; max-width: 400px; }
|
||||
h2 { text-align: center; margin-bottom: 30px; font-size: 24px; }
|
||||
#pin-display {
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
font-size: 48px;
|
||||
text-align: center;
|
||||
margin: 20px 0;
|
||||
border: 3px solid #34495e;
|
||||
border-radius: 12px;
|
||||
background: #34495e;
|
||||
color: #ecf0f1;
|
||||
letter-spacing: 20px;
|
||||
min-height: 90px;
|
||||
line-height: 50px;
|
||||
font-family: monospace;
|
||||
}
|
||||
#error { color: #e74c3c; text-align: center; display: none; margin: 10px 0; font-weight: bold; font-size: 18px; }
|
||||
.numpad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin: 20px 0; }
|
||||
.numpad button {
|
||||
padding: 30px;
|
||||
font-size: 32px;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
background: #34495e;
|
||||
color: #ecf0f1;
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
.numpad button:active { background: #3498db; }
|
||||
.numpad button:hover { background: #475d6d; }
|
||||
.actions { display: grid; grid-template-columns: 1fr 1fr; gap: 15px; margin-top: 20px; }
|
||||
.btn { padding: 20px; font-size: 20px; cursor: pointer; border: none; border-radius: 12px; font-weight: bold; }
|
||||
#clear { background: #f39c12; color: white; }
|
||||
#backspace { background: #e67e22; color: white; }
|
||||
#submit { background: #27ae60; color: white; }
|
||||
#cancel { background: #e74c3c; color: white; }
|
||||
.info { text-align: center; font-size: 14px; color: #95a5a6; margin-top: 20px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>🔒 Enter PIN</h2>
|
||||
<div id="pin-display">••••</div>
|
||||
<div id="error">❌ Incorrect PIN</div>
|
||||
<div class="numpad">
|
||||
<button onclick="addDigit('1')">1</button>
|
||||
<button onclick="addDigit('2')">2</button>
|
||||
<button onclick="addDigit('3')">3</button>
|
||||
<button onclick="addDigit('4')">4</button>
|
||||
<button onclick="addDigit('5')">5</button>
|
||||
<button onclick="addDigit('6')">6</button>
|
||||
<button onclick="addDigit('7')">7</button>
|
||||
<button onclick="addDigit('8')">8</button>
|
||||
<button onclick="addDigit('9')">9</button>
|
||||
<button id="clear" onclick="clearPin()">Clear</button>
|
||||
<button onclick="addDigit('0')">0</button>
|
||||
<button id="backspace" onclick="backspace()">⌫</button>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="btn" id="submit" onclick="submitPin()">✓ Submit</button>
|
||||
<button class="btn" id="cancel" onclick="cancel()">✗ Cancel</button>
|
||||
</div>
|
||||
<div class="info">Default PIN: 1234 (4-8 digits)</div>
|
||||
</div>
|
||||
<script>
|
||||
const {ipcRenderer} = require('electron');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const pinFile = path.join(__dirname, '.jitsi-pin');
|
||||
let correctPin = '1234';
|
||||
let enteredPin = '';
|
||||
try {
|
||||
const stored = fs.readFileSync(pinFile, 'utf8').trim();
|
||||
if (stored !== 'NOPIN') correctPin = stored;
|
||||
else correctPin = null;
|
||||
} catch(e) {}
|
||||
function updateDisplay() {
|
||||
const display = document.getElementById('pin-display');
|
||||
if (enteredPin.length === 0) {
|
||||
display.textContent = '••••';
|
||||
display.style.color = '#7f8c8d';
|
||||
} else {
|
||||
display.textContent = '•'.repeat(enteredPin.length);
|
||||
display.style.color = '#ecf0f1';
|
||||
}
|
||||
}
|
||||
function addDigit(digit) {
|
||||
if (enteredPin.length < 8) {
|
||||
enteredPin += digit;
|
||||
updateDisplay();
|
||||
document.getElementById('error').style.display = 'none';
|
||||
}
|
||||
}
|
||||
function backspace() { enteredPin = enteredPin.slice(0, -1); updateDisplay(); }
|
||||
function clearPin() { enteredPin = ''; updateDisplay(); document.getElementById('error').style.display = 'none'; }
|
||||
function submitPin() {
|
||||
if (enteredPin.length < 4) {
|
||||
document.getElementById('error').textContent = '❌ PIN must be 4-8 digits';
|
||||
document.getElementById('error').style.display = 'block';
|
||||
return;
|
||||
}
|
||||
if (correctPin === null || enteredPin === correctPin) {
|
||||
ipcRenderer.send('pin-correct');
|
||||
} else {
|
||||
document.getElementById('error').textContent = '❌ Incorrect PIN';
|
||||
document.getElementById('error').style.display = 'block';
|
||||
enteredPin = '';
|
||||
updateDisplay();
|
||||
}
|
||||
}
|
||||
function cancel() { ipcRenderer.send('pin-cancelled'); }
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key >= '0' && e.key <= '9') addDigit(e.key);
|
||||
else if (e.key === 'Backspace') backspace();
|
||||
else if (e.key === 'Enter') submitPin();
|
||||
else if (e.key === 'Escape') cancel();
|
||||
});
|
||||
updateDisplay();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user