88 lines
3.4 KiB
Markdown
88 lines
3.4 KiB
Markdown
# 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:
|
|
|
|
```javascript
|
|
// 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`:
|
|
|
|
```javascript
|
|
// 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:
|
|
|
|
```javascript
|
|
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:
|
|
|
|
```javascript
|
|
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`:
|
|
|
|
```bash
|
|
#!/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.
|