import { authenticate } from "../shopify.server"; import { ensureShop, loadShopConfig } from "../services/config.server"; import { features } from "../services/plans"; /** * Authenticate an embedded admin request and load the shop's config + plan features. * If the shop record is missing or marked uninstalled (afterAuth didn't run or failed, or a * reinstall), create/revive it here so the merchant never lands on a 404. */ export async function adminContext(request: Request, opts: { fresh?: boolean } = {}) { const ctx = await authenticate.admin(request); const cfg = await loadShopConfig(ctx.session.shop, opts).catch(async (e: unknown) => { if (!(e instanceof Response) || e.status !== 404) throw e; await ensureShop(ctx.session.shop, ctx.admin); return loadShopConfig(ctx.session.shop, { fresh: true }); }); return { ...ctx, cfg, features: features(cfg.shop.plan), actor: `merchant:${ctx.session.onlineAccessInfo?.associated_user?.email ?? "staff"}` }; } export function formString(fd: FormData, k: string): string { const v = fd.get(k); return typeof v === "string" ? v.trim() : ""; } export function formInt(fd: FormData, k: string): number | null { const s = formString(fd, k); if (s === "") return null; const n = Number(s); return Number.isFinite(n) ? Math.round(n) : null; } export function formMoney(fd: FormData, k: string): number | null { const s = formString(fd, k); if (s === "") return null; const n = Number(s.replace(/[$,]/g, "")); return Number.isFinite(n) ? Math.round(n * 100) : null; } export const formBool = (fd: FormData, k: string) => ["on", "true", "1"].includes(formString(fd, k)); export const formList = (fd: FormData, k: string) => formString(fd, k) .split(/[,\n]/) .map((s) => s.trim()) .filter(Boolean); /** "10:00-13:00, 14:00-18:00" → ranges; throws on bad input. */ export function parseRanges(s: string): { start: string; end: string }[] { if (!s.trim()) return []; return s.split(",").map((part) => { const m = part.trim().match(/^(\d{1,2}):(\d{2})\s*[-–]\s*(\d{1,2}):(\d{2})$/); if (!m) throw new Error(`"${part.trim()}" isn't a time range like 10:00-13:00`); const pad = (h: string) => h.padStart(2, "0"); const start = `${pad(m[1])}:${m[2]}`; const end = `${pad(m[3])}:${m[4]}`; if (start >= end) throw new Error(`${start}–${end}: end must be after start`); return { start, end }; }); }