CampgroundTickets/app/lib/storage.ts
Hank 0e8fe3bb9a 4-digit auto-submit PIN + operator name in audit logs
Login: fixed-length 4-digit PIN that auto-submits on the 4th digit (no submit
button to scroll to on small iPhone screens) and clears on a wrong PIN.
Compact, vertically-centered keypad so it fits without scrolling.

Operator tracking: after PIN auth, staff enter their name (new /operator
screen, persisted per device). The name is sent as X-Operator on every authed
request and recorded on each check-in/undo/ice audit entry (new Operator
column), so logs show who did what. Shown in the scanner header and the admin
audit view.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 16:45:23 +00:00

84 lines
2.2 KiB
TypeScript

import { Platform } from "react-native";
// Token persistence: localStorage on web, SecureStore on native.
const KEY = "campscan.token";
const OPERATOR_KEY = "campscan.operator";
export async function saveToken(token: string): Promise<void> {
if (Platform.OS === "web") {
try {
window.localStorage.setItem(KEY, token);
} catch {
/* ignore */
}
return;
}
const SecureStore = await import("expo-secure-store");
await SecureStore.setItemAsync(KEY, token);
}
export async function loadToken(): Promise<string | null> {
if (Platform.OS === "web") {
try {
return window.localStorage.getItem(KEY);
} catch {
return null;
}
}
const SecureStore = await import("expo-secure-store");
return SecureStore.getItemAsync(KEY);
}
export async function clearToken(): Promise<void> {
if (Platform.OS === "web") {
try {
window.localStorage.removeItem(KEY);
} catch {
/* ignore */
}
return;
}
const SecureStore = await import("expo-secure-store");
await SecureStore.deleteItemAsync(KEY);
}
// Operator (gate staff) name — plain persistence, not sensitive. Kept in
// localStorage on both platforms for simplicity (SecureStore is overkill here;
// on native we still use localStorage-less AsyncStorage-free approach below).
export async function saveOperator(name: string): Promise<void> {
if (Platform.OS === "web") {
try {
window.localStorage.setItem(OPERATOR_KEY, name);
} catch {
/* ignore */
}
return;
}
const SecureStore = await import("expo-secure-store");
await SecureStore.setItemAsync(OPERATOR_KEY, name);
}
export async function loadOperator(): Promise<string | null> {
if (Platform.OS === "web") {
try {
return window.localStorage.getItem(OPERATOR_KEY);
} catch {
return null;
}
}
const SecureStore = await import("expo-secure-store");
return SecureStore.getItemAsync(OPERATOR_KEY);
}
export async function clearOperator(): Promise<void> {
if (Platform.OS === "web") {
try {
window.localStorage.removeItem(OPERATOR_KEY);
} catch {
/* ignore */
}
return;
}
const SecureStore = await import("expo-secure-store");
await SecureStore.deleteItemAsync(OPERATOR_KEY);
}