Add multi-user accounts with per-user data scoping and 2FA

Projects were previously global to anyone who could reach the server.
Adds email/password accounts with httpOnly cookie sessions, scopes
every project (both SQLite and localStorage) to the signed-in user,
and auto-claims pre-existing unowned projects for whoever registers
first. Also adds optional TOTP two-factor auth with backup codes,
managed from a new Account Settings page, since there's no
password-reset flow to fall back on otherwise.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
nmemmert
2026-07-06 08:49:33 -04:00
parent bf83dc7cc4
commit 5140958305
13 changed files with 1524 additions and 127 deletions
+10 -2
View File
@@ -75,8 +75,16 @@ chown -R "$SERVICE_USER":"$SERVICE_USER" "$INSTALL_DIR"
# ── systemd unit ──────────────────────────────────────────────────────────────
echo "Installing systemd unit..."
sed "s#/opt/study-app#$INSTALL_DIR#g; s#User=study-app#User=$SERVICE_USER#; s#Group=study-app#Group=$SERVICE_USER#" \
"$ROOT_DIR/deploy/study-app.service" > "/etc/systemd/system/${SERVICE_NAME}.service"
EXISTING_UNIT="/etc/systemd/system/${SERVICE_NAME}.service"
# Reuse the existing session secret across re-installs (upgrades) so signed-in
# users aren't logged out; only generate a new one on first install.
if [ -f "$EXISTING_UNIT" ] && grep -q '^Environment=SESSION_SECRET=' "$EXISTING_UNIT"; then
SESSION_SECRET="$(grep '^Environment=SESSION_SECRET=' "$EXISTING_UNIT" | head -1 | cut -d= -f3-)"
else
SESSION_SECRET="$(openssl rand -hex 32)"
fi
sed "s#/opt/study-app#$INSTALL_DIR#g; s#User=study-app#User=$SERVICE_USER#; s#Group=study-app#Group=$SERVICE_USER#; s#__SESSION_SECRET__#$SESSION_SECRET#" \
"$ROOT_DIR/deploy/study-app.service" > "$EXISTING_UNIT"
systemctl daemon-reload
systemctl enable "$SERVICE_NAME"