Files
2026-07-12 10:17:17 -04:00

3.4 KiB

PocketBase Patterns & Pitfalls

Redirect Loop: Stale localStorage Token

Problem: index.html blindly checks localStorage.getItem('pocketbase_auth') (line 131) and redirects to dashboard.html if ANY value exists — including stale/expired tokens. When dashboard.js bounces back to index.html without clearing the stale token, it creates an infinite loop.

Fix: Every JS file that redirects away to index.html on auth failure MUST clear the stale token first:

// In onAuthStateChanged callback, before redirect:
localStorage.removeItem('pocketbase_auth');
window.location.href = 'index.html';

Files: dashboard.js:122, appointments.js:50, repair-orders.js:58

Also add the same pattern in shared/header-functionality.js logout paths.

Auto-Cancelled Requests: PocketBase SDK requestKey

Problem: PocketBase SDK auto-cancels requests that share the same default requestKey. All create calls to the same collection share a key — a rapid second call cancels the first. Manifests as "The request was auto-cancelled" error.

Fix: Pass { requestKey: null } on ALL mutating PocketBase SDK calls in pocketbase.js:

// addDoc
const record = await pb.collection(collName).create(pbData, { requestKey: null });

// updateDoc
const record = await pb.collection(collName).update(docId, pbData, { requestKey: null });

// setDoc (both paths)
return pb.collection(collName).update(records[0].id, pbData, { requestKey: null });
return pb.collection(collName).create(pbData, { requestKey: null });

Double-Submit Guard

Problem: Form submit handler lacks guard against rapid double-clicks. Two simultaneous addDoc calls + PocketBase auto-cancellation = first request cancelled.

Fix: Disable the submit button on first click, re-enable on both success AND error:

function handleCreateROSubmit() {
    const submitBtn = document.getElementById('submit-create-ro');
    if (submitBtn && submitBtn.disabled) return;  // guard
    if (submitBtn) {
        submitBtn.disabled = true;
        submitBtn.textContent = 'Creating...';
    }
    // ... try/catch ...
    // On success:
    submitBtn.disabled = false;
    submitBtn.textContent = 'Create Repair Order';
    // On catch:
    submitBtn.disabled = false;
    submitBtn.textContent = 'Create Repair Order';
}

Block-Scope Bug: const in try Block

Problem: const roData = ... declared inside a try {} block but referenced outside. JavaScript throws a silent ReferenceError in async handlers — crashes with no visible error.

Fix: Declare variables that need to be accessed outside the try before the block:

const roData = repairOrders.find(ro => ro.id === roId);  // outside try
try {
    const servicesLines = roData?.services ? ...;
    // ...
} catch (e) {}
// roData is accessible here

DuckDNS: Force VPS IP (Not Auto-Detect)

Problem: DuckDNS update script runs on home server with &ip= (auto-detect), so DuckDNS records the home IP instead of the VPS IP. Traffic should route VPS → Tailscale → home, but DNS points directly to home.

Fix: Hard-code VPS IP in /opt/duckdns/update.sh:

#!/bin/bash
echo url="https://www.duckdns.org/update?domains=grajmedia&token=TOKEN&ip=51.81.84.34" | curl -k -s -o /opt/duckdns/duck.log -K -

Also check /etc/hosts for local overrides (127.0.0.1 grajmedia.duckdns.org) for local access.