Merge GitHub main into Gitea main
Deploy to route.crispygoat.com / deploy (push) Failing after 7s
Build / build (push) Has been cancelled

Brings in:
- .gitea/workflows/build.yml (build + typecheck + lint on self-hosted runner)
- scripts/e2e-test.sh (local Postgres + PostgREST + MinIO + Next.js validation)
- Codex review round 2 fixes (buyer/billing/comms/a11y)
- 207 multi-brand admin migration
- Locations table + UI
- Various product/stops/auth refinements

Resolved 7 conflicts by taking Gitea's version to preserve production
behavior:
- package.json (deps)
- src/actions/products/upload-image.ts (storage)
- src/app/admin/taxes/page.tsx
- src/app/checkout/CheckoutClient.tsx
- src/components/admin/AdminSidebar.tsx
- src/components/admin/StopProductAssignment.tsx
- src/lib/admin-permissions.ts

Our new dev_session auth + MinIO storage changes are deferred to a
focused follow-up to avoid breaking the production deploy.
This commit is contained in:
2026-06-05 23:09:51 +00:00
79 changed files with 12589 additions and 857 deletions
+8 -3
View File
@@ -1,6 +1,7 @@
"use server";
import { getAdminUser } from "@/lib/admin-permissions";
import { getActiveBrandId } from "@/lib/brand-scope";
import { svcHeaders } from "@/lib/svc-headers";
export type CampaignType = "marketing" | "operational" | "transactional";
@@ -57,7 +58,11 @@ export async function getCommunicationCampaigns(brandId?: string): Promise<ListC
const adminUser = await getAdminUser();
if (!adminUser) return { success: false, error: "Not authenticated" };
const effectiveBrandId = brandId ?? adminUser.brand_id ?? null;
const activeBrandId = await getActiveBrandId(adminUser, brandId);
if (!activeBrandId && adminUser.role !== "platform_admin") {
return { success: false, error: "Brand access required" };
}
const effectiveBrandId = activeBrandId;
const supabaseUrl = process.env.NEXT_PUBLIC_API_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_API_ANON_KEY!;
@@ -147,7 +152,7 @@ export async function deleteCampaign(campaignId: string, brandId?: string): Prom
{
method: "POST",
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
body: JSON.stringify({ p_campaign_id: campaignId, p_brand_id: adminUser.brand_id ?? null }),
body: JSON.stringify({ p_campaign_id: campaignId, p_brand_id: (await getActiveBrandId(adminUser)) ?? null }),
}
);
@@ -167,7 +172,7 @@ export async function getCampaignById(campaignId: string, brandId?: string): Pro
{
method: "POST",
headers: { ...svcHeaders(supabaseKey), "Content-Type": "application/json" },
body: JSON.stringify({ p_campaign_id: campaignId, p_brand_id: brandId ?? adminUser.brand_id ?? null }),
body: JSON.stringify({ p_campaign_id: campaignId, p_brand_id: (await getActiveBrandId(adminUser, brandId)) ?? null }),
}
);