← back to Govarbitrage

apps/mobile/lib/settings.ts

98 lines

/**
 * Settings persistence — base URL and Basic Auth credentials stored in
 * expo-secure-store so they never touch AsyncStorage (unencrypted).
 *
 * Keys:
 *   GOVARB_BASE_URL   — e.g. "https://auctions.agentabrams.com"
 *   GOVARB_USERNAME   — Basic auth username
 *   GOVARB_PASSWORD   — Basic auth password
 */
import * as SecureStore from "expo-secure-store";
export { buildBasicAuthHeader } from "./auth-headers";

const KEYS = {
  BASE_URL: "GOVARB_BASE_URL",
  USERNAME: "GOVARB_USERNAME",
  PASSWORD: "GOVARB_PASSWORD",
  // Sign in with Apple — the app session JWT issued by /api/auth/apple, plus the
  // account identity to show in Settings. Optional: absent = signed out.
  SESSION_JWT: "GOVARB_SESSION_JWT",
  ACCOUNT_NAME: "GOVARB_ACCOUNT_NAME",
  ACCOUNT_EMAIL: "GOVARB_ACCOUNT_EMAIL",
} as const;

const DEFAULTS = {
  BASE_URL: "https://auctions.agentabrams.com",
  // Empty by default — the default server's read API is public, so a fresh
  // install sends no Authorization header. Credentials are only for users
  // pointing the app at their own self-hosted server.
  USERNAME: "",
  PASSWORD: "",
} as const;

export interface AppSettings {
  baseUrl: string;
  username: string;
  password: string;
}

export async function loadSettings(): Promise<AppSettings> {
  const [baseUrl, username, password] = await Promise.all([
    SecureStore.getItemAsync(KEYS.BASE_URL),
    SecureStore.getItemAsync(KEYS.USERNAME),
    SecureStore.getItemAsync(KEYS.PASSWORD),
  ]);
  return {
    baseUrl: baseUrl ?? DEFAULTS.BASE_URL,
    username: username ?? DEFAULTS.USERNAME,
    password: password ?? DEFAULTS.PASSWORD,
  };
}

export async function saveSettings(settings: AppSettings): Promise<void> {
  await Promise.all([
    SecureStore.setItemAsync(KEYS.BASE_URL, settings.baseUrl.trim()),
    SecureStore.setItemAsync(KEYS.USERNAME, settings.username.trim()),
    SecureStore.setItemAsync(KEYS.PASSWORD, settings.password),
  ]);
}

// ── Sign in with Apple session ──────────────────────────────────────────────

export interface AppleAccount {
  token: string;
  name: string | null;
  email: string | null;
}

/** The signed-in Apple account, or null when signed out. */
export async function loadAppleAccount(): Promise<AppleAccount | null> {
  const [token, name, email] = await Promise.all([
    SecureStore.getItemAsync(KEYS.SESSION_JWT),
    SecureStore.getItemAsync(KEYS.ACCOUNT_NAME),
    SecureStore.getItemAsync(KEYS.ACCOUNT_EMAIL),
  ]);
  if (!token) return null;
  return { token, name: name ?? null, email: email ?? null };
}

export async function saveAppleAccount(account: AppleAccount): Promise<void> {
  await Promise.all([
    SecureStore.setItemAsync(KEYS.SESSION_JWT, account.token),
    account.name
      ? SecureStore.setItemAsync(KEYS.ACCOUNT_NAME, account.name)
      : SecureStore.deleteItemAsync(KEYS.ACCOUNT_NAME),
    account.email
      ? SecureStore.setItemAsync(KEYS.ACCOUNT_EMAIL, account.email)
      : SecureStore.deleteItemAsync(KEYS.ACCOUNT_EMAIL),
  ]);
}

export async function clearAppleAccount(): Promise<void> {
  await Promise.all([
    SecureStore.deleteItemAsync(KEYS.SESSION_JWT),
    SecureStore.deleteItemAsync(KEYS.ACCOUNT_NAME),
    SecureStore.deleteItemAsync(KEYS.ACCOUNT_EMAIL),
  ]);
}