feat(selfhost): complete Supabase removal migration
Deploy to route.crispygoat.com / deploy (push) Failing after 9s

Phase 1 — Pattern library
- Add src/lib/api.ts (typed PostgREST client, anon-key)
- Add src/lib/svc-fetch.ts (service-role fetch + PostgREST client)
- Add src/lib/db-types.ts (Database generic, RowOf helper)
- Add src/lib/auth-admin.ts (better-auth admin wrappers: createUser, listUsers, removeUser, requestPasswordReset, validateSession)

Phase 2 — Server action migration
- Migrate all 67 server actions off @supabase/supabase-js to svcApi/svcRpc
- Replace service.auth.admin.* with authAdmin.* (better-auth)
- Replace publicApi.auth.* with authAdmin.* (better-auth)
- Add typed single() null guards + nullable column fallbacks

Phase 3 — Delete mock data + debug routes
- Remove if(useMockData) branches from 7 files (-226 lines)
- Delete src/lib/mock-data.ts (no longer referenced)
- Delete 7 debug API routes (debug-admin-users, debug-auth, debug-cookie, debug-env, debug-get-admin-users, debug-hello, debug-me)

Phase 4 — Hard cut
- Delete src/lib/supabase.ts (orphan — no importers)
- Delete src/app/api/supabase + src/app/api/supabase-test routes
- Remove @supabase/ssr + @supabase/supabase-js from package.json
- Rename env vars: NEXT_PUBLIC_SUPABASE_URL → NEXT_PUBLIC_API_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY → NEXT_PUBLIC_API_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY → POSTGREST_SERVICE_KEY

Phase 5 — Verify
- npx tsc --noEmit: 0 errors
- npm run lint: 0 new errors from migration (104 pre-existing errors unrelated)
- npm run build: same failure mode as main worktree (PostgREST-dependent prerender), no regression

Net: 158 files changed, +1640 / -1903 lines (-263 net)
This commit is contained in:
2026-06-05 20:17:02 +00:00
parent e7ac495831
commit d892b3f64f
158 changed files with 1640 additions and 1903 deletions
+5 -5
View File
@@ -5,13 +5,13 @@ POSTGRES_DB=route_commerce
# Used by the app and push-migrations.js to talk to the local DB # Used by the app and push-migrations.js to talk to the local DB
DATABASE_URL=postgresql://routecommerce:routecommerce_dev_password@127.0.0.1:5432/route_commerce?schema=public DATABASE_URL=postgresql://routecommerce:routecommerce_dev_password@127.0.0.1:5432/route_commerce?schema=public
# --- PostgREST (REST API — replaces Supabase REST) --- # --- PostgREST (REST API) ---
# Server actions call `${NEXT_PUBLIC_SUPABASE_URL}/rest/v1/rpc/...` # Server actions call `${NEXT_PUBLIC_API_URL}/rest/v1/rpc/...`
# Point that URL at the local PostgREST container. Anon key can be anything # Point that URL at the local PostgREST container. Anon key can be anything
# non-empty since we handle auth at the app layer (Better Auth + dev_session). # non-empty since we handle auth at the app layer (Better Auth + dev_session).
NEXT_PUBLIC_SUPABASE_URL=http://localhost:3001 NEXT_PUBLIC_API_URL=http://localhost:3001
NEXT_PUBLIC_SUPABASE_ANON_KEY=local-postgrest-anon-key NEXT_PUBLIC_API_ANON_KEY=local-postgrest-anon-key
SUPABASE_SERVICE_ROLE_KEY=local-postgrest-service-key POSTGREST_SERVICE_KEY=local-postgrest-service-key
# --- MinIO (S3-compatible object storage — replaces Supabase Storage) --- # --- MinIO (S3-compatible object storage — replaces Supabase Storage) ---
MINIO_ROOT_USER=routecommerce MINIO_ROOT_USER=routecommerce
-2
View File
@@ -21,8 +21,6 @@
"@google/generative-ai": "^0.24.1", "@google/generative-ai": "^0.24.1",
"@gsap/react": "^2.1.2", "@gsap/react": "^2.1.2",
"@sentry/nextjs": "^10.55.0", "@sentry/nextjs": "^10.55.0",
"@supabase/ssr": "^0.10.2",
"@supabase/supabase-js": "^2.105.3",
"@upstash/ratelimit": "^2.0.8", "@upstash/ratelimit": "^2.0.8",
"@upstash/redis": "^1.38.0", "@upstash/redis": "^1.38.0",
"better-auth": "^1.6.14", "better-auth": "^1.6.14",
+3 -14
View File
@@ -1,15 +1,6 @@
"use server"; "use server";
import { createClient as createServiceClient } from "@supabase/supabase-js"; import { svcRpc } from "@/lib/svc-fetch";
function getServiceClient() {
const roleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!roleKey) throw new Error("SUPABASE_SERVICE_ROLE_KEY is not set");
return createServiceClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
roleKey,
);
}
type AdminActionPayload = { type AdminActionPayload = {
action_type: "create" | "update" | "delete"; action_type: "create" | "update" | "delete";
@@ -30,8 +21,7 @@ type UserActivityPayload = {
export async function logAdminAction(payload: AdminActionPayload): Promise<void> { export async function logAdminAction(payload: AdminActionPayload): Promise<void> {
try { try {
const service = getServiceClient(); await svcRpc("log_admin_action", {
await service.rpc("log_admin_action", {
p_payload: { p_payload: {
action_type: payload.action_type, action_type: payload.action_type,
admin_id: payload.admin_id ?? null, admin_id: payload.admin_id ?? null,
@@ -48,8 +38,7 @@ export async function logAdminAction(payload: AdminActionPayload): Promise<void>
export async function logUserActivity(payload: UserActivityPayload): Promise<void> { export async function logUserActivity(payload: UserActivityPayload): Promise<void> {
try { try {
const service = getServiceClient(); await svcRpc("log_user_activity", {
await service.rpc("log_user_activity", {
p_payload: { p_payload: {
user_id: payload.user_id, user_id: payload.user_id,
activity_type: payload.activity_type, activity_type: payload.activity_type,
+3 -8
View File
@@ -1,7 +1,7 @@
"use server"; "use server";
import { cookies } from "next/headers"; import { cookies } from "next/headers";
import { createClient as createServiceClient } from "@supabase/supabase-js"; import { svcRpc } from "@/lib/svc-fetch";
export async function updatePasswordAction( export async function updatePasswordAction(
newPassword: string newPassword: string
@@ -15,16 +15,11 @@ export async function updatePasswordAction(
return { error: "Not authenticated. Please log in again." }; return { error: "Not authenticated. Please log in again." };
} }
const service = createServiceClient( const { error } = await svcRpc("update_user_password", {
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.SUPABASE_SERVICE_ROLE_KEY!
);
const { error } = await service.rpc("update_user_password", {
p_user_id: uid, p_user_id: uid,
p_password: newPassword, p_password: newPassword,
}); });
if (error) return { error: error.message }; if (error) return { error: error.message };
return {}; return {};
} }
+7 -21
View File
@@ -1,15 +1,6 @@
"use server"; "use server";
import { createClient as createServiceClient } from "@supabase/supabase-js"; import * as authAdmin from "@/lib/auth-admin";
function getServiceClient() {
const roleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!roleKey) throw new Error("SUPABASE_SERVICE_ROLE_KEY not set");
return createServiceClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
roleKey,
);
}
export type ResetAdminPasswordResult = export type ResetAdminPasswordResult =
| { success: true; tempPassword: string } | { success: true; tempPassword: string }
@@ -24,24 +15,19 @@ export async function resetAdminPassword(
email: string, email: string,
newPassword: string newPassword: string
): Promise<ResetAdminPasswordResult> { ): Promise<ResetAdminPasswordResult> {
const service = getServiceClient();
// Look up auth user by email // Look up auth user by email
const { data: authUsers, error: listError } = await service.auth.admin.listUsers(); const { data: users, error: listError } = await authAdmin.listUsers({ searchValue: email });
if (listError || !authUsers?.users) { if (listError) {
return { success: false, error: "Could not list users: " + listError?.message }; return { success: false, error: "Could not list users: " + listError.message };
} }
const authUser = authUsers.users.find((u) => u.email?.toLowerCase() === email.toLowerCase()); const authUser = (users ?? []).find((u) => u.email?.toLowerCase() === email.toLowerCase());
if (!authUser) { if (!authUser) {
return { success: false, error: "No auth user found for that email address." }; return { success: false, error: "No auth user found for that email address." };
} }
// Update password via service role // Update password via the better-auth admin wrapper (uses `update_user_password` RPC internally)
const { error: updateError } = await service.auth.admin.updateUserById( const { error: updateError } = await authAdmin.setUserPassword(authUser.id, newPassword);
authUser.id,
{ password: newPassword, email_confirm: true }
);
if (updateError) { if (updateError) {
return { success: false, error: updateError.message }; return { success: false, error: updateError.message };
+93 -203
View File
@@ -1,14 +1,10 @@
"use server"; "use server";
import { cookies, headers } from "next/headers"; import { cookies, headers } from "next/headers";
import { createServerClient } from "@supabase/ssr"; import { api as publicApi } from "@/lib/api";
import { NextRequest } from "next/server"; import { auth } from "@/lib/auth";
import { NextResponse } from "next/server"; import { svcApi, svcRpc } from "@/lib/svc-fetch";
import { createClient as createServiceClient } from "@supabase/supabase-js"; import * as authAdmin from "@/lib/auth-admin";
import { supabase as publicSupabase } from "@/lib/supabase";
import { getMockTableData, mockBrands } from "@/lib/mock-data";
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
export type AdminUserRow = { export type AdminUserRow = {
id: string; id: string;
@@ -75,66 +71,44 @@ export type UpdateAdminUserInput = {
phone_number?: string | null; phone_number?: string | null;
}; };
// ─── SSR client for authenticated requests ───────────────────────────────── // ─── Auth helper: validate session via better-auth ──────────────────────────
async function getAuthClient() { /**
const cookieStore = await cookies(); * Read rc_auth_uid from the raw HTTP Cookie header. This is set by the
* Emergency Force Login flow and acts as a dev-mode bypass for normal auth.
*/
async function readRcAuthUid(): Promise<string | null> {
const headerStore = await headers(); const headerStore = await headers();
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!;
const request = new NextRequest("http://localhost/admin", { headers: new Headers() });
const response = NextResponse.next({ request });
// Read rc_auth_uid from the raw HTTP Cookie header (document.cookie sets
// cookies that arrive in the header but NOT in next/headers cookies()).
const cookieHeader = headerStore.get("cookie") || ""; const cookieHeader = headerStore.get("cookie") || "";
const allCookies = cookieHeader.split(";").map(c => c.trim()); const allCookies = cookieHeader.split(";").map(c => c.trim());
const rcUidCookie = allCookies.find(c => c.startsWith("rc_auth_uid=")); const rcUidCookie = allCookies.find(c => c.startsWith("rc_auth_uid="));
const rcAuthUid = rcUidCookie ? rcUidCookie.split("=")[1] : null; return rcUidCookie ? rcUidCookie.split("=")[1] : null;
const supabase = createServerClient(supabaseUrl, supabaseAnonKey, {
cookies: {
getAll() { return cookieStore.getAll(); },
setAll(cookiesToSet, headers) {
cookiesToSet.forEach(({ name, value, options }) => response.cookies.set(name, value, options));
Object.entries(headers).forEach(([key, value]) => response.headers.set(key, value));
},
},
});
return { supabase, response, rcAuthUid };
} }
/**
* Authenticated RPC call. Validates the session via better-auth, then calls
* the named RPC using the service-role client.
*
* In development, the DEV_FORCE_UID cookie bypasses the auth check (the
* Emergency Force Login path is itself authenticated upstream).
*/
async function callRpcWithAuth<T>(fn: string, params: Record<string, unknown>): Promise<{ data: T | null; error: string | null }> { async function callRpcWithAuth<T>(fn: string, params: Record<string, unknown>): Promise<{ data: T | null; error: string | null }> {
const { supabase, rcAuthUid } = await getAuthClient();
// Dev force-login UID bypasses Supabase auth entirely
const DEV_FORCE_UID = "dev-user-00000000-0000-0000-0000-000000000001"; const DEV_FORCE_UID = "dev-user-00000000-0000-0000-0000-000000000001";
const rcAuthUid = await readRcAuthUid();
if (rcAuthUid === DEV_FORCE_UID) { if (rcAuthUid === DEV_FORCE_UID) {
return { data: null, error: null }; // let the action proceed without auth check return { data: null, error: null }; // dev force-login bypass
} }
const { data: userData, error: userError } = await supabase.auth.getUser(); const headerStore = await headers();
if (userError || !userData.user) { const session = await auth.api.getSession({ headers: headerStore });
if (!session?.user) {
return { data: null, error: "Not authenticated" }; return { data: null, error: "Not authenticated" };
} }
const { data, error } = await supabase.rpc(fn, params as Record<string, unknown>);
if (error) { /* RPC error handled silently */ } const { data, error } = await svcRpc<T>(fn, params);
return { data: data as T, error: error ? error.message : null }; return { data: data as T, error: error ? error.message : null };
} }
// ─── Service role client (server-only, never exposed to browser) ───────────
function getServiceClient() {
const roleKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!roleKey) {
throw new Error("SUPABASE_SERVICE_ROLE_KEY is not set. Cannot use service role in dev path.");
}
return createServiceClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
roleKey,
);
}
// ─── Dev-only path — uses service role to create auth user + admin_users ── // ─── Dev-only path — uses service role to create auth user + admin_users ──
async function devCreateAdminUser(input: CreateAdminUserInput): Promise<{ user: AdminUserRow | null; error: string | null }> { async function devCreateAdminUser(input: CreateAdminUserInput): Promise<{ user: AdminUserRow | null; error: string | null }> {
@@ -147,27 +121,21 @@ async function devCreateAdminUser(input: CreateAdminUserInput): Promise<{ user:
return { user: null, error: "Not authenticated" }; return { user: null, error: "Not authenticated" };
} }
const service = getServiceClient(); // Create auth user with the provided password (better-auth signUpEmail)
const { data: authUser, error: authError } = await authAdmin.createUser({
// Create auth user with the provided password
const { data: authUser, error: authError } = await service.auth.admin.createUser({
email: input.email, email: input.email,
password: input.password, password: input.password,
email_confirm: true, name: input.display_name || input.email.split("@")[0],
user_metadata: {
display_name: input.display_name || input.email.split("@")[0],
phone_number: input.phone_number ?? null,
},
}); });
if (authError || !authUser.user) { if (authError || !authUser) {
return { user: null, error: authError?.message ?? "Failed to create auth user" }; return { user: null, error: authError?.message ?? "Failed to create auth user" };
} }
// Insert into admin_users // Insert into admin_users (service role bypasses RLS)
const { data: inserted, error: insertError } = await service const { data: inserted, error: insertError } = await svcApi
.from("admin_users") .from("admin_users")
.insert({ .insert({
user_id: authUser.user.id, user_id: authUser.id,
role: input.role, role: input.role,
brand_id: input.brand_id, brand_id: input.brand_id,
display_name: input.display_name || input.email.split("@")[0], display_name: input.display_name || input.email.split("@")[0],
@@ -185,11 +153,14 @@ async function devCreateAdminUser(input: CreateAdminUserInput): Promise<{ user:
must_change_password: input.mustChangePassword ?? true, must_change_password: input.mustChangePassword ?? true,
}) })
.select() .select()
.single(); .single() as { data: any; error: { message: string } | null };
if (insertError) { if (insertError) {
return { user: null, error: insertError.message }; return { user: null, error: insertError.message };
} }
if (!inserted) {
return { user: null, error: "Insert returned no row" };
}
// Send welcome email // Send welcome email
try { try {
@@ -263,27 +234,24 @@ function mapUserRow(row: Record<string, unknown>): AdminUserRow {
// ─── Dev path helpers (service role, local only) ─────────────────────────── // ─── Dev path helpers (service role, local only) ───────────────────────────
async function devListAdminUsers(callerUid?: string): Promise<{ users: AdminUserRow[]; error: string | null }> { async function devListAdminUsers(callerUid?: string): Promise<{ users: AdminUserRow[]; error: string | null }> {
const service = getServiceClient();
// Ensure caller has an admin_users record // Ensure caller has an admin_users record
if (callerUid) { if (callerUid) {
const { data: existing } = await service const { data: existing } = await svcApi
.from("admin_users") .from("admin_users")
.select("id") .select("id")
.eq("user_id", callerUid) .eq("user_id", callerUid)
.maybeSingle(); .maybeSingle() as { data: any };
if (!existing) { if (!existing) {
// auto-creating admin_users for uid // auto-creating admin_users for uid
const { data: authData } = await service.auth.admin.listUsers(); const { data: listData } = await authAdmin.listUsers({ searchValue: undefined });
const authUser = authData?.users?.find((u) => u.id === callerUid); const authUser = (listData ?? []).find((u: any) => u.id === callerUid);
const meta = (authUser as { user_metadata?: Record<string, unknown> })?.user_metadata; await svcApi.from("admin_users").insert({
await service.from("admin_users").insert({
user_id: callerUid, user_id: callerUid,
role: "platform_admin", role: "platform_admin",
brand_id: null, brand_id: null,
display_name: (meta?.display_name as string | null) ?? authUser?.email?.split("@")[0] ?? "Admin", display_name: authUser?.name ?? authUser?.email?.split("@")[0] ?? "Admin",
phone_number: (meta?.phone_number as string | null) ?? null, phone_number: null,
can_manage_products: true, can_manage_products: true,
can_manage_stops: true, can_manage_stops: true,
can_manage_orders: true, can_manage_orders: true,
@@ -300,7 +268,7 @@ async function devListAdminUsers(callerUid?: string): Promise<{ users: AdminUser
} }
// Fetch all admin_users rows (no RLS for service role) // Fetch all admin_users rows (no RLS for service role)
const { data: adminRows, error: adminError } = await service const { data: adminRows, error: adminError } = await svcApi
.from("admin_users") .from("admin_users")
.select(` .select(`
id, user_id, role, brand_id, active, must_change_password, created_at, last_login, id, user_id, role, brand_id, active, must_change_password, created_at, last_login,
@@ -308,57 +276,30 @@ async function devListAdminUsers(callerUid?: string): Promise<{ users: AdminUser
can_manage_messages, can_manage_refunds, can_manage_users, can_manage_water_log, can_manage_reports, can_manage_messages, can_manage_refunds, can_manage_users, can_manage_water_log, can_manage_reports,
brands (name) brands (name)
`) `)
.order("created_at", { ascending: false }); .order("created_at", { ascending: false }) as { data: any; error: any };
if (adminError) return { users: [], error: adminError.message }; if (adminError) return { users: [], error: adminError.message };
// Fetch auth user details via service role admin API // Fetch auth user details via better-auth admin list
const { data: authData, error: authError } = await service.auth.admin.listUsers(); const { data: listData, error: authError } = await authAdmin.listUsers({ searchValue: undefined });
if (authError) return { users: [], error: authError.message }; if (authError) return { users: [], error: authError.message };
const authMap: Record<string, { email: string; display_name: string | null; phone_number: string | null }> = {}; const authMap: Record<string, { email: string; display_name: string | null }> = {};
(authData?.users ?? []).forEach((u) => { (listData ?? []).forEach((u: any) => {
const user = u as { id: string; email?: string; user_metadata?: Record<string, unknown> };
authMap[user.id] = {
email: user.email ?? "",
display_name: (user.user_metadata?.display_name as string | null) ?? (user.user_metadata?.full_name as string | null) ?? null,
phone_number: (user.user_metadata?.phone_number as string | null) ?? null,
};
});
const users: AdminUserRow[] = (adminRows ?? []).map((row) => {
const r = row as Record<string, unknown> & { brands?: { name?: string } };
const authInfo = authMap[String(r.user_id ?? "")] ?? { email: "", display_name: null, phone_number: null };
return {
...mapUserRow(r),
email: authInfo.email || "No Email",
display_name: authInfo.display_name ?? null,
phone_number: authInfo.phone_number ?? (r.phone_number as string | null) ?? null,
brand_name: r.brands?.name ?? null,
};
});
return { users, error: null };
}
function buildUsersFromRows(adminRows: Record<string, unknown>[], authUsers: { id: string; email?: string; user_metadata?: Record<string, unknown> }[]): { users: AdminUserRow[]; error: string | null } {
const authMap: Record<string, { email: string; display_name: string | null; phone_number: string | null }> = {};
(authUsers ?? []).forEach((u) => {
authMap[u.id] = { authMap[u.id] = {
email: u.email ?? "", email: u.email ?? "",
display_name: (u.user_metadata?.display_name as string | null) ?? (u.user_metadata?.full_name as string | null) ?? null, display_name: u.name ?? null,
phone_number: (u.user_metadata?.phone_number as string | null) ?? null,
}; };
}); });
const users: AdminUserRow[] = adminRows.map((row) => { const users: AdminUserRow[] = (adminRows ?? []).map((row: any) => {
const r = row as Record<string, unknown> & { brands?: { name?: string } }; const r = row as Record<string, unknown> & { brands?: { name?: string } };
const authInfo = authMap[String(r.user_id ?? "")] ?? { email: "", display_name: null, phone_number: null }; const authInfo = authMap[String(r.user_id ?? "")] ?? { email: "", display_name: null };
return { return {
...mapUserRow(r), ...mapUserRow(r),
email: authInfo.email || "No Email", email: authInfo.email || "No Email",
display_name: authInfo.display_name ?? null, display_name: authInfo.display_name ?? null,
phone_number: authInfo.phone_number ?? (r.phone_number as string | null) ?? null, phone_number: (r.phone_number as string | null) ?? null,
brand_name: r.brands?.name ?? null, brand_name: r.brands?.name ?? null,
}; };
}); });
@@ -371,15 +312,6 @@ function buildUsersFromRows(adminRows: Record<string, unknown>[], authUsers: { i
const DEV_FORCE_UID = "dev-user-00000000-0000-0000-0000-000000000001"; const DEV_FORCE_UID = "dev-user-00000000-0000-0000-0000-000000000001";
export async function getAdminUsers(brandId?: string): Promise<{ users: AdminUserRow[]; error: string | null }> { export async function getAdminUsers(brandId?: string): Promise<{ users: AdminUserRow[]; error: string | null }> {
if (useMockData) {
const mockUsers = getMockTableData("users") as AdminUserRow[];
let filteredUsers = mockUsers;
if (brandId) {
filteredUsers = mockUsers.filter(u => u.brand_id === brandId);
}
return { users: filteredUsers, error: null };
}
const cookieStore = await cookies(); const cookieStore = await cookies();
const headerStore = await headers(); const headerStore = await headers();
const devSession = cookieStore.get("dev_session")?.value; const devSession = cookieStore.get("dev_session")?.value;
@@ -390,7 +322,7 @@ export async function getAdminUsers(brandId?: string): Promise<{ users: AdminUse
.find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null; .find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null;
// In development mode: ALL requests with a valid rc_auth_uid use the dev/service path. // In development mode: ALL requests with a valid rc_auth_uid use the dev/service path.
// This includes real Supabase auth users — bypassing supabase.auth.getUser JWT validation. // This includes real Supabase auth users — bypassing api.auth.getUser JWT validation.
if (process.env.NODE_ENV !== "production" && rcAuthUid && rcAuthUid !== DEV_FORCE_UID) { if (process.env.NODE_ENV !== "production" && rcAuthUid && rcAuthUid !== DEV_FORCE_UID) {
return devListAdminUsers(rcAuthUid); return devListAdminUsers(rcAuthUid);
} }
@@ -406,38 +338,8 @@ export async function getAdminUsers(brandId?: string): Promise<{ users: AdminUse
// Production path: try authenticated RPC first, fall back to service role if not authenticated // Production path: try authenticated RPC first, fall back to service role if not authenticated
const result = await callRpcWithAuth<AdminUserRow[]>("get_admin_users", { p_brand_id: brandId ?? null }); const result = await callRpcWithAuth<AdminUserRow[]>("get_admin_users", { p_brand_id: brandId ?? null });
if (result.error === "Not authenticated" && rcAuthUid) { if (result.error === "Not authenticated" && rcAuthUid) {
// No Supabase session token in browser — use service role with rc_auth_uid // No better-auth session token in browser — use service role via devListAdminUsers
const service = getServiceClient(); return devListAdminUsers(rcAuthUid);
const { data: adminRows, error: adminError } = await service
.from("admin_users")
.select(`id, user_id, role, brand_id, active, must_change_password, created_at, last_login,
can_manage_products, can_manage_stops, can_manage_orders, can_manage_pickup,
can_manage_messages, can_manage_refunds, can_manage_users, can_manage_water_log, can_manage_reports,
brands (name)`)
.order("created_at", { ascending: false });
if (adminError) return { users: [], error: adminError.message };
const { data: authData } = await service.auth.admin.listUsers();
const authMap: Record<string, { email: string; display_name: string | null; phone_number: string | null }> = {};
(authData?.users ?? []).forEach((u) => {
const user = u as { id: string; email?: string; user_metadata?: Record<string, unknown> };
authMap[user.id] = {
email: user.email ?? "",
display_name: (user.user_metadata?.display_name as string | null) ?? null,
phone_number: (user.user_metadata?.phone_number as string | null) ?? null,
};
});
const users: AdminUserRow[] = (adminRows ?? []).map((row) => {
const r = row as Record<string, unknown> & { brands?: { name?: string } };
const authInfo = authMap[String(r.user_id ?? "")] ?? { email: "", display_name: null, phone_number: null };
return {
...mapUserRow(r),
email: authInfo.email || "No Email",
display_name: authInfo.display_name ?? null,
phone_number: authInfo.phone_number ?? (r.phone_number as string | null) ?? null,
brand_name: r.brands?.name ?? null,
};
});
return { users, error: null };
} }
return { users: result.data ?? [], error: result.error }; return { users: result.data ?? [], error: result.error };
} }
@@ -466,27 +368,21 @@ export async function createAdminUser(input: CreateAdminUserInput): Promise<{ us
// Production path — use service role directly (bypasses Supabase JWT auth) // Production path — use service role directly (bypasses Supabase JWT auth)
// rc_auth_uid cookie proves the admin is logged in; service role creates the account // rc_auth_uid cookie proves the admin is logged in; service role creates the account
if (rcAuthUid) { if (rcAuthUid) {
const service = getServiceClient(); // Create auth user via better-auth
const { data: authUser, error: authError } = await authAdmin.createUser({
// Create auth user
const { data: authUser, error: authError } = await service.auth.admin.createUser({
email: input.email, email: input.email,
password: input.password, password: input.password,
email_confirm: true, name: input.display_name || input.email.split("@")[0],
user_metadata: {
display_name: input.display_name || input.email.split("@")[0],
phone_number: input.phone_number ?? null,
},
}); });
if (authError || !authUser.user) { if (authError || !authUser) {
return { user: null, error: authError?.message ?? "Failed to create auth user" }; return { user: null, error: authError?.message ?? "Failed to create auth user" };
} }
// Insert into admin_users // Insert into admin_users via service-role PostgREST
const { data: inserted, error: insertError } = await service const { data: inserted, error: insertError } = await svcApi
.from("admin_users") .from("admin_users")
.insert({ .insert({
user_id: authUser.user.id, user_id: authUser.id,
role: input.role, role: input.role,
brand_id: input.brand_id, brand_id: input.brand_id,
display_name: input.display_name || input.email.split("@")[0], display_name: input.display_name || input.email.split("@")[0],
@@ -506,7 +402,9 @@ export async function createAdminUser(input: CreateAdminUserInput): Promise<{ us
.select() .select()
.single(); .single();
if (insertError) return { user: null, error: insertError.message }; if (insertError || !inserted) {
return { user: null, error: insertError?.message ?? "Insert returned no row" };
}
// Send welcome email // Send welcome email
try { try {
@@ -525,26 +423,26 @@ export async function createAdminUser(input: CreateAdminUserInput): Promise<{ us
return { return {
user: { user: {
id: inserted.id, id: inserted.id ?? "",
user_id: inserted.user_id, user_id: inserted.user_id ?? authUser.id,
display_name: inserted.display_name ?? input.display_name ?? input.email.split("@")[0], display_name: inserted.display_name ?? input.display_name ?? input.email.split("@")[0],
email: input.email, email: input.email,
phone_number: inserted.phone_number ?? input.phone_number ?? null, phone_number: inserted.phone_number ?? input.phone_number ?? null,
role: inserted.role, role: (inserted.role as AdminUserRow["role"]) ?? "store_employee",
brand_id: inserted.brand_id, brand_id: inserted.brand_id ?? null,
brand_name: null, brand_name: null,
can_manage_products: inserted.can_manage_products, can_manage_products: inserted.can_manage_products ?? false,
can_manage_stops: inserted.can_manage_stops, can_manage_stops: inserted.can_manage_stops ?? false,
can_manage_orders: inserted.can_manage_orders, can_manage_orders: inserted.can_manage_orders ?? false,
can_manage_pickup: inserted.can_manage_pickup, can_manage_pickup: inserted.can_manage_pickup ?? false,
can_manage_messages: inserted.can_manage_messages, can_manage_messages: inserted.can_manage_messages ?? false,
can_manage_refunds: inserted.can_manage_refunds, can_manage_refunds: inserted.can_manage_refunds ?? false,
can_manage_users: inserted.can_manage_users, can_manage_users: inserted.can_manage_users ?? false,
can_manage_water_log: inserted.can_manage_water_log, can_manage_water_log: inserted.can_manage_water_log ?? false,
can_manage_reports: inserted.can_manage_reports, can_manage_reports: inserted.can_manage_reports ?? false,
active: inserted.active, active: inserted.active ?? true,
must_change_password: inserted.must_change_password ?? true, must_change_password: inserted.must_change_password ?? true,
created_at: inserted.created_at, created_at: inserted.created_at ?? new Date().toISOString(),
last_login: null, last_login: null,
}, },
error: null, error: null,
@@ -564,8 +462,7 @@ export async function updateAdminUser(input: UpdateAdminUserInput): Promise<{ us
.find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null; .find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null;
// DEV_FORCE_UID bypass — Emergency Force Login sets this cookie directly // DEV_FORCE_UID bypass — Emergency Force Login sets this cookie directly
if (rcAuthUid === DEV_FORCE_UID) { if (rcAuthUid === DEV_FORCE_UID) {
const service = getServiceClient(); const { data, error } = await svcApi
const { data, error } = await service
.from("admin_users") .from("admin_users")
.update({ .update({
role: input.role ?? undefined, role: input.role ?? undefined,
@@ -586,8 +483,8 @@ export async function updateAdminUser(input: UpdateAdminUserInput): Promise<{ us
.eq("id", input.id) .eq("id", input.id)
.select() .select()
.single(); .single();
if (error) return { user: null, error: error.message }; if (error || !data) return { user: null, error: error?.message ?? "Update returned no row" };
return { user: mapUserRow(data), error: null }; return { user: mapUserRow(data as Record<string, unknown>), error: null };
} }
const result = await callRpcWithAuth<AdminUserRow[]>("update_admin_user", { const result = await callRpcWithAuth<AdminUserRow[]>("update_admin_user", {
@@ -613,20 +510,19 @@ export async function deleteAdminUser(id: string): Promise<{ success: boolean; e
.find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null; .find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null;
// DEV_FORCE_UID bypass — Emergency Force Login sets this cookie directly // DEV_FORCE_UID bypass — Emergency Force Login sets this cookie directly
if (rcAuthUid === DEV_FORCE_UID) { if (rcAuthUid === DEV_FORCE_UID) {
const service = getServiceClient();
// Get user_id first // Get user_id first
const { data: adminRow, error: fetchError } = await service const { data: adminRow, error: fetchError } = await svcApi
.from("admin_users") .from("admin_users")
.select("user_id") .select("user_id")
.eq("id", id) .eq("id", id)
.single(); .single();
if (fetchError) return { success: false, error: fetchError.message }; if (fetchError) return { success: false, error: fetchError.message };
// Delete from admin_users // Delete from admin_users
const { error: deleteError } = await service.from("admin_users").delete().eq("id", id); const { error: deleteError } = await svcApi.from("admin_users").delete().eq("id", id);
if (deleteError) return { success: false, error: deleteError.message }; if (deleteError) return { success: false, error: deleteError.message };
// Delete auth user // Delete auth user via better-auth admin
if (adminRow?.user_id) { if (adminRow?.user_id) {
await service.auth.admin.deleteUser(adminRow.user_id); await authAdmin.removeUser(adminRow.user_id);
} }
return { success: true, error: null }; return { success: true, error: null };
} }
@@ -643,30 +539,24 @@ export async function setMustChangePassword(userId: string): Promise<{ success:
.find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null; .find(c => c.startsWith("rc_auth_uid="))?.split("=")[1] ?? null;
if (rcAuthUid === DEV_FORCE_UID || process.env.NODE_ENV !== "production") { if (rcAuthUid === DEV_FORCE_UID || process.env.NODE_ENV !== "production") {
const service = getServiceClient(); const { error } = await svcApi.from("admin_users").update({ must_change_password: true }).eq("id", userId);
const { error } = await service.from("admin_users").update({ must_change_password: true }).eq("id", userId);
return { success: !error, error: error?.message ?? null }; return { success: !error, error: error?.message ?? null };
} }
// Production path — use service role via direct update // Production path — use service role via direct update
const service = getServiceClient(); const { error } = await svcApi.from("admin_users").update({ must_change_password: true }).eq("id", userId);
const { error } = await service.from("admin_users").update({ must_change_password: true }).eq("id", userId);
return { success: !error, error: error?.message ?? null }; return { success: !error, error: error?.message ?? null };
} }
export async function sendPasswordResetEmail(email: string): Promise<{ success: boolean; error: string | null }> { export async function sendPasswordResetEmail(email: string): Promise<{ success: boolean; error: string | null }> {
const { error } = await publicSupabase.auth.resetPasswordForEmail(email, { const { error } = await authAdmin.requestPasswordReset({
email,
redirectTo: `${process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000"}/change-password`, redirectTo: `${process.env.NEXT_PUBLIC_BASE_URL ?? "http://localhost:3000"}/change-password`,
}); });
return { success: !error, error: error?.message ?? null }; return { success: !error, error: error?.message ?? null };
} }
export async function getBrands(): Promise<{ brands: { id: string; name: string }[]; error: string | null }> { export async function getBrands(): Promise<{ brands: { id: string; name: string }[]; error: string | null }> {
if (useMockData) { const { data, error } = await publicApi.from("brands").select("id, name").order("name");
const brands = mockBrands.map(b => ({ id: b.id, name: b.name }));
return { brands, error: null };
}
const { data, error } = await publicSupabase.from("brands").select("id, name").order("name");
return { brands: data ?? [], error: error?.message ?? null }; return { brands: data ?? [], error: error?.message ?? null };
} }
+3 -3
View File
@@ -1,6 +1,6 @@
"use server"; "use server";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
export type AIAuthConfig = { export type AIAuthConfig = {
provider: string; provider: string;
@@ -18,7 +18,7 @@ export async function getAIPreferences(brandId: string): Promise<{
model?: string; model?: string;
max_tokens?: number; max_tokens?: number;
} | null> { } | null> {
const { data, error } = await supabase const { data, error } = await api
.from("brand_ai_settings") .from("brand_ai_settings")
.select("api_key, organization_id, base_url, model, max_tokens") .select("api_key, organization_id, base_url, model, max_tokens")
.eq("brand_id", brandId) .eq("brand_id", brandId)
@@ -32,7 +32,7 @@ export async function saveAIPreferences(
brandId: string, brandId: string,
config: AIAuthConfig config: AIAuthConfig
): Promise<{ success: boolean; error?: string }> { ): Promise<{ success: boolean; error?: string }> {
const { error } = await supabase const { error } = await api
.from("brand_ai_settings") .from("brand_ai_settings")
.upsert({ .upsert({
brand_id: brandId, brand_id: brandId,
+2 -2
View File
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// ── Types ──────────────────────────────────────────────────────────────────── // ── Types ────────────────────────────────────────────────────────────────────
+2 -2
View File
@@ -27,8 +27,8 @@ type AuditResult =
* Audit writes bypass RLS via the SECURITY DEFINER log_audit_event RPC function. * Audit writes bypass RLS via the SECURITY DEFINER log_audit_event RPC function.
*/ */
export async function logAuditEvent(payload: AuditPayload): Promise<AuditResult> { export async function logAuditEvent(payload: AuditPayload): Promise<AuditResult> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
+2 -2
View File
@@ -39,8 +39,8 @@ import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { ADDONS, PLAN_TIERS, type AddonKey, type PlanTierKey } from "@/lib/pricing"; import { ADDONS, PLAN_TIERS, type AddonKey, type PlanTierKey } from "@/lib/pricing";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
export type BillingSubscriptionStatus = export type BillingSubscriptionStatus =
| "active" | "active"
+2 -2
View File
@@ -32,8 +32,8 @@ export async function createRetailStripeCheckoutSession(
})); }));
// Get brand name for Stripe metadata // Get brand name for Stripe metadata
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
let brandName = "Route Commerce"; let brandName = "Route Commerce";
try { try {
const brandRes = await fetch( const brandRes = await fetch(
+4 -4
View File
@@ -43,8 +43,8 @@ export async function createStripeCheckoutSession(
const priceId = getPriceId(priceKey); const priceId = getPriceId(priceKey);
if (!priceId) return { success: false, error: `No Stripe price configured for "${priceKey}". Set ${priceKey.toUpperCase()}_PRICE in your environment.` }; if (!priceId) return { success: false, error: `No Stripe price configured for "${priceKey}". Set ${priceKey.toUpperCase()}_PRICE in your environment.` };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Get brand's Stripe customer ID // Get brand's Stripe customer ID
const custRes = await fetch( const custRes = await fetch(
@@ -123,8 +123,8 @@ export async function cancelAddonSubscription(
const stripeKey = process.env.STRIPE_SECRET_KEY; const stripeKey = process.env.STRIPE_SECRET_KEY;
if (!stripeKey) return { success: false, error: "Stripe not configured" }; if (!stripeKey) return { success: false, error: "Stripe not configured" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Get active subscription for this brand // Get active subscription for this brand
const subRes = await fetch( const subRes = await fetch(
+12 -12
View File
@@ -13,8 +13,8 @@ export async function getStripeBillingPortalUrl(brandId: string): Promise<{ succ
const stripeKey = process.env.STRIPE_SECRET_KEY; const stripeKey = process.env.STRIPE_SECRET_KEY;
if (!stripeKey) return { success: false, error: "Stripe not configured" }; if (!stripeKey) return { success: false, error: "Stripe not configured" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Get stripe_customer_id from brands table // Get stripe_customer_id from brands table
const custRes = await fetch( const custRes = await fetch(
@@ -49,8 +49,8 @@ export async function updateBrandPlanTier(brandId: string, planTier: string): Pr
return { success: false, error: "Invalid plan tier" }; return { success: false, error: "Invalid plan tier" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_brand_plan_tier`, `${supabaseUrl}/rest/v1/rpc/update_brand_plan_tier`,
@@ -72,8 +72,8 @@ export async function updateBrandStripeCustomerId(brandId: string, stripeCustome
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_brand_stripe_customer_id`, `${supabaseUrl}/rest/v1/rpc/update_brand_stripe_customer_id`,
@@ -89,8 +89,8 @@ export async function updateBrandStripeCustomerId(brandId: string, stripeCustome
} }
export async function getBrandPlanInfo(brandId: string): Promise<{ success: boolean; data?: any; error?: string }> { export async function getBrandPlanInfo(brandId: string): Promise<{ success: boolean; data?: any; error?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_brand_plan_info`, `${supabaseUrl}/rest/v1/rpc/get_brand_plan_info`,
@@ -108,8 +108,8 @@ export async function getBrandPlanInfo(brandId: string): Promise<{ success: bool
} }
export async function getEnabledAddons(brandId: string): Promise<Record<string, boolean>> { export async function getEnabledAddons(brandId: string): Promise<Record<string, boolean>> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_brand_features`, `${supabaseUrl}/rest/v1/rpc/get_brand_features`,
@@ -128,8 +128,8 @@ export async function getEnabledAddons(brandId: string): Promise<Record<string,
} }
export async function getRecentWholesaleOrders(brandId: string, limit = 20): Promise<any[]> { export async function getRecentWholesaleOrders(brandId: string, limit = 20): Promise<any[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_orders`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_orders`,
+12 -12
View File
@@ -50,8 +50,8 @@ export async function uploadBrandLogo(
return { success: false, error: `Upload failed: ${(e as Error).message}` }; return { success: false, error: `Upload failed: ${(e as Error).message}` };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const saveRes = await fetch( const saveRes = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`,
@@ -112,8 +112,8 @@ export async function uploadOlatheSweetLogo(
return { success: false, error: `Upload failed: ${(e as Error).message}` }; return { success: false, error: `Upload failed: ${(e as Error).message}` };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const saveRes = await fetch( const saveRes = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`,
@@ -174,8 +174,8 @@ export async function uploadOlatheSweetLogoDark(
return { success: false, error: `Upload failed: ${(e as Error).message}` }; return { success: false, error: `Upload failed: ${(e as Error).message}` };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const saveRes = await fetch( const saveRes = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`,
@@ -246,8 +246,8 @@ export type SaveBrandSettingsResult =
| { success: false; error: string }; | { success: false; error: string };
export async function getBrandSettings(brandId: string): Promise<GetBrandSettingsResult> { export async function getBrandSettings(brandId: string): Promise<GetBrandSettingsResult> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_brand_settings`, `${supabaseUrl}/rest/v1/rpc/get_brand_settings`,
@@ -265,8 +265,8 @@ export async function getBrandSettings(brandId: string): Promise<GetBrandSetting
// Public version for storefront pages — uses slug, no auth required // Public version for storefront pages — uses slug, no auth required
export async function getBrandSettingsPublic(brandSlug: string): Promise<GetBrandSettingsResult & { wholesaleEnabled?: boolean | null }> { export async function getBrandSettingsPublic(brandSlug: string): Promise<GetBrandSettingsResult & { wholesaleEnabled?: boolean | null }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_brand_settings_by_slug`, `${supabaseUrl}/rest/v1/rpc/get_brand_settings_by_slug`,
@@ -331,8 +331,8 @@ export async function saveBrandSettings(params: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_brand_settings`,
+8 -8
View File
@@ -64,8 +64,8 @@ export async function createOrder(
brandId?: string, brandId?: string,
shippingAddress?: ShippingAddress shippingAddress?: ShippingAddress
): Promise<CheckoutResult> { ): Promise<CheckoutResult> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// ── Calculate tax if brand collects tax ───────────────────────────────── // ── Calculate tax if brand collects tax ─────────────────────────────────
let taxAmount = 0; let taxAmount = 0;
@@ -150,8 +150,8 @@ export async function createOrder(
// ── Cart Persistence ────────────────────────────────────────────────────────── // ── Cart Persistence ──────────────────────────────────────────────────────────
export async function getServerCart(userId: string): Promise<CartItem[]> { export async function getServerCart(userId: string): Promise<CartItem[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
try { try {
const response = await fetch( const response = await fetch(
@@ -177,8 +177,8 @@ export async function mergeLocalCart(
): Promise<{ merged: CartItem[] }> { ): Promise<{ merged: CartItem[] }> {
if (!localCart || localCart.length === 0) return { merged: [] }; if (!localCart || localCart.length === 0) return { merged: [] };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Fetch server cart // Fetch server cart
let serverCart: CartItem[] = []; let serverCart: CartItem[] = [];
@@ -243,8 +243,8 @@ export async function mergeLocalCart(
} }
export async function clearServerCart(userId: string): Promise<void> { export async function clearServerCart(userId: string): Promise<void> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
try { try {
await fetch( await fetch(
+8 -8
View File
@@ -59,8 +59,8 @@ export async function getCommunicationCampaigns(brandId?: string): Promise<ListC
const effectiveBrandId = brandId ?? adminUser.brand_id ?? null; const effectiveBrandId = brandId ?? adminUser.brand_id ?? null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_campaigns`, `${supabaseUrl}/rest/v1/rpc/get_communication_campaigns`,
@@ -97,8 +97,8 @@ export async function upsertCampaign(params: {
return { success: false, error: "Not authorized to operate on this brand's campaigns" }; return { success: false, error: "Not authorized to operate on this brand's campaigns" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_communication_campaign`, `${supabaseUrl}/rest/v1/rpc/upsert_communication_campaign`,
@@ -139,8 +139,8 @@ export async function deleteCampaign(campaignId: string, brandId?: string): Prom
return { success: false, error: "Not authorized to delete this brand's campaigns" }; return { success: false, error: "Not authorized to delete this brand's campaigns" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_communication_campaign`, `${supabaseUrl}/rest/v1/rpc/delete_communication_campaign`,
@@ -159,8 +159,8 @@ export async function getCampaignById(campaignId: string, brandId?: string): Pro
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return null; if (!adminUser) return null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_campaign_by_id`, `${supabaseUrl}/rest/v1/rpc/get_communication_campaign_by_id`,
+12 -12
View File
@@ -109,8 +109,8 @@ export async function getContacts(params: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_contacts`, `${supabaseUrl}/rest/v1/rpc/get_communication_contacts`,
@@ -172,8 +172,8 @@ export async function upsertContact(contact: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_communication_contact`, `${supabaseUrl}/rest/v1/rpc/upsert_communication_contact`,
@@ -228,8 +228,8 @@ export async function importContactsBatch(params: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/import_communication_contacts_batch`, `${supabaseUrl}/rest/v1/rpc/import_communication_contacts_batch`,
@@ -271,8 +271,8 @@ export async function optOutContact(params: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/opt_out_contact`, `${supabaseUrl}/rest/v1/rpc/opt_out_contact`,
@@ -302,8 +302,8 @@ export async function deleteContact(id: string, brandId?: string): Promise<{ suc
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_communication_contact`, `${supabaseUrl}/rest/v1/rpc/delete_communication_contact`,
@@ -353,8 +353,8 @@ export async function exportContacts(params: {
if (!effectiveBrandId) return { success: false, error: "No brand context" }; if (!effectiveBrandId) return { success: false, error: "No brand context" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const allContacts: Contact[] = []; const allContacts: Contact[] = [];
let offset = 0; let offset = 0;
@@ -66,8 +66,8 @@ export async function processBucketImport(
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/process_contact_import_from_url`, `${supabaseUrl}/rest/v1/rpc/process_contact_import_from_url`,
+6 -6
View File
@@ -33,8 +33,8 @@ export async function getCommunicationSegments(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_segments`, `${supabaseUrl}/rest/v1/rpc/get_communication_segments`,
@@ -64,8 +64,8 @@ export async function upsertSegment(params: {
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_communication_segment`, `${supabaseUrl}/rest/v1/rpc/upsert_communication_segment`,
@@ -99,8 +99,8 @@ export async function deleteSegment(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_communication_segment`, `${supabaseUrl}/rest/v1/rpc/delete_communication_segment`,
+6 -6
View File
@@ -21,8 +21,8 @@ export async function previewCampaignAudience(
return null; return null;
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/preview_campaign_audience`, `${supabaseUrl}/rest/v1/rpc/preview_campaign_audience`,
@@ -86,8 +86,8 @@ export async function getMessageLogs(params: {
return { success: false, error: "Not authorized to view these logs" }; return { success: false, error: "Not authorized to view these logs" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_message_logs`, `${supabaseUrl}/rest/v1/rpc/get_message_logs`,
@@ -128,8 +128,8 @@ export async function sendCampaign(campaignId: string, brandId?: string): Promis
return { success: false, error: "Not authorized to send this brand's campaigns" }; return { success: false, error: "Not authorized to send this brand's campaigns" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/send_campaign`, `${supabaseUrl}/rest/v1/rpc/send_campaign`,
+4 -4
View File
@@ -24,8 +24,8 @@ export type UpsertSettingsResult = {
}; };
export async function getCommunicationSettings(brandId: string): Promise<CommunicationSettings | null> { export async function getCommunicationSettings(brandId: string): Promise<CommunicationSettings | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_settings`, `${supabaseUrl}/rest/v1/rpc/get_communication_settings`,
@@ -57,8 +57,8 @@ export async function upsertCommunicationSettings(params: {
return { success: false, error: "Not authorized to modify this brand's communication settings" }; return { success: false, error: "Not authorized to modify this brand's communication settings" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_communication_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_communication_settings`,
+2 -2
View File
@@ -22,8 +22,8 @@ export async function sendStopBlast(params: {
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/send_stop_blast`, `${supabaseUrl}/rest/v1/rpc/send_stop_blast`,
+6 -6
View File
@@ -40,8 +40,8 @@ export async function getCommunicationTemplates(brandId?: string): Promise<{ suc
return { success: true, templates: [] }; return { success: true, templates: [] };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_templates`, `${supabaseUrl}/rest/v1/rpc/get_communication_templates`,
@@ -70,8 +70,8 @@ export async function upsertTemplate(params: {
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_communication_template`, `${supabaseUrl}/rest/v1/rpc/upsert_communication_template`,
@@ -104,8 +104,8 @@ export async function getTemplateById(templateId: string): Promise<Template | nu
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return null; if (!adminUser) return null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_templates`, `${supabaseUrl}/rest/v1/rpc/get_communication_templates`,
+2 -2
View File
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// ── Types ──────────────────────────────────────────────────────────────────── // ── Types ────────────────────────────────────────────────────────────────────
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// ── Types ───────────────────────────────────────────────────────────────────── // ── Types ─────────────────────────────────────────────────────────────────────
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// ── Types ───────────────────────────────────────────────────────────────────── // ── Types ─────────────────────────────────────────────────────────────────────
+4 -4
View File
@@ -53,8 +53,8 @@ export async function getHarvestReachCampaigns(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_campaigns`, `${supabaseUrl}/rest/v1/rpc/get_communication_campaigns`,
@@ -82,8 +82,8 @@ export async function getCampaignAnalytics(
if (!adminUser) return []; if (!adminUser) return [];
if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) return []; if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) return [];
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_campaign_analytics`, `${supabaseUrl}/rest/v1/rpc/get_campaign_analytics`,
+2 -2
View File
@@ -17,8 +17,8 @@ export async function getProductsForSegmentPicker(
if (!adminUser) return []; if (!adminUser) return [];
if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) return []; if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) return [];
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_products_for_segment_picker`, `${supabaseUrl}/rest/v1/rpc/get_products_for_segment_picker`,
+8 -8
View File
@@ -75,8 +75,8 @@ export async function getHarvestReachSegments(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_communication_segments`, `${supabaseUrl}/rest/v1/rpc/get_communication_segments`,
@@ -110,8 +110,8 @@ export async function upsertHarvestReachSegment(params: {
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_communication_segment`, `${supabaseUrl}/rest/v1/rpc/upsert_communication_segment`,
@@ -149,8 +149,8 @@ export async function deleteHarvestReachSegment(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_communication_segment`, `${supabaseUrl}/rest/v1/rpc/delete_communication_segment`,
@@ -180,8 +180,8 @@ export async function previewSegmentWithCustomers(
return null; return null;
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/preview_campaign_audience`, `${supabaseUrl}/rest/v1/rpc/preview_campaign_audience`,
+2 -2
View File
@@ -23,8 +23,8 @@ export async function getStopsForSegmentPicker(
if (!adminUser) return []; if (!adminUser) return [];
if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) return []; if (adminUser.role === "brand_admin" && adminUser.brand_id !== brandId) return [];
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_stops_for_segment_picker`, `${supabaseUrl}/rest/v1/rpc/get_stops_for_segment_picker`,
+2 -2
View File
@@ -25,8 +25,8 @@ export async function importOrdersBatch(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const results = { imported: 0, errors: [] as { row: number; error: string }[] }; const results = { imported: 0, errors: [] as { row: number; error: string }[] };
+2 -2
View File
@@ -26,8 +26,8 @@ export async function importProductsBatch(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/bulk_upsert_products`, `${supabaseUrl}/rest/v1/rpc/bulk_upsert_products`,
+10 -10
View File
@@ -33,8 +33,8 @@ const MINIMAX_DEFAULT_BASE_URL = "https://api.minimax.io/v1";
// ── Get AI provider settings ───────────────────────────────────────────────────── // ── Get AI provider settings ─────────────────────────────────────────────────────
export async function getAIProviderSettings(brandId: string): Promise<AIProviderSettings> { export async function getAIProviderSettings(brandId: string): Promise<AIProviderSettings> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_ai_provider_settings`, `${supabaseUrl}/rest/v1/rpc/get_ai_provider_settings`,
@@ -75,8 +75,8 @@ export async function setAIProviderSettings(
const current = await getAIProviderSettings(brandId); const current = await getAIProviderSettings(brandId);
const merged = { ...current, ...settings }; const merged = { ...current, ...settings };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
const payload = { const payload = {
provider: merged.provider, provider: merged.provider,
@@ -206,8 +206,8 @@ export async function getAIClient(brandId: string): Promise<{
// ── Custom integrations ───────────────────────────────────────────────────────── // ── Custom integrations ─────────────────────────────────────────────────────────
export async function getCustomIntegrations(brandId: string): Promise<CustomIntegration[]> { export async function getCustomIntegrations(brandId: string): Promise<CustomIntegration[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_custom_integrations`, `${supabaseUrl}/rest/v1/rpc/get_custom_integrations`,
@@ -234,8 +234,8 @@ export async function upsertCustomIntegration(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (adminUser.brand_id && adminUser.brand_id !== brandId) return { success: false, error: "Not authorized" }; if (adminUser.brand_id && adminUser.brand_id !== brandId) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_custom_integration`, `${supabaseUrl}/rest/v1/rpc/upsert_custom_integration`,
@@ -263,8 +263,8 @@ export async function deleteCustomIntegration(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (adminUser.brand_id && adminUser.brand_id !== brandId) return { success: false, error: "Not authorized" }; if (adminUser.brand_id && adminUser.brand_id !== brandId) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_custom_integration`, `${supabaseUrl}/rest/v1/rpc/delete_custom_integration`,
+8 -8
View File
@@ -24,8 +24,8 @@ export type SaveCredentialsResult =
// ── Resend Credentials ───────────────────────────────────────────────────────── // ── Resend Credentials ─────────────────────────────────────────────────────────
export async function getResendCredentials(brandId: string): Promise<ResendCredentials> { export async function getResendCredentials(brandId: string): Promise<ResendCredentials> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_resend_credentials`, `${supabaseUrl}/rest/v1/rpc/get_resend_credentials`,
@@ -66,8 +66,8 @@ export async function saveResendCredentials(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Get current credentials to merge // Get current credentials to merge
const current = await getResendCredentials(brandId); const current = await getResendCredentials(brandId);
@@ -99,8 +99,8 @@ export async function saveResendCredentials(
// ── Twilio Credentials ───────────────────────────────────────────────────────── // ── Twilio Credentials ─────────────────────────────────────────────────────────
export async function getTwilioCredentials(brandId: string): Promise<TwilioCredentials> { export async function getTwilioCredentials(brandId: string): Promise<TwilioCredentials> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_twilio_credentials`, `${supabaseUrl}/rest/v1/rpc/get_twilio_credentials`,
@@ -141,8 +141,8 @@ export async function saveTwilioCredentials(
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Get current credentials to merge // Get current credentials to merge
const current = await getTwilioCredentials(brandId); const current = await getTwilioCredentials(brandId);
+4 -64
View File
@@ -2,7 +2,6 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { mockOrders, mockStops } from "@/lib/mock-data";
type AdminOrder = { type AdminOrder = {
id: string; id: string;
@@ -106,51 +105,12 @@ type AdminOrderDetail = {
}>; }>;
}; };
// Mock orders for UI review
const mockAdminOrders: AdminOrder[] = mockOrders.map((o: any) => ({
id: o.id,
customer_name: o.customer_name,
customer_email: o.customer_email,
customer_phone: o.customer_phone,
stop_id: o.stop_id,
status: o.status,
subtotal: o.subtotal,
pickup_complete: o.pickup_complete,
pickup_completed_at: o.pickup_completed_at,
pickup_completed_by: null,
created_at: o.created_at,
payment_processor: o.payment_processor,
stops: o.stops,
order_items: o.order_items,
}));
const mockAdminStops: AdminStop[] = mockStops.map((s: any) => ({
id: s.id,
city: s.city,
state: s.state,
date: s.date,
location: s.location,
brand_id: s.brand_id,
}));
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
export async function getAdminOrders(): Promise<AdminOrdersResponse> { export async function getAdminOrders(): Promise<AdminOrdersResponse> {
// Return mock data in mock mode
if (useMockData) {
return {
success: true,
orders: mockAdminOrders,
stops: mockAdminStops,
error: null,
};
}
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
const brandId = adminUser?.brand_id ?? null; const brandId = adminUser?.brand_id ?? null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_admin_orders`, `${supabaseUrl}/rest/v1/rpc/get_admin_orders`,
@@ -193,31 +153,11 @@ export async function getAdminPickedUpOrders(): Promise<AdminOrder[]> {
} }
export async function getAdminOrderDetail(orderId: string): Promise<AdminOrderDetail | null> { export async function getAdminOrderDetail(orderId: string): Promise<AdminOrderDetail | null> {
// Return mock order detail in mock mode
if (useMockData) {
const order = mockAdminOrders.find((o) => o.id === orderId);
if (!order) return null;
return {
...order,
discount_amount: null,
tax_amount: order.subtotal * 0.08,
tax_rate: 0.08,
tax_location: "Colorado",
discount_reason: null,
internal_notes: null,
payment_status: "paid",
payment_transaction_id: `txn_mock_${orderId}`,
refunded_amount: 0,
refund_reason: null,
refunds: [],
};
}
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
const brandId = adminUser?.brand_id ?? null; const brandId = adminUser?.brand_id ?? null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_admin_order_detail`, `${supabaseUrl}/rest/v1/rpc/get_admin_order_detail`,
+2 -2
View File
@@ -53,8 +53,8 @@ export async function createAdminOrder(
return { success: false, error: "At least one item is required" }; return { success: false, error: "At least one item is required" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Build items for RPC (match checkout shape) // Build items for RPC (match checkout shape)
const rpcItems = input.items.map((i) => ({ const rpcItems = input.items.map((i) => ({
+2 -2
View File
@@ -22,8 +22,8 @@ export async function createRefund(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch(`${supabaseUrl}/rest/v1/refunds`, { const res = await fetch(`${supabaseUrl}/rest/v1/refunds`, {
method: "POST", method: "POST",
+6 -6
View File
@@ -36,8 +36,8 @@ export async function updateOrder(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const patchData: Record<string, unknown> = {}; const patchData: Record<string, unknown> = {};
if (data.customer_name !== undefined) patchData.customer_name = data.customer_name; if (data.customer_name !== undefined) patchData.customer_name = data.customer_name;
@@ -85,8 +85,8 @@ export async function updateOrderItem(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const patchData: Record<string, unknown> = {}; const patchData: Record<string, unknown> = {};
if (data.quantity !== undefined) patchData.quantity = data.quantity; if (data.quantity !== undefined) patchData.quantity = data.quantity;
@@ -111,8 +111,8 @@ export async function deleteOrderItem(itemId: string): Promise<UpdateOrderResult
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch(`${supabaseUrl}/rest/v1/order_items?id=eq.${itemId}`, { const res = await fetch(`${supabaseUrl}/rest/v1/order_items?id=eq.${itemId}`, {
method: "DELETE", method: "DELETE",
+4 -4
View File
@@ -25,8 +25,8 @@ export type GetPaymentSettingsResult =
| { success: false; error: string }; | { success: false; error: string };
export async function getPaymentSettings(brandId: string): Promise<GetPaymentSettingsResult> { export async function getPaymentSettings(brandId: string): Promise<GetPaymentSettingsResult> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_payment_settings`, `${supabaseUrl}/rest/v1/rpc/get_payment_settings`,
@@ -72,8 +72,8 @@ export async function savePaymentSettings(params: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_payment_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_payment_settings`,
+10 -10
View File
@@ -28,9 +28,9 @@ export async function markPickupComplete(
// brand_admin: verify the order belongs to their brand // brand_admin: verify the order belongs to their brand
if (adminUser.role === "brand_admin" && adminUser.brand_id) { if (adminUser.role === "brand_admin" && adminUser.brand_id) {
const brandRes = await fetch( const brandRes = await fetch(
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/rest/v1/orders?id=eq.${orderId}&select=stop_id,brand_id`, `${process.env.NEXT_PUBLIC_API_URL}/rest/v1/orders?id=eq.${orderId}&select=stop_id,brand_id`,
{ {
headers: svcHeaders(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!), headers: svcHeaders(process.env.NEXT_PUBLIC_API_ANON_KEY!),
} }
); );
@@ -52,9 +52,9 @@ export async function markPickupComplete(
if (!order.brand_id && order.stop_id) { if (!order.brand_id && order.stop_id) {
const stopRes = await fetch( const stopRes = await fetch(
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/rest/v1/stops?id=eq.${order.stop_id}&select=brand_id`, `${process.env.NEXT_PUBLIC_API_URL}/rest/v1/stops?id=eq.${order.stop_id}&select=brand_id`,
{ {
headers: svcHeaders(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!), headers: svcHeaders(process.env.NEXT_PUBLIC_API_ANON_KEY!),
} }
); );
@@ -69,11 +69,11 @@ export async function markPickupComplete(
// PATCH the order // PATCH the order
const patchRes = await fetch( const patchRes = await fetch(
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/rest/v1/orders?id=eq.${orderId}`, `${process.env.NEXT_PUBLIC_API_URL}/rest/v1/orders?id=eq.${orderId}`,
{ {
method: "PATCH", method: "PATCH",
headers: { headers: {
...svcHeaders(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!), ...svcHeaders(process.env.NEXT_PUBLIC_API_ANON_KEY!),
"Content-Type": "application/json", "Content-Type": "application/json",
Prefer: "return=representation", Prefer: "return=representation",
}, },
@@ -111,9 +111,9 @@ export async function markPickupComplete(
// Emit pickup_completed event // Emit pickup_completed event
// Need brand_id — get it from the order we just patched // Need brand_id — get it from the order we just patched
const orderRes = await fetch( const orderRes = await fetch(
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/rest/v1/orders?id=eq.${orderId}&select=brand_id`, `${process.env.NEXT_PUBLIC_API_URL}/rest/v1/orders?id=eq.${orderId}&select=brand_id`,
{ {
headers: svcHeaders(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!), headers: svcHeaders(process.env.NEXT_PUBLIC_API_ANON_KEY!),
} }
); );
if (orderRes.ok) { if (orderRes.ok) {
@@ -121,11 +121,11 @@ export async function markPickupComplete(
const orderBrandId = Array.isArray(orderData) && orderData[0]?.brand_id; const orderBrandId = Array.isArray(orderData) && orderData[0]?.brand_id;
if (orderBrandId) { if (orderBrandId) {
await fetch( await fetch(
`${process.env.NEXT_PUBLIC_SUPABASE_URL}/rest/v1/rpc/record_pickup_completed_event`, `${process.env.NEXT_PUBLIC_API_URL}/rest/v1/rpc/record_pickup_completed_event`,
{ {
method: "POST", method: "POST",
headers: { headers: {
...svcHeaders(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!), ...svcHeaders(process.env.NEXT_PUBLIC_API_ANON_KEY!),
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
+2 -2
View File
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// ── Types ──────────────────────────────────────────────────────────────────── // ── Types ────────────────────────────────────────────────────────────────────
+4 -4
View File
@@ -18,8 +18,8 @@ export async function deleteProduct(
const effectiveBrandId = brandId ?? adminUser.brand_id ?? null; const effectiveBrandId = brandId ?? adminUser.brand_id ?? null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_product`, `${supabaseUrl}/rest/v1/rpc/delete_product`,
@@ -63,8 +63,8 @@ async function logAuditEvent(event: {
new_data: Record<string, unknown>; new_data: Record<string, unknown>;
brand_id: string | null; brand_id: string | null;
}) { }) {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
await fetch( await fetch(
`${supabaseUrl}/rest/v1/rpc.log_audit_event`, `${supabaseUrl}/rest/v1/rpc.log_audit_event`,
+2 -24
View File
@@ -1,11 +1,8 @@
"use server"; "use server";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { getMockTableData } from "@/lib/mock-data";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
export type CreateProductResult = export type CreateProductResult =
| { success: true; id: string } | { success: true; id: string }
| { success: false; error: string }; | { success: false; error: string };
@@ -31,27 +28,8 @@ export async function createProduct(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
if (useMockData) { const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const mockProducts = getMockTableData("products") as any[]; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const newId = `mock-prod-${Date.now()}`;
const newProduct = {
id: newId,
name: data.name,
description: data.description,
price: data.price,
type: data.type,
is_active: data.active,
image_url: data.image_url ?? null,
is_taxable: data.is_taxable,
pickup_type: data.pickup_type ?? "scheduled_stop",
brand_id: brandId,
};
mockProducts.push(newProduct);
return { success: true, id: newId };
}
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const res = await fetch(`${supabaseUrl}/rest/v1/rpc/bulk_upsert_products`, { const res = await fetch(`${supabaseUrl}/rest/v1/rpc/bulk_upsert_products`, {
method: "POST", method: "POST",
+2 -9
View File
@@ -1,11 +1,8 @@
"use server"; "use server";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { getMockTableData } from "@/lib/mock-data";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
export type UpdateProductResult = export type UpdateProductResult =
| { success: true } | { success: true }
| { success: false; error: string }; | { success: false; error: string };
@@ -32,12 +29,8 @@ export async function updateProduct(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
if (useMockData) { const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
return { success: true }; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
}
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const res = await fetch(`${supabaseUrl}/rest/v1/products?id=eq.${productId}`, { const res = await fetch(`${supabaseUrl}/rest/v1/products?id=eq.${productId}`, {
method: "PATCH", method: "PATCH",
+4 -4
View File
@@ -49,8 +49,8 @@ export async function uploadProductImage(
return { success: true, imageUrl: url }; return { success: true, imageUrl: url };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const patchRes = await fetch( const patchRes = await fetch(
`${supabaseUrl}/rest/v1/products?id=eq.${productId}`, `${supabaseUrl}/rest/v1/products?id=eq.${productId}`,
@@ -74,8 +74,8 @@ export async function deleteProductImage(
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const patchRes = await fetch( const patchRes = await fetch(
`${supabaseUrl}/rest/v1/products?id=eq.${productId}`, `${supabaseUrl}/rest/v1/products?id=eq.${productId}`,
+2 -2
View File
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
export type DateRange = { start: string; end: string }; export type DateRange = { start: string; end: string };
+1 -1
View File
@@ -3,7 +3,7 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!; const SUPABASE_URL = process.env.NEXT_PUBLIC_API_URL!;
const SUPABASE_PAT = process.env.SUPABASE_PAT!; const SUPABASE_PAT = process.env.SUPABASE_PAT!;
async function adminFetch(endpoint: string, options?: RequestInit) { async function adminFetch(endpoint: string, options?: RequestInit) {
+2 -2
View File
@@ -23,8 +23,8 @@ export async function toggleBrandFeature(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/set_brand_feature`, `${supabaseUrl}/rest/v1/rpc/set_brand_feature`,
+4 -4
View File
@@ -16,8 +16,8 @@ export async function updateShippingStatus(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_shipping_order`, `${supabaseUrl}/rest/v1/rpc/update_shipping_order`,
@@ -70,8 +70,8 @@ export async function getShippingOrders(): Promise<GetShippingOrdersResult> {
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_shipping_orders`, `${supabaseUrl}/rest/v1/rpc/get_shipping_orders`,
+6 -6
View File
@@ -58,8 +58,8 @@ async function getFedExTokenForCreate(
brandId: string | null, brandId: string | null,
adminUserId: string | null adminUserId: string | null
): Promise<{ accessToken: string } | { error: string }> { ): Promise<{ accessToken: string } | { error: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Try to get from shipping_settings // Try to get from shipping_settings
const settingsRes = await fetch( const settingsRes = await fetch(
@@ -88,8 +88,8 @@ async function getFedExTokenForCreate(
} }
async function isShipmentPerishable(orderId: string): Promise<boolean> { async function isShipmentPerishable(orderId: string): Promise<boolean> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_order_items_perishable?p_order_id=${encodeURIComponent(orderId)}`, `${supabaseUrl}/rest/v1/rpc/get_order_items_perishable?p_order_id=${encodeURIComponent(orderId)}`,
@@ -128,8 +128,8 @@ export async function createFedExShipment(
return { success: false, error: "Not authorized to create shipments" }; return { success: false, error: "Not authorized to create shipments" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Fetch order // Fetch order
const orderRes = await fetch( const orderRes = await fetch(
+4 -4
View File
@@ -95,8 +95,8 @@ async function isOrderPerishable(
orderId: string, orderId: string,
brandId: string | null brandId: string | null
): Promise<boolean> { ): Promise<boolean> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_order_items_perishable?p_order_id=${encodeURIComponent(orderId)}`, `${supabaseUrl}/rest/v1/rpc/get_order_items_perishable?p_order_id=${encodeURIComponent(orderId)}`,
@@ -151,8 +151,8 @@ export async function getFedExRates(
return { success: false, error: "Not authorized to view shipping rates" }; return { success: false, error: "Not authorized to view shipping rates" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Fetch order + brand + address // Fetch order + brand + address
const orderRes = await fetch( const orderRes = await fetch(
+4 -4
View File
@@ -77,8 +77,8 @@ async function getFedExToken(apiKey: string, apiSecret: string, useProduction: b
// ── Get Settings ───────────────────────────────────────────────────────────── // ── Get Settings ─────────────────────────────────────────────────────────────
export async function getShippingSettings(brandId: string): Promise<GetShippingSettingsResult> { export async function getShippingSettings(brandId: string): Promise<GetShippingSettingsResult> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/shipping_settings?brand_id=eq.${encodeURIComponent(brandId)}&limit=1`, `${supabaseUrl}/rest/v1/shipping_settings?brand_id=eq.${encodeURIComponent(brandId)}&limit=1`,
@@ -115,8 +115,8 @@ export async function saveShippingSettings(params: {
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Get existing settings to get ID for update // Get existing settings to get ID for update
const existing = await fetch( const existing = await fetch(
+4 -4
View File
@@ -110,8 +110,8 @@ export async function syncInventoryToSquare(
const errors: string[] = []; const errors: string[] = [];
const synced = 0; const synced = 0;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Build product name → quantity map // Build product name → quantity map
const itemQtyMap = new Map(items.map((i) => [i.productId, i.quantity])); const itemQtyMap = new Map(items.map((i) => [i.productId, i.quantity]));
@@ -177,8 +177,8 @@ export async function syncInventoryFromSquare(brandId: string): Promise<SyncResu
const errors: string[] = []; const errors: string[] = [];
const synced = 0; const synced = 0;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
try { try {
// Fetch Square inventory counts for the location // Fetch Square inventory counts for the location
+2 -2
View File
@@ -85,8 +85,8 @@ export async function syncOrdersFromSquare(brandId: string): Promise<SyncResult>
const errors: string[] = []; const errors: string[] = [];
let synced = 0; let synced = 0;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Determine sync start time — last sync or 30 days ago // Determine sync start time — last sync or 30 days ago
const since = settings.square_last_sync_at const since = settings.square_last_sync_at
+4 -4
View File
@@ -136,8 +136,8 @@ export async function syncProductsToSquare(brandId: string): Promise<SyncResult>
try { try {
// Fetch wholesale products via RPC (avoids rc_product_id bug in direct query) // Fetch wholesale products via RPC (avoids rc_product_id bug in direct query)
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const rpcRes = await fetch( const rpcRes = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_products`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_products`,
@@ -247,8 +247,8 @@ export async function syncProductsFromSquare(brandId: string): Promise<SyncResul
const errors: string[] = []; const errors: string[] = [];
let synced = 0; let synced = 0;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
let cursor: string | undefined; let cursor: string | undefined;
do { do {
+2 -2
View File
@@ -65,8 +65,8 @@ export async function getSyncLog(brandId: string): Promise<{
return { success: false, logs: [] }; return { success: false, logs: [] };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/square_sync_log?brand_id=eq.${brandId}&order=created_at.desc&limit=10`, `${supabaseUrl}/rest/v1/square_sync_log?brand_id=eq.${brandId}&order=created_at.desc&limit=10`,
+9 -9
View File
@@ -30,11 +30,11 @@ export async function createStopsBatch(
return { success: false, created: 0, error: "No brand selected" }; return { success: false, created: 0, error: "No brand selected" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
// Use anon key — admin_create_stops_batch is SECURITY DEFINER so RLS is // Use anon key — admin_create_stops_batch is SECURITY DEFINER so RLS is
// bypassed. This fixes the prior 42501 RLS violation that came from doing // bypassed. This fixes the prior 42501 RLS violation that came from doing
// direct REST inserts against the RLS-blocked `stops` table. // direct REST inserts against the RLS-blocked `stops` table.
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const rows = stops.map((s) => ({ const rows = stops.map((s) => ({
city: s.city, city: s.city,
@@ -74,8 +74,8 @@ export async function publishStop(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_stops) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_stops) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const res = await fetch(`${supabaseUrl}/rest/v1/stops?id=eq.${stopId}`, { const res = await fetch(`${supabaseUrl}/rest/v1/stops?id=eq.${stopId}`, {
method: "PATCH", method: "PATCH",
@@ -105,8 +105,8 @@ export type StopForSitemap = {
}; };
export async function getActiveStopsForSitemap(): Promise<StopForSitemap[]> { export async function getActiveStopsForSitemap(): Promise<StopForSitemap[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Get all active stops with their brand slug // Get all active stops with their brand slug
const response = await fetch( const response = await fetch(
@@ -127,7 +127,7 @@ export async function getActiveStopsForSitemap(): Promise<StopForSitemap[]> {
* Fetch active stops for a brand by slug. * Fetch active stops for a brand by slug.
* Public action used by the storefront stop pages (e.g. /tuxedo/stops, * Public action used by the storefront stop pages (e.g. /tuxedo/stops,
* /indian-river-direct/stops) — replaces the previous client-side * /indian-river-direct/stops) — replaces the previous client-side
* `supabase.from("stops")` query so supabase-js no longer ships to * `api.from("stops")` query so api-js no longer ships to
* the browser for those routes. * the browser for those routes.
* *
* Cached at the edge for 5 minutes; invalidated by `revalidateTag('stops')` * Cached at the edge for 5 minutes; invalidated by `revalidateTag('stops')`
@@ -150,8 +150,8 @@ export async function getPublicStopsForBrand(
): Promise<PublicStop[]> { ): Promise<PublicStop[]> {
if (!brandSlug) return []; if (!brandSlug) return [];
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_public_stops_for_brand`, `${supabaseUrl}/rest/v1/rpc/get_public_stops_for_brand`,
+5 -13
View File
@@ -3,9 +3,6 @@
import { revalidateTag } from "next/cache"; import { revalidateTag } from "next/cache";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { getMockTableData } from "@/lib/mock-data";
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
export type CreateStopResult = export type CreateStopResult =
| { success: true; id: string } | { success: true; id: string }
@@ -33,18 +30,13 @@ export async function createStop(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
if (useMockData) { const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const mockStops = getMockTableData("stops") as Array<{ id: string }>;
return { success: true, id: `mock-stop-${Date.now()}` };
}
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
// Preferred path: anon key + SECURITY DEFINER RPC (bypasses RLS, works even if // Preferred path: anon key + SECURITY DEFINER RPC (bypasses RLS, works even if
// service role key is absent at runtime). See: // service role key is absent at runtime). See:
// supabase/migrations/202_fix_admin_create_stop.sql // api/migrations/202_fix_admin_create_stop.sql
// scripts/apply-admin-create-stop.js (run this locally with the keys you have) // scripts/apply-admin-create-stop.js (run this locally with the keys you have)
// supabase/ADMIN_CREATE_STOP_FIX.sql (exact prompt SQL for SQL editor) // api/ADMIN_CREATE_STOP_FIX.sql (exact prompt SQL for SQL editor)
const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const anonKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const res = await fetch(`${supabaseUrl}/rest/v1/rpc/admin_create_stop`, { const res = await fetch(`${supabaseUrl}/rest/v1/rpc/admin_create_stop`, {
method: "POST", method: "POST",
@@ -114,7 +106,7 @@ export async function createStop(
" 1. On a machine that can reach your database (normal laptop usually works):\n" + " 1. On a machine that can reach your database (normal laptop usually works):\n" +
" node scripts/apply-admin-create-stop.js\n" + " node scripts/apply-admin-create-stop.js\n" +
" 2. Or: npm run migrate:one 202\n" + " 2. Or: npm run migrate:one 202\n" +
" 3. Or apply supabase/migrations/202_fix_admin_create_stop.sql via psql / Supabase CLI with --db-url.\n\n" + " 3. Or apply api/migrations/202_fix_admin_create_stop.sql via psql / Supabase CLI with --db-url.\n\n" +
"After it succeeds, restart your dev server and Add New Stop will work.", "After it succeeds, restart your dev server and Add New Stop will work.",
}; };
} }
+2 -8
View File
@@ -4,8 +4,6 @@ import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { logAuditEvent } from "@/actions/audit"; import { logAuditEvent } from "@/actions/audit";
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
export type UpdateStopResult = export type UpdateStopResult =
| { success: true } | { success: true }
| { success: false; error: string }; | { success: false; error: string };
@@ -33,12 +31,8 @@ export async function updateStop(
return { success: false, error: "Not authorized for this brand" }; return { success: false, error: "Not authorized for this brand" };
} }
if (useMockData) { const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
return { success: true }; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
}
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const slug = `${data.city.toLowerCase().replace(/\s+/g, "-")}-${data.date}`; const slug = `${data.city.toLowerCase().replace(/\s+/g, "-")}-${data.date}`;
+6 -6
View File
@@ -19,8 +19,8 @@ export async function getStripeConnectStatus(brandId: string): Promise<{
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
if (!adminUser) return { is_connected: false, error: "Not authenticated" }; if (!adminUser) return { is_connected: false, error: "Not authenticated" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Get brand's payment settings // Get brand's payment settings
const res = await fetch( const res = await fetch(
@@ -183,8 +183,8 @@ export async function saveStripeConnectAccount(brandId: string, accountId: strin
success: boolean; success: boolean;
error?: string; error?: string;
}> { }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Save to payment_settings via RPC // Save to payment_settings via RPC
const res = await fetch( const res = await fetch(
@@ -220,8 +220,8 @@ export async function disconnectStripeConnect(brandId: string): Promise<{
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/disconnect_stripe_connect`, `${supabaseUrl}/rest/v1/rpc/disconnect_stripe_connect`,
+2 -2
View File
@@ -102,8 +102,8 @@ export async function calculateOrderTax(params: {
} }
async function getBrandTaxSettings(brandId: string): Promise<BrandTaxSettings | null> { async function getBrandTaxSettings(brandId: string): Promise<BrandTaxSettings | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_brand_settings`, `${supabaseUrl}/rest/v1/rpc/get_brand_settings`,
+2 -2
View File
@@ -2,8 +2,8 @@
import { cookies } from "next/headers"; import { cookies } from "next/headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
function rpcBody(body: Record<string, unknown>) { function rpcBody(body: Record<string, unknown>) {
return JSON.stringify(body); return JSON.stringify(body);
+2 -111
View File
@@ -2,13 +2,9 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { mockWorkers, mockTasks, mockTimeEntries } from "@/lib/mock-data";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Mock mode flag - only enabled when explicitly set
const useMockData = process.env.NEXT_PUBLIC_USE_MOCK_DATA === "true";
function rpcBody(body: Record<string, unknown>) { function rpcBody(body: Record<string, unknown>) {
return JSON.stringify(body); return JSON.stringify(body);
@@ -62,20 +58,6 @@ export type TimeSummary = {
// ── Workers ─────────────────────────────────────────────────────────────────── // ── Workers ───────────────────────────────────────────────────────────────────
export async function getTimeTrackingWorkers(brandId: string): Promise<TimeWorker[]> { export async function getTimeTrackingWorkers(brandId: string): Promise<TimeWorker[]> {
if (useMockData) {
return mockWorkers.map(w => ({
id: w.id,
brand_id: brandId,
name: w.name,
role: w.role,
lang: w.language,
pin: "0000",
active: w.is_active,
last_used_at: null,
created_at: new Date().toISOString(),
worker_number: null,
}));
}
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_time_tracking_workers`, `${supabaseUrl}/rest/v1/rpc/get_time_tracking_workers`,
{ {
@@ -182,16 +164,6 @@ export async function deleteTimeWorker(workerId: string): Promise<{ success: boo
// ── Tasks ──────────────────────────────────────────────────────────────────── // ── Tasks ────────────────────────────────────────────────────────────────────
export async function getTimeTrackingTasks(brandId: string, activeOnly = false): Promise<TimeTask[]> { export async function getTimeTrackingTasks(brandId: string, activeOnly = false): Promise<TimeTask[]> {
if (useMockData) {
return mockTasks.map(t => ({
id: t.id,
name: t.name_en,
name_es: t.name_es,
unit: t.unit,
active: true,
sort_order: t.sort_order,
}));
}
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_time_tracking_tasks`, `${supabaseUrl}/rest/v1/rpc/get_time_tracking_tasks`,
{ {
@@ -282,30 +254,6 @@ export async function getWorkerTimeLogs(
offset?: number; offset?: number;
} = {} } = {}
): Promise<TimeLog[]> { ): Promise<TimeLog[]> {
if (useMockData) {
// Filter by worker, task, date range
let entries = mockTimeEntries.filter(e => e.brand_id === brandId);
if (options.workerId) entries = entries.filter(e => e.worker_id === options.workerId);
return entries.map(e => {
const worker = mockWorkers.find(w => w.id === e.worker_id);
const task = mockTasks.find(t => t.id === e.task_id);
return {
id: e.id,
worker_id: e.worker_id,
worker_name: worker?.name ?? "Unknown",
task_id: e.task_id,
task_name: task?.name_en ?? "Unknown",
clock_in: `${e.date}T09:00:00Z`,
clock_out: `${e.date}T${9 + e.hours}:00:00Z`,
lunch_break_minutes: 0,
notes: null,
submitted_via: "web",
total_minutes: Math.round(e.hours * 60),
created_at: new Date().toISOString(),
};
});
}
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_worker_time_logs`, `${supabaseUrl}/rest/v1/rpc/get_worker_time_logs`,
{ {
@@ -380,39 +328,6 @@ export async function getTimeTrackingSummary(
start: string, start: string,
end: string end: string
): Promise<TimeSummary> { ): Promise<TimeSummary> {
if (useMockData) {
const entries = mockTimeEntries.filter(e => e.brand_id === brandId);
// Calculate by worker
const workerMap = new Map<string, { id: string; name: string; total_hours: number; entry_count: number }>();
entries.forEach(e => {
const worker = mockWorkers.find(w => w.id === e.worker_id);
const existing = workerMap.get(e.worker_id) || { id: e.worker_id, name: worker?.name ?? "Unknown", total_hours: 0, entry_count: 0 };
existing.total_hours += e.hours;
existing.entry_count += 1;
workerMap.set(e.worker_id, existing);
});
// Calculate by task
const taskMap = new Map<string, { id: string; name: string; name_es: string | null; total_hours: number; entry_count: number }>();
entries.forEach(e => {
const task = mockTasks.find(t => t.id === e.task_id);
const existing = taskMap.get(e.task_id) || { id: e.task_id, name: task?.name_en ?? "Unknown", name_es: task?.name_es ?? null, total_hours: 0, entry_count: 0 };
existing.total_hours += e.hours;
existing.entry_count += 1;
taskMap.set(e.task_id, existing);
});
return {
by_worker: Array.from(workerMap.values()),
by_task: Array.from(taskMap.values()),
totals: {
entry_count: entries.length,
total_hours: entries.reduce((sum, e) => sum + e.hours, 0),
open_count: 0,
},
};
}
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_time_tracking_summary`, `${supabaseUrl}/rest/v1/rpc/get_time_tracking_summary`,
{ {
@@ -447,26 +362,6 @@ export type TimeTrackingSettings = {
}; };
export async function getTimeTrackingSettings(brandId: string): Promise<TimeTrackingSettings | null> { export async function getTimeTrackingSettings(brandId: string): Promise<TimeTrackingSettings | null> {
if (useMockData) {
return {
id: "settings-mock",
brand_id: brandId,
pay_period_start_day: 0,
pay_period_length_days: 7,
daily_overtime_threshold: 8,
weekly_overtime_threshold: 40,
overtime_multiplier: 1.5,
overtime_notifications: true,
notification_emails: ["admin@tuxedocorn.com"],
notification_sms_numbers: [],
enable_daily_alerts: true,
enable_weekly_alerts: true,
daily_alert_threshold: 8,
weekly_alert_threshold: 40,
send_end_of_period_summary: true,
brand_name: "Tuxedo Corn",
};
}
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_time_tracking_settings`, `${supabaseUrl}/rest/v1/rpc/get_time_tracking_settings`,
{ {
@@ -552,10 +447,6 @@ export async function getTimeTrackingNotificationLog(
brandId: string, brandId: string,
limit = 100 limit = 100
): Promise<NotificationLogEntry[]> { ): Promise<NotificationLogEntry[]> {
if (useMockData) {
// Return empty log in mock mode
return [];
}
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_time_tracking_notification_log`, `${supabaseUrl}/rest/v1/rpc/get_time_tracking_notification_log`,
{ {
+2 -2
View File
@@ -3,8 +3,8 @@
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
export type OvertimeCheckResult = { export type OvertimeCheckResult = {
sent: boolean; sent: boolean;
+32 -32
View File
@@ -50,8 +50,8 @@ export async function createWaterHeadgate(brandId: string, name: string, unit: s
return { success: false, error: "Not authorized" }; return { success: false, error: "Not authorized" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; // prefer service for admin muts const supabaseKey = process.env.POSTGREST_SERVICE_KEY!; // prefer service for admin muts
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/create_water_headgate`, `${supabaseUrl}/rest/v1/rpc/create_water_headgate`,
@@ -81,8 +81,8 @@ export async function updateWaterHeadgate(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_water_headgate`, `${supabaseUrl}/rest/v1/rpc/update_water_headgate`,
@@ -111,8 +111,8 @@ export async function updateWaterHeadgate(
// ── Irrigator Admin ───────────────────────────────────────── // ── Irrigator Admin ─────────────────────────────────────────
export async function getWaterIrrigators(brandId: string): Promise<Irrigator[]> { export async function getWaterIrrigators(brandId: string): Promise<Irrigator[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_users`, `${supabaseUrl}/rest/v1/rpc/get_water_users`,
@@ -142,8 +142,8 @@ export async function createWaterUser(
role: "irrigator" | "water_admin", role: "irrigator" | "water_admin",
lang: string = "en" lang: string = "en"
): Promise<{ success: boolean; user?: Irrigator; pin?: string; error?: string }> { ): Promise<{ success: boolean; user?: Irrigator; pin?: string; error?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/create_water_user`, `${supabaseUrl}/rest/v1/rpc/create_water_user`,
@@ -172,8 +172,8 @@ export async function updateWaterIrrigator(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_water_user`, `${supabaseUrl}/rest/v1/rpc/update_water_user`,
@@ -205,8 +205,8 @@ export async function resetWaterIrrigatorPin(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/reset_water_user_pin`, `${supabaseUrl}/rest/v1/rpc/reset_water_user_pin`,
@@ -232,8 +232,8 @@ export async function deleteWaterUser(userId: string): Promise<{ success: boolea
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_water_user`, `${supabaseUrl}/rest/v1/rpc/delete_water_user`,
@@ -259,8 +259,8 @@ export async function deleteWaterHeadgate(headgateId: string): Promise<{ success
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_water_headgate`, `${supabaseUrl}/rest/v1/rpc/delete_water_headgate`,
@@ -308,8 +308,8 @@ export async function deleteWaterHeadgate(headgateId: string): Promise<{ success
// ── Entries ──────────────────────────────────────────────── // ── Entries ────────────────────────────────────────────────
export async function getWaterEntries(brandId: string, limit = 50): Promise<WaterEntry[]> { export async function getWaterEntries(brandId: string, limit = 50): Promise<WaterEntry[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_entries`, `${supabaseUrl}/rest/v1/rpc/get_water_entries`,
@@ -326,8 +326,8 @@ export async function getWaterEntries(brandId: string, limit = 50): Promise<Wate
} }
export async function getWaterHeadgatesAdmin(brandId: string): Promise<Headgate[]> { export async function getWaterHeadgatesAdmin(brandId: string): Promise<Headgate[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_headgates_admin`, `${supabaseUrl}/rest/v1/rpc/get_water_headgates_admin`,
@@ -348,8 +348,8 @@ export async function regenerateHeadgateToken(headgateId: string): Promise<{ suc
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/regenerate_headgate_token`, `${supabaseUrl}/rest/v1/rpc/regenerate_headgate_token`,
@@ -379,8 +379,8 @@ export async function updateWaterEntry(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_water_entry`, `${supabaseUrl}/rest/v1/rpc/update_water_entry`,
@@ -411,8 +411,8 @@ export async function deleteWaterEntry(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_water_entry`, `${supabaseUrl}/rest/v1/rpc/delete_water_entry`,
@@ -434,8 +434,8 @@ export async function deleteWaterEntry(
} }
export async function getWaterEntryById(entryId: string): Promise<WaterEntry | null> { export async function getWaterEntryById(entryId: string): Promise<WaterEntry | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_entry_by_id`, `${supabaseUrl}/rest/v1/rpc/get_water_entry_by_id`,
@@ -481,8 +481,8 @@ export type WaterDisplaySummary = {
}; };
export async function getWaterDisplaySummary(brandId: string): Promise<WaterDisplaySummary | null> { export async function getWaterDisplaySummary(brandId: string): Promise<WaterDisplaySummary | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_display_summary`, `${supabaseUrl}/rest/v1/rpc/get_water_display_summary`,
@@ -511,8 +511,8 @@ export type AlertLogEntry = {
}; };
export async function getWaterAlertLog(brandId: string, limit = 50): Promise<AlertLogEntry[]> { export async function getWaterAlertLog(brandId: string, limit = 50): Promise<AlertLogEntry[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_alert_log`, `${supabaseUrl}/rest/v1/rpc/get_water_alert_log`,
+8 -8
View File
@@ -35,8 +35,8 @@ type HeadgatesResult = {
}; };
export async function getWaterHeadgates(brandId: string, activeOnly = false): Promise<Headgate[]> { export async function getWaterHeadgates(brandId: string, activeOnly = false): Promise<Headgate[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_headgates`, `${supabaseUrl}/rest/v1/rpc/get_water_headgates`,
@@ -53,8 +53,8 @@ export async function getWaterHeadgates(brandId: string, activeOnly = false): Pr
} }
export async function verifyWaterPin(brandId: string, pin: string): Promise<VerifyPinResult> { export async function verifyWaterPin(brandId: string, pin: string): Promise<VerifyPinResult> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/verify_water_pin`, `${supabaseUrl}/rest/v1/rpc/verify_water_pin`,
@@ -134,8 +134,8 @@ export async function submitWaterEntry(
return { success: false, error: "Not logged in" }; return { success: false, error: "Not logged in" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/submit_water_entry`, `${supabaseUrl}/rest/v1/rpc/submit_water_entry`,
@@ -236,8 +236,8 @@ export async function getWaterAdminSession(): Promise<{ user_id: string; name: s
if (!sessionId) return null; if (!sessionId) return null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Use get_water_admin_session RPC (SECURITY DEFINER) to avoid direct water_sessions + water_users JOIN // Use get_water_admin_session RPC (SECURITY DEFINER) to avoid direct water_sessions + water_users JOIN
const response = await fetch( const response = await fetch(
+6 -6
View File
@@ -14,8 +14,8 @@ export type WaterAdminSettings = {
}; };
export async function getWaterAdminSettings(brandId: string): Promise<WaterAdminSettings | null> { export async function getWaterAdminSettings(brandId: string): Promise<WaterAdminSettings | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_admin_settings`, `${supabaseUrl}/rest/v1/rpc/get_water_admin_settings`,
@@ -39,8 +39,8 @@ export async function saveWaterAdminSettings(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_water_log) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
let pinHash: string | null = null; let pinHash: string | null = null;
if (settings.pin) { if (settings.pin) {
@@ -88,8 +88,8 @@ export async function verifyWaterAdminPin(
brandId: string, brandId: string,
pin: string pin: string
): Promise<{ success: boolean; session_id?: string; error?: string }> { ): Promise<{ success: boolean; session_id?: string; error?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const settingsRes = await fetch( const settingsRes = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_water_admin_settings`, `${supabaseUrl}/rest/v1/rpc/get_water_admin_settings`,
+24 -24
View File
@@ -10,8 +10,8 @@ export async function registerWholesaleCustomer(params: {
email: string; email: string;
phone?: string; phone?: string;
}): Promise<{ success: boolean; error?: string; requiresApproval?: boolean }> { }): Promise<{ success: boolean; error?: string; requiresApproval?: boolean }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/register_wholesale_customer`, `${supabaseUrl}/rest/v1/rpc/register_wholesale_customer`,
@@ -54,8 +54,8 @@ export async function getPendingWholesaleRegistrations(brandId: string) {
if (!adminUser) return []; if (!adminUser) return [];
if (!adminUser.can_manage_orders) return []; if (!adminUser.can_manage_orders) return [];
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_pending_wholesale_registrations`, `${supabaseUrl}/rest/v1/rpc/get_pending_wholesale_registrations`,
@@ -85,8 +85,8 @@ export async function approveWholesaleRegistration(
return { success: false, error: "Not authorized to operate on this brand" }; return { success: false, error: "Not authorized to operate on this brand" };
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/approve_wholesale_registration`, `${supabaseUrl}/rest/v1/rpc/approve_wholesale_registration`,
@@ -128,8 +128,8 @@ export async function getWholesaleCustomerByUser(
role: string; role: string;
brand_id: string; brand_id: string;
} | null> { } | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_customer_by_user`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_customer_by_user`,
@@ -162,8 +162,8 @@ export async function getWholesaleCustomer(
role: string; role: string;
brand_id: string; brand_id: string;
} | null> { } | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/wholesale_customers?id=eq.${customerId}&select=id,user_id,company_name,contact_name,email,phone,account_status,role,brand_id`, `${supabaseUrl}/rest/v1/wholesale_customers?id=eq.${customerId}&select=id,user_id,company_name,contact_name,email,phone,account_status,role,brand_id`,
@@ -179,8 +179,8 @@ export async function getWholesaleCustomer(
export async function getWholesaleProducts(brandId: string) { export async function getWholesaleProducts(brandId: string) {
// Uses SECURITY DEFINER RPC — no auth required for admins, anon key works for customers too // Uses SECURITY DEFINER RPC — no auth required for admins, anon key works for customers too
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_products`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_products`,
@@ -217,8 +217,8 @@ export async function submitWholesaleOrder(params: {
orderTotal?: number; orderTotal?: number;
idempotent?: boolean; idempotent?: boolean;
}> { }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Generate UUID at the start of checkout — before RPC call for true idempotency // Generate UUID at the start of checkout — before RPC call for true idempotency
const checkoutSessionId = params.checkoutSessionId ?? crypto.randomUUID(); const checkoutSessionId = params.checkoutSessionId ?? crypto.randomUUID();
@@ -284,8 +284,8 @@ export async function submitWholesaleOrder(params: {
export async function enqueueWholesaleWebhookForOrderCreated(orderId: string, brandId: string, customerId: string, invoiceNumber: string | null, subtotal: number) { export async function enqueueWholesaleWebhookForOrderCreated(orderId: string, brandId: string, customerId: string, invoiceNumber: string | null, subtotal: number) {
try { try {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? process.env.SUPABASE_ANON_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY ?? process.env.SUPABASE_ANON_KEY!;
await fetch(`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, { await fetch(`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, {
method: "POST", method: "POST",
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" }, headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
@@ -341,8 +341,8 @@ export type WholesalePricingOverride = {
}; };
export async function getWholesaleCustomerPricing(customerId: string): Promise<WholesalePricingOverride[]> { export async function getWholesaleCustomerPricing(customerId: string): Promise<WholesalePricingOverride[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_customer_pricing`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_customer_pricing`,
@@ -365,8 +365,8 @@ export async function upsertWholesaleCustomerPricing(params: {
productId: string; productId: string;
customUnitPrice: number; customUnitPrice: number;
}): Promise<{ success: boolean; error?: string }> { }): Promise<{ success: boolean; error?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_wholesale_customer_pricing`, `${supabaseUrl}/rest/v1/rpc/upsert_wholesale_customer_pricing`,
@@ -392,8 +392,8 @@ export async function deleteWholesaleCustomerPricing(params: {
customerId: string; customerId: string;
productId: string; productId: string;
}): Promise<{ success: boolean; error?: string }> { }): Promise<{ success: boolean; error?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_wholesale_customer_pricing`, `${supabaseUrl}/rest/v1/rpc/delete_wholesale_customer_pricing`,
@@ -415,8 +415,8 @@ export async function deleteWholesaleCustomerPricing(params: {
} }
export async function getWholesaleCustomerOrders(customerId: string): Promise<WholesaleCustomerOrder[]> { export async function getWholesaleCustomerOrders(customerId: string): Promise<WholesaleCustomerOrder[]> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_customer_orders`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_customer_orders`,
+52 -52
View File
@@ -187,8 +187,8 @@ export async function getWholesaleOrders(brandId?: string): Promise<WholesaleOrd
if (!adminUser) return []; if (!adminUser) return [];
const bid = resolveBrandId(adminUser, brandId); const bid = resolveBrandId(adminUser, brandId);
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_orders`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_orders`,
@@ -211,8 +211,8 @@ export async function getWholesalePickupOrders(brandId?: string): Promise<Wholes
if (!adminUser) return []; if (!adminUser) return [];
const bid = resolveBrandId(adminUser, brandId); const bid = resolveBrandId(adminUser, brandId);
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_pickup_orders`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_pickup_orders`,
@@ -259,8 +259,8 @@ export async function markWholesaleOrderFulfilled(orderId: string, brandId?: str
const { brandId: resolved, error } = enforceBrandScope(adminUser, brandId); const { brandId: resolved, error } = enforceBrandScope(adminUser, brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/mark_wholesale_order_fulfilled`, `${supabaseUrl}/rest/v1/rpc/mark_wholesale_order_fulfilled`,
@@ -282,8 +282,8 @@ export async function markWholesaleOrderFulfilled(orderId: string, brandId?: str
} }
async function enqueueWholesaleWebhookForOrderFulfilled(orderId: string, brandId?: string) { async function enqueueWholesaleWebhookForOrderFulfilled(orderId: string, brandId?: string) {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? process.env.SUPABASE_ANON_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY ?? process.env.SUPABASE_ANON_KEY!;
await fetch(`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, { await fetch(`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, {
method: "POST", method: "POST",
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" }, headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
@@ -308,8 +308,8 @@ export async function updateWholesaleOrderStatus(
const { error } = enforceBrandScope(adminUser, brandId); const { error } = enforceBrandScope(adminUser, brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/update_wholesale_order_status`, `${supabaseUrl}/rest/v1/rpc/update_wholesale_order_status`,
@@ -335,8 +335,8 @@ export async function deleteWholesaleOrder(orderId: string, brandId?: string): P
const { error } = enforceBrandScope(adminUser, brandId); const { error } = enforceBrandScope(adminUser, brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_wholesale_order`, `${supabaseUrl}/rest/v1/rpc/delete_wholesale_order`,
@@ -362,8 +362,8 @@ export async function deleteWholesaleCustomer(customerId: string, brandId?: stri
const { error } = enforceBrandScope(adminUser, brandId); const { error } = enforceBrandScope(adminUser, brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_wholesale_customer`, `${supabaseUrl}/rest/v1/rpc/delete_wholesale_customer`,
@@ -391,8 +391,8 @@ export async function deleteWholesaleProduct(productId: string, brandId?: string
const { error } = enforceBrandScope(adminUser, brandId); const { error } = enforceBrandScope(adminUser, brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/delete_wholesale_product`, `${supabaseUrl}/rest/v1/rpc/delete_wholesale_product`,
@@ -419,8 +419,8 @@ export async function getWholesaleCustomers(brandId?: string): Promise<Wholesale
if (!adminUser) return []; if (!adminUser) return [];
const bid = resolveBrandId(adminUser, brandId); const bid = resolveBrandId(adminUser, brandId);
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_customers`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_customers`,
@@ -461,8 +461,8 @@ export async function saveWholesaleCustomer(params: {
const { error } = enforceBrandScope(adminUser, params.brandId); const { error } = enforceBrandScope(adminUser, params.brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_wholesale_customer`, `${supabaseUrl}/rest/v1/rpc/upsert_wholesale_customer`,
@@ -503,8 +503,8 @@ export async function getWholesaleProducts(brandId?: string): Promise<WholesaleP
if (!adminUser) return []; if (!adminUser) return [];
const bid = resolveBrandId(adminUser, brandId); const bid = resolveBrandId(adminUser, brandId);
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_products`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_products`,
@@ -548,8 +548,8 @@ export async function saveWholesaleProduct(params: {
const { error } = enforceBrandScope(adminUser, params.brandId); const { error } = enforceBrandScope(adminUser, params.brandId);
if (error) return { success: false, error }; if (error) return { success: false, error };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_wholesale_product`, `${supabaseUrl}/rest/v1/rpc/upsert_wholesale_product`,
@@ -593,8 +593,8 @@ export async function getWholesaleSettings(brandId?: string): Promise<WholesaleS
if (!adminUser) return null; if (!adminUser) return null;
const bid = brandId ?? adminUser.brand_id ?? null; const bid = brandId ?? adminUser.brand_id ?? null;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_settings`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_settings`,
@@ -635,8 +635,8 @@ export async function saveWholesaleSettings(params: {
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/upsert_wholesale_settings`, `${supabaseUrl}/rest/v1/rpc/upsert_wholesale_settings`,
@@ -683,8 +683,8 @@ export async function recordWholesaleDeposit(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/record_wholesale_deposit`, `${supabaseUrl}/rest/v1/rpc/record_wholesale_deposit`,
@@ -716,8 +716,8 @@ export async function recordWholesaleDeposit(
} }
async function enqueueWholesaleWebhookForDepositRecorded(orderId: string, amount: number, brandId?: string) { async function enqueueWholesaleWebhookForDepositRecorded(orderId: string, amount: number, brandId?: string) {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? process.env.SUPABASE_ANON_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY ?? process.env.SUPABASE_ANON_KEY!;
await fetch(`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, { await fetch(`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, {
method: "POST", method: "POST",
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" }, headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
@@ -739,8 +739,8 @@ export async function bulkFulfillWholesaleOrders(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/bulk_fulfill_wholesale_orders`, `${supabaseUrl}/rest/v1/rpc/bulk_fulfill_wholesale_orders`,
@@ -774,8 +774,8 @@ export async function bulkRecordWholesaleDeposit(
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/bulk_record_wholesale_deposit`, `${supabaseUrl}/rest/v1/rpc/bulk_record_wholesale_deposit`,
@@ -824,8 +824,8 @@ export type WholesaleNotification = {
export async function getWholesaleNotificationStats( export async function getWholesaleNotificationStats(
brandId: string brandId: string
): Promise<{ pending: number; sent: number; failed: number; total: number }> { ): Promise<{ pending: number; sent: number; failed: number; total: number }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_notification_stats`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_notification_stats`,
@@ -851,8 +851,8 @@ export async function getWholesalePendingNotifications(
if (!adminUser) return []; if (!adminUser) return [];
if (!adminUser.can_manage_orders) return []; if (!adminUser.can_manage_orders) return [];
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/get_wholesale_pending_notifications`, `${supabaseUrl}/rest/v1/rpc/get_wholesale_pending_notifications`,
@@ -878,8 +878,8 @@ export async function markWholesaleNotificationSent(
if (!adminUser) return { success: false }; if (!adminUser) return { success: false };
if (!adminUser.can_manage_orders) return { success: false }; if (!adminUser.can_manage_orders) return { success: false };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/mark_wholesale_notification_sent`, `${supabaseUrl}/rest/v1/rpc/mark_wholesale_notification_sent`,
@@ -907,8 +907,8 @@ export async function enqueueWholesaleNotification(params: {
bodyHtml?: string; bodyHtml?: string;
bodyText?: string; bodyText?: string;
}): Promise<{ success: boolean; error?: string }> { }): Promise<{ success: boolean; error?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_notification`, `${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_notification`,
@@ -949,8 +949,8 @@ export type WebhookSettings = {
}; };
export async function getWebhookSettings(brandId: string): Promise<WebhookSettings | null> { export async function getWebhookSettings(brandId: string): Promise<WebhookSettings | null> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/wholesale_webhook_settings?brand_id=eq.${brandId}&select=*`, `${supabaseUrl}/rest/v1/wholesale_webhook_settings?brand_id=eq.${brandId}&select=*`,
@@ -974,8 +974,8 @@ export async function saveWebhookSettings(params: {
if (!adminUser) return { success: false, error: "Not authenticated" }; if (!adminUser) return { success: false, error: "Not authenticated" };
if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" }; if (!adminUser.can_manage_orders) return { success: false, error: "Not authorized" };
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const serviceKey = process.env.POSTGREST_SERVICE_KEY!;
const body: Record<string, unknown> = { const body: Record<string, unknown> = {
brand_id: params.brandId, brand_id: params.brandId,
@@ -1007,8 +1007,8 @@ export async function enqueueWholesaleWebhook(
payload: Record<string, unknown> | null = null, payload: Record<string, unknown> | null = null,
brandId?: string brandId?: string
): Promise<{ success: boolean; logId?: string }> { ): Promise<{ success: boolean; logId?: string }> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY ?? process.env.NEXT_PUBLIC_API_ANON_KEY!;
const response = await fetch( const response = await fetch(
`${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`, `${supabaseUrl}/rest/v1/rpc/enqueue_wholesale_webhook`,
@@ -1041,8 +1041,8 @@ export async function getRecentWebhookActivity(brandId: string, limit = 10): Pro
created_at: string; created_at: string;
response: string | null; response: string | null;
}>> { }>> {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY ?? process.env.NEXT_PUBLIC_API_ANON_KEY!;
const res = await fetch( const res = await fetch(
`${supabaseUrl}/rest/v1/wholesale_sync_log?brand_id=eq.${brandId}&order=created_at.desc&limit=${limit}`, `${supabaseUrl}/rest/v1/wholesale_sync_log?brand_id=eq.${brandId}&order=created_at.desc&limit=${limit}`,
+2 -2
View File
@@ -11,8 +11,8 @@ export default async function DebugAuthPage() {
let adminUsersResult: string | null = null; let adminUsersResult: string | null = null;
if (rcAuthUid) { if (rcAuthUid) {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY; const serviceKey = process.env.POSTGREST_SERVICE_KEY;
if (supabaseUrl && serviceKey) { if (supabaseUrl && serviceKey) {
try { try {
const res = await fetch( const res = await fetch(
+2 -2
View File
@@ -4,7 +4,7 @@ import { getAdminOrders } from "@/actions/orders";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
import { PageHeader } from "@/components/admin/design-system"; import { PageHeader } from "@/components/admin/design-system";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
export const dynamic = "force-dynamic"; export const dynamic = "force-dynamic";
@@ -33,7 +33,7 @@ export default async function AdminOrdersPage() {
// Fetch active products for this brand (for admin "New Order" item picker) // Fetch active products for this brand (for admin "New Order" item picker)
let brandProducts: Array<{ id: string; name: string; price: number; type?: string | null; active?: boolean }> = []; let brandProducts: Array<{ id: string; name: string; price: number; type?: string | null; active?: boolean }> = [];
try { try {
let prodQuery = supabase let prodQuery = api
.from("products") .from("products")
.select("id, name, price, type, active") .select("id, name, price, type, active")
.eq("active", true) .eq("active", true)
+2 -2
View File
@@ -1,5 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { isFeatureEnabled } from "@/lib/feature-flags"; import { isFeatureEnabled } from "@/lib/feature-flags";
import { getBillingOverview } from "@/actions/billing/billing-overview"; import { getBillingOverview } from "@/actions/billing/billing-overview";
@@ -30,7 +30,7 @@ export default async function AdminPage() {
// (0/0/0) usage values and contradicts the billing page's "Products 1/25". // (0/0/0) usage values and contradicts the billing page's "Products 1/25".
let dashboardBrandId: string | null = adminUser?.brand_id ?? null; let dashboardBrandId: string | null = adminUser?.brand_id ?? null;
if (!dashboardBrandId && adminUser?.role === "platform_admin") { if (!dashboardBrandId && adminUser?.role === "platform_admin") {
const { data: firstBrand } = await supabase const { data: firstBrand } = await api
.from("brands") .from("brands")
.select("id") .select("id")
.limit(1) .limit(1)
+5 -5
View File
@@ -1,4 +1,4 @@
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import ProductEditForm from "@/components/admin/ProductEditForm"; import ProductEditForm from "@/components/admin/ProductEditForm";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
@@ -14,10 +14,10 @@ type ProductDetailPageProps = {
export default async function ProductDetailPage({ params }: ProductDetailPageProps) { export default async function ProductDetailPage({ params }: ProductDetailPageProps) {
const { id } = await params; const { id } = await params;
const [{ data: product, error }, { data: brands }] = await Promise.all([ const [{ data: product, error }, { data: brands }] = (await Promise.all([
supabase.from("products").select("*, brands(name, slug)").eq("id", id).single(), api.from("products").select("*, brands(name, slug)").eq("id", id).single(),
supabase.from("brands").select("id, name, slug"), api.from("brands").select("id, name, slug"),
]); ])) as any;
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
+3 -3
View File
@@ -1,4 +1,4 @@
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
import ProductsClient from "@/components/admin/ProductsClient"; import ProductsClient from "@/components/admin/ProductsClient";
@@ -31,7 +31,7 @@ export default async function AdminProductsPage() {
const brandId = adminUser.brand_id; const brandId = adminUser.brand_id;
let query = supabase let query = api
.from("products") .from("products")
.select(` .select(`
id, id,
@@ -52,7 +52,7 @@ export default async function AdminProductsPage() {
query = query.eq("brand_id", adminUser.brand_id); query = query.eq("brand_id", adminUser.brand_id);
} }
const { data: products, error } = await query; const { data: products, error } = (await query) as any;
if (error) { if (error) {
return ( return (
+2 -2
View File
@@ -1,4 +1,4 @@
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
import ReportsDashboard from "@/components/admin/ReportsDashboard"; import ReportsDashboard from "@/components/admin/ReportsDashboard";
@@ -12,7 +12,7 @@ export default async function ReportsPage() {
const isPlatformAdmin = adminUser.role === "platform_admin"; const isPlatformAdmin = adminUser.role === "platform_admin";
const { data: brands } = await supabase const { data: brands } = await api
.from("brands") .from("brands")
.select("id, name") .select("id, name")
.order("name"); .order("name");
+2 -2
View File
@@ -1,4 +1,4 @@
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { getBillingOverview } from "@/actions/billing/billing-overview"; import { getBillingOverview } from "@/actions/billing/billing-overview";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
@@ -18,7 +18,7 @@ export default async function BillingPage({ params }: Props) {
let resolvedBrandId = effectiveBrandId; let resolvedBrandId = effectiveBrandId;
if (isPlatformAdmin && !resolvedBrandId) { if (isPlatformAdmin && !resolvedBrandId) {
const { data: firstBrand } = await supabase const { data: firstBrand } = await api
.from("brands") .from("brands")
.select("id") .select("id")
.limit(1) .limit(1)
+2 -2
View File
@@ -1,5 +1,5 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import IntegrationsClientPage from "./IntegrationsClientPage"; import IntegrationsClientPage from "./IntegrationsClientPage";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
@@ -18,7 +18,7 @@ export default async function IntegrationsPage() {
// Platform admins: fetch all brands for the picker // Platform admins: fetch all brands for the picker
const brands = isPlatformAdmin const brands = isPlatformAdmin
? (await supabase.from("brands").select("id, name").order("name")).data ?? [] ? (await api.from("brands").select("id, name").order("name")).data ?? []
: []; : [];
return ( return (
+2 -2
View File
@@ -1,5 +1,5 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { getShippingSettings } from "@/actions/shipping/settings"; import { getShippingSettings } from "@/actions/shipping/settings";
import ShippingSettingsForm from "@/components/admin/ShippingSettingsForm"; import ShippingSettingsForm from "@/components/admin/ShippingSettingsForm";
@@ -17,7 +17,7 @@ export default async function ShippingSettingsPage() {
// Platform admins: fetch all brands for the picker // Platform admins: fetch all brands for the picker
const brands = isPlatformAdmin const brands = isPlatformAdmin
? (await supabase.from("brands").select("id, name").order("name")).data ?? [] ? (await api.from("brands").select("id, name").order("name")).data ?? []
: []; : [];
const effectiveBrandId = brandId || (brands[0]?.id ?? ""); const effectiveBrandId = brandId || (brands[0]?.id ?? "");
+9 -9
View File
@@ -1,4 +1,4 @@
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import StopEditForm from "@/components/admin/StopEditForm"; import StopEditForm from "@/components/admin/StopEditForm";
import StopProductAssignment from "@/components/admin/StopProductAssignment"; import StopProductAssignment from "@/components/admin/StopProductAssignment";
import MessageCustomersSection from "@/components/admin/MessageCustomersSection"; import MessageCustomersSection from "@/components/admin/MessageCustomersSection";
@@ -16,14 +16,14 @@ type StopDetailPageProps = {
export default async function StopDetailPage({ params }: StopDetailPageProps) { export default async function StopDetailPage({ params }: StopDetailPageProps) {
const { id } = await params; const { id } = await params;
const [{ data: stop, error }, { data: brands }] = await Promise.all([ const [{ data: stop, error }, { data: brands }] = (await Promise.all([
supabase api
.from("stops") .from("stops")
.select("*, brands(name, slug)") .select("*, brands(name, slug)")
.eq("id", id) .eq("id", id)
.single(), .single(),
supabase.from("brands").select("id, name, slug"), api.from("brands").select("id, name, slug"),
]); ])) as any;
const adminUser = await getAdminUser(); const adminUser = await getAdminUser();
@@ -56,17 +56,17 @@ export default async function StopDetailPage({ params }: StopDetailPageProps) {
); );
} }
const [{ data: allProducts }, { data: productStops }] = await Promise.all([ const [{ data: allProducts }, { data: productStops }] = (await Promise.all([
supabase api
.from("products") .from("products")
.select("id, name, type, price") .select("id, name, type, price")
.eq("brand_id", stop.brand_id) .eq("brand_id", stop.brand_id)
.eq("active", true), .eq("active", true),
supabase api
.from("product_stops") .from("product_stops")
.select("id, product_id, products(id, name, type, price)") .select("id, product_id, products(id, name, type, price)")
.eq("stop_id", id), .eq("stop_id", id),
]); ])) as any;
const assignedProducts = (productStops ?? []) const assignedProducts = (productStops ?? [])
.map((ps: any) => ps) .map((ps: any) => ps)
+4 -4
View File
@@ -1,4 +1,4 @@
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import NewStopForm from "@/components/admin/NewStopForm"; import NewStopForm from "@/components/admin/NewStopForm";
import StopProductAssignment from "@/components/admin/StopProductAssignment"; import StopProductAssignment from "@/components/admin/StopProductAssignment";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
@@ -32,11 +32,11 @@ export default async function NewStopPage({
let duplicateFrom: Stop | null = null; let duplicateFrom: Stop | null = null;
if (duplicate) { if (duplicate) {
const { data } = await supabase const { data } = (await api
.from("stops") .from("stops")
.select("city, state, location, date, time, brand_id, active, address, zip, cutoff_time") .select("city, state, location, date, time, brand_id, active, address, zip, cutoff_time")
.eq("id", duplicate) .eq("id", duplicate)
.single(); .single()) as { data: Stop | null };
duplicateFrom = data; duplicateFrom = data;
} }
@@ -44,7 +44,7 @@ export default async function NewStopPage({
adminUser.brand_id ?? "64294306-5f42-463d-a5e8-2ad6c81a96de"; adminUser.brand_id ?? "64294306-5f42-463d-a5e8-2ad6c81a96de";
const [{ data: allProducts }] = await Promise.all([ const [{ data: allProducts }] = await Promise.all([
supabase api
.from("products") .from("products")
.select("id, name, type, price") .select("id, name, type, price")
.eq("brand_id", brandId) .eq("brand_id", brandId)
+3 -3
View File
@@ -1,6 +1,6 @@
import StopTableClient from "@/components/admin/StopTableClient"; import StopTableClient from "@/components/admin/StopTableClient";
import StopsHeaderActions from "@/components/admin/StopsHeaderActions"; import StopsHeaderActions from "@/components/admin/StopsHeaderActions";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
import { PageHeader } from "@/components/admin/design-system"; import { PageHeader } from "@/components/admin/design-system";
@@ -22,7 +22,7 @@ export default async function AdminStopsPage() {
redirect("/admin/pickup"); redirect("/admin/pickup");
} }
let query = supabase let query = api
.from("stops") .from("stops")
.select(` .select(`
id, id,
@@ -49,7 +49,7 @@ export default async function AdminStopsPage() {
query = query.eq("brand_id", adminUser.brand_id); query = query.eq("brand_id", adminUser.brand_id);
} }
const { data: stops, error } = await query; const { data: stops, error } = (await query) as any;
if (error) { if (error) {
return ( return (
+3 -3
View File
@@ -1,7 +1,7 @@
"use server"; "use server";
import { getAdminUser } from "@/lib/admin-permissions"; import { getAdminUser } from "@/lib/admin-permissions";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
import AdminAccessDenied from "@/components/admin/AdminAccessDenied"; import AdminAccessDenied from "@/components/admin/AdminAccessDenied";
import TaxDashboard from "@/components/admin/TaxDashboard"; import TaxDashboard from "@/components/admin/TaxDashboard";
import { PageHeader } from "@/components/admin/design-system"; import { PageHeader } from "@/components/admin/design-system";
@@ -20,7 +20,7 @@ export default async function TaxesPage({ params }: Props) {
let resolvedBrandId = effectiveBrandId; let resolvedBrandId = effectiveBrandId;
if (isPlatformAdmin && !resolvedBrandId) { if (isPlatformAdmin && !resolvedBrandId) {
const { data: firstBrand } = await supabase const { data: firstBrand } = await api
.from("brands") .from("brands")
.select("id") .select("id")
.limit(1) .limit(1)
@@ -54,7 +54,7 @@ export default async function TaxesPage({ params }: Props) {
if (!resolvedBrandId) return <AdminAccessDenied />; if (!resolvedBrandId) return <AdminAccessDenied />;
const { data: brands } = await supabase const { data: brands } = await api
.from("brands") .from("brands")
.select("id, name") .select("id, name")
.order("name"); .order("name");
+2 -2
View File
@@ -82,8 +82,8 @@ Use "recent_orders" for recent order questions.`;
const queryType = parsed.queryType ?? "recent_orders"; const queryType = parsed.queryType ?? "recent_orders";
const days = parsed.days ?? 30; const days = parsed.days ?? 30;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY; const supabaseKey = process.env.POSTGREST_SERVICE_KEY;
// Route to appropriate RPC based on query type // Route to appropriate RPC based on query type
let rpcName = "get_recent_orders_insights"; let rpcName = "get_recent_orders_insights";
+2 -2
View File
@@ -2,8 +2,8 @@ import { NextResponse } from "next/server";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
export async function GET() { export async function GET() {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY; const supabaseKey = process.env.POSTGREST_SERVICE_KEY;
if (!supabaseUrl || !supabaseKey) { if (!supabaseUrl || !supabaseKey) {
return NextResponse.json({ error: "Missing configuration" }, { status: 500 }); return NextResponse.json({ error: "Missing configuration" }, { status: 500 });
-45
View File
@@ -1,45 +0,0 @@
import { NextResponse } from "next/server";
export async function GET() {
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY ?? "";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL ?? "";
if (!serviceKey || !supabaseUrl) {
return NextResponse.json({ error: "Missing env vars", serviceKey: !!serviceKey, supabaseUrl: !!supabaseUrl }, { status: 500 });
}
// Test 1: just a simple health endpoint that doesn't require the key
let healthResult = null;
try {
const res = await fetch(`${supabaseUrl}/rest/v1/`, {
headers: { apikey: serviceKey },
});
healthResult = { status: res.status, ok: res.ok };
} catch (e: any) {
healthResult = { error: e?.message };
}
// Test 2: try admin_users with POST (bypasses RLS select policies)
let adminResult = null;
try {
const res = await fetch(
`${supabaseUrl}/rest/v1/admin_users?select=id,user_id,role,email,display_name&limit=5`,
{
method: "GET",
headers: {
apikey: serviceKey,
"Content-Type": "application/json",
Prefer: "return=representation",
},
}
);
const body = await res.text();
let parsed = null;
try { parsed = JSON.parse(body); } catch { parsed = body; }
adminResult = { status: res.status, body: parsed };
} catch (e: any) {
adminResult = { error: e?.message };
}
return NextResponse.json({ healthResult, adminResult, serviceKeyLen: serviceKey.length });
}
-48
View File
@@ -1,48 +0,0 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
export async function GET() {
const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
const rcAuthUid = cookieStore.get("rc_auth_uid")?.value;
const rcUid = cookieStore.get("rc_uid")?.value;
const rcAccessToken = cookieStore.get("rc_access_token")?.value;
let adminUsersStatus = "not_tried";
let adminUsersResult: string | null = null;
if (rcAuthUid) {
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (supabaseUrl && serviceKey) {
try {
const res = await fetch(
`${supabaseUrl}/rest/v1/admin_users?user_id=eq.${rcAuthUid}&limit=1`,
{ headers: { apikey: serviceKey, "Content-Type": "application/json" } }
);
adminUsersStatus = String(res.status);
const data = await res.json().catch(() => null);
adminUsersResult = JSON.stringify(data);
} catch (e) {
adminUsersStatus = "error: " + (e instanceof Error ? e.message : String(e));
}
} else {
adminUsersStatus = "missing_env_vars";
}
} else {
adminUsersStatus = "no_rc_auth_uid_cookie";
}
return NextResponse.json({
cookies: {
rc_auth_uid: rcAuthUid ? `${rcAuthUid.slice(0, 10)}...` : null,
rc_uid: rcUid ? `${rcUid.slice(0, 10)}...` : null,
rc_access_token: rcAccessToken ? "present" : null,
all_cookie_names: allCookies.map(c => c.name),
},
admin_users: {
status: adminUsersStatus,
result: adminUsersResult,
},
});
}
-18
View File
@@ -1,18 +0,0 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
export const dynamic = "force-dynamic";
export async function GET() {
const cookieStore = await cookies();
const allCookies = cookieStore.getAll();
const rcAuthUid = cookieStore.get("rc_auth_uid")?.value;
const rcUid = cookieStore.get("rc_uid")?.value;
return NextResponse.json({
received_cookies: allCookies.map(c => ({ name: c.name, value_len: c.value.length })),
rc_auth_uid: rcAuthUid ? `${rcAuthUid.slice(0, 8)}...` : "MISSING",
rc_uid: rcUid ? `${rcUid.slice(0, 8)}...` : "MISSING",
raw_rc_auth_uid: rcAuthUid ?? null,
});
}
-31
View File
@@ -1,31 +0,0 @@
import { NextResponse } from "next/server";
export async function GET() {
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
const key = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
const nodeEnv = process.env.NODE_ENV;
const result = {
NODE_ENV: nodeEnv,
NEXT_PUBLIC_SUPABASE_URL: url ? `${url.substring(0, 40)}... (SET)` : "MISSING",
NEXT_PUBLIC_SUPABASE_ANON_KEY: key ? `${key.substring(0, 20)}... (SET)` : "MISSING",
SUPABASE_SERVICE_ROLE_KEY: serviceKey ? `${serviceKey.substring(0, 20)}... (SET)` : "MISSING",
supabaseClientCanCreate: false as boolean,
error: null as string | null,
};
if (url && key) {
try {
const { createClient } = await import("@supabase/supabase-js");
const client = createClient(url, key);
result.supabaseClientCanCreate = true;
} catch (e: any) {
result.error = e?.message ?? String(e);
}
} else {
result.error = "Missing env vars";
}
return NextResponse.json(result, { status: 200 });
}
@@ -1,34 +0,0 @@
import { NextResponse } from "next/server";
import { getAdminUser } from "@/lib/admin-permissions";
export async function GET() {
// Test the REST call directly from this endpoint
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY!;
const uid = "c023efb3-e3ef-4156-bed6-d17b92ea8aca";
let restResult = null;
try {
const res = await fetch(
`${supabaseUrl}/rest/v1/admin_users?user_id=eq.${uid}&limit=1`,
{ headers: { apikey: serviceKey, "Content-Type": "application/json" } }
);
const data = await res.json().catch(() => null);
restResult = { status: res.status, data, keyLen: serviceKey.length };
} catch (e: any) {
restResult = { error: e?.message };
}
const adminUser = await getAdminUser();
return NextResponse.json({
adminUser: adminUser ? {
id: adminUser.id,
user_id: adminUser.user_id,
role: adminUser.role,
brand_id: adminUser.brand_id,
active: adminUser.active,
} : null,
restResult,
supabaseUrl: supabaseUrl ? "SET" : "MISSING",
});
}
-9
View File
@@ -1,9 +0,0 @@
import { NextResponse } from "next/server";
export async function GET() {
return NextResponse.json({
ts: new Date().toISOString(),
deployment: "debug-hello",
msg: "hello from latest build"
});
}
-80
View File
@@ -1,80 +0,0 @@
import { NextResponse } from "next/server";
import { cookies } from "next/headers";
export const dynamic = "force-dynamic";
export async function GET() {
const cookieStore = await cookies();
const uid = cookieStore.get("rc_auth_uid")?.value ?? cookieStore.get("rc_uid")?.value;
if (!uid) {
return NextResponse.json({ error: "No uid cookie found" }, { status: 400 });
}
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const serviceKey = process.env.SUPABASE_SERVICE_ROLE_KEY;
if (!supabaseUrl || !serviceKey) {
return NextResponse.json({
uid,
error: "Missing env vars",
supabaseUrl: supabaseUrl ?? "MISSING",
serviceKeyPresent: !!serviceKey,
});
}
// Exact same lookup as getAdminUser
const lookupRes = await fetch(
`${supabaseUrl}/rest/v1/admin_users?user_id=eq.${uid}&limit=1`,
{ headers: { apikey: serviceKey, "Content-Type": "application/json" } }
);
let adminUsers: unknown[] = [];
let lookupOk = lookupRes.ok;
let lookupStatus = lookupRes.status;
let lookupData: unknown = null;
if (lookupRes.ok) {
lookupData = await lookupRes.json().catch(() => []);
adminUsers = Array.isArray(lookupData) ? lookupData : [];
} else {
lookupData = await lookupRes.text().catch(() => "unknown error");
}
if (adminUsers.length > 0) {
return NextResponse.json({
uid,
result: "found",
adminUser: adminUsers[0],
});
}
// Try auto-create
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!UUID_REGEX.test(uid)) {
return NextResponse.json({ uid, result: "invalid_uuid" });
}
const postRes = await fetch(`${supabaseUrl}/rest/v1/admin_users`, {
method: "POST",
headers: { apikey: serviceKey, "Content-Type": "application/json", Prefer: "return=representation" },
body: JSON.stringify({
user_id: uid, role: "platform_admin", brand_id: null, active: true,
can_manage_products: true, can_manage_stops: true, can_manage_orders: true,
can_manage_pickup: true, can_manage_messages: true, can_manage_refunds: true,
can_manage_users: true, can_manage_water_log: true, can_manage_reports: true,
can_manage_settings: true, must_change_password: false
}),
});
return NextResponse.json({
uid,
result: "auto_created",
lookupOk,
lookupStatus,
adminUsersFound: adminUsers.length,
postStatus: postRes.status,
postOk: postRes.ok,
postData: postRes.ok ? await postRes.json().catch(() => null) : await postRes.text().catch(() => null),
});
}
@@ -2,8 +2,8 @@ import { NextRequest, NextResponse } from "next/server";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { sendAbandonedCartEmail, type AbandonedCart } from "@/actions/email-automation/abandoned-cart"; import { sendAbandonedCartEmail, type AbandonedCart } from "@/actions/email-automation/abandoned-cart";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const BRAND_IDS = [ const BRAND_IDS = [
{ id: "64294306-5f42-463d-a5e8-2ad6c81a96de", name: "Tuxedo Corn" }, { id: "64294306-5f42-463d-a5e8-2ad6c81a96de", name: "Tuxedo Corn" },
@@ -2,8 +2,8 @@ import { NextRequest, NextResponse } from "next/server";
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
import { sendWelcomeEmail, type WelcomeSequenceEntry } from "@/actions/email-automation/welcome-sequence"; import { sendWelcomeEmail, type WelcomeSequenceEntry } from "@/actions/email-automation/welcome-sequence";
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
const BRAND_IDS = [ const BRAND_IDS = [
{ id: "64294306-5f42-463d-a5e8-2ad6c81a96de", name: "Tuxedo Corn" }, { id: "64294306-5f42-463d-a5e8-2ad6c81a96de", name: "Tuxedo Corn" },
+2 -2
View File
@@ -9,8 +9,8 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "Email is required." }, { status: 400 }); return NextResponse.json({ error: "Email is required." }, { status: 400 });
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseAnonKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
if (!supabaseUrl || !supabaseAnonKey) { if (!supabaseUrl || !supabaseAnonKey) {
return NextResponse.json({ error: "Server misconfiguration." }, { status: 500 }); return NextResponse.json({ error: "Server misconfiguration." }, { status: 500 });
} }
@@ -1,11 +1,11 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { PDFDocument, rgb, StandardFonts } from "pdf-lib"; import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
import { supabase } from "@/lib/supabase"; import { api } from "@/lib/api";
export async function GET() { export async function GET() {
const brandSlug = "indian-river-direct"; const brandSlug = "indian-river-direct";
const { data: brand } = await supabase const { data: brand } = await api
.from("brands") .from("brands")
.select("id, name") .select("id, name")
.eq("slug", brandSlug) .eq("slug", brandSlug)
@@ -15,14 +15,14 @@ export async function GET() {
return new NextResponse("Brand not found", { status: 404 }); return new NextResponse("Brand not found", { status: 404 });
} }
const { data: stops } = await supabase const { data: stops } = (await api
.from("stops") .from("stops")
.select("*") .select("*")
.eq("brand_id", brand.id) .eq("brand_id", brand.id)
.eq("active", true) .eq("active", true)
.order("date", { ascending: true }); .order("date", { ascending: true })) as { data: Array<{ city: string; state: string; date: string; time: string; location: string }> | null };
const { data: settings } = await supabase const { data: settings } = await api
.from("brand_settings") .from("brand_settings")
.select("logo_url, email, phone, schedule_pdf_notes") .select("logo_url, email, phone, schedule_pdf_notes")
.eq("brand_id", brand.id) .eq("brand_id", brand.id)
+2 -2
View File
@@ -8,8 +8,8 @@ export async function POST(request: Request) {
return NextResponse.json({ error: "Email and password are required." }, { status: 400 }); return NextResponse.json({ error: "Email and password are required." }, { status: 400 });
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; const supabaseAnonKey = process.env.NEXT_PUBLIC_API_ANON_KEY;
if (!supabaseUrl || !supabaseAnonKey) { if (!supabaseUrl || !supabaseAnonKey) {
return NextResponse.json({ error: "Server misconfiguration." }, { status: 500 }); return NextResponse.json({ error: "Server misconfiguration." }, { status: 500 });
} }
+2 -2
View File
@@ -19,8 +19,8 @@ export async function GET(req: NextRequest) {
return NextResponse.json({ error: "Forbidden" }, { status: 403 }); return NextResponse.json({ error: "Forbidden" }, { status: 403 });
} }
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!; const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
// Fetch orders report data // Fetch orders report data
const response = await fetch( const response = await fetch(
+2 -2
View File
@@ -49,8 +49,8 @@ export async function POST(req: NextRequest) {
} }
const { type, data } = event; const { type, data } = event;
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!; const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.SUPABASE_SERVICE_ROLE_KEY!; const supabaseKey = process.env.POSTGREST_SERVICE_KEY!;
// Look up by customer_email + subject (event_id is not populated by send_campaign) // Look up by customer_email + subject (event_id is not populated by send_campaign)
// Filter to recent logs (last 7 days) to avoid stale matches // Filter to recent logs (last 7 days) to avoid stale matches
@@ -1,6 +1,6 @@
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!; const SUPABASE_URL = process.env.NEXT_PUBLIC_API_URL!;
const SUPABASE_PAT = process.env.SUPABASE_PAT!; const SUPABASE_PAT = process.env.SUPABASE_PAT!;
interface LotRow { interface LotRow {
+1 -1
View File
@@ -1,6 +1,6 @@
import { svcHeaders } from "@/lib/svc-headers"; import { svcHeaders } from "@/lib/svc-headers";
const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL!; const SUPABASE_URL = process.env.NEXT_PUBLIC_API_URL!;
const SUPABASE_PAT = process.env.SUPABASE_PAT!; const SUPABASE_PAT = process.env.SUPABASE_PAT!;
type LotRow = { type LotRow = {

Some files were not shown because too many files have changed in this diff Show More