feat(storage): MinIO object storage, Neon Auth, Supabase removal
Deploy to route.crispygoat.com / deploy (push) Failing after 3m1s

- Add MinIO/S3-compatible storage client (src/lib/storage.ts) with uploadObject,
  deleteObject, presigned URL helpers, and BUCKETS constant
- Wire product images, brand logos, and water log photos to MinIO via the
  new storage client
- Migrate forgot-password to Neon Auth (remove Supabase /auth/v1/recover call)
- Migrate send-scheduled cron to direct Postgres + Resend (remove Supabase Edge
  Function proxy)
- Add logoUrl to email types (OrderReceipt, Welcome, PasswordReset) and pass
  brand_settings.logo_url from all call sites
- Update email templates to use dynamic logoUrl instead of hardcoded Supabase
  bucket URLs
- Remove hardcoded Supabase URLs from TuxedoVideoHero, TuxedoAboutPage,
  TimeTrackingFieldClient; use brand_settings props + local public/ fallback
- Download brand logos (3) and tuxedo-hero.mp4 (36MB) from Supabase bucket to
  public/ for local development
- Add MinIO env vars to .env.example (endpoint, access key, secret, buckets)
- Fix TimeTrackingFieldClient to destructure logoUrl and brandAccent props
- Fix admin/users.ts logoUrl type (null → undefined for optional string)
- Remove stale sb- cookie from wholesale-auth
- Migrate tuxedo/about page to remove supabase import and use pool query for
  wholesale_settings lookup
This commit is contained in:
openclaw
2026-06-09 11:32:17 -06:00
parent bb464bda65
commit 916ad39176
349 changed files with 6706 additions and 3343 deletions
+1 -1
View File
@@ -29,7 +29,7 @@ export default async function BillingPage({ params }: Props) {
.limit(1)
.single();
if (firstBrand?.id) {
resolvedBrandId = firstBrand.id;
resolvedBrandId = String(firstBrand.id);
} else {
return (
<main className="min-h-screen bg-[var(--admin-bg)] px-6 py-12">
+1 -1
View File
@@ -18,7 +18,7 @@ export default async function IntegrationsPage() {
// Platform admins: fetch all brands for the picker
const brands = isPlatformAdmin
? (await supabase.from("brands").select("id, name").order("name")).data ?? []
? ((await supabase.from("brands").select("id, name").order("name")) as unknown as { data: { id: string; name: string }[] }).data ?? []
: [];
return (
+1 -1
View File
@@ -17,7 +17,7 @@ export default async function ShippingSettingsPage() {
// Platform admins: fetch all brands for the picker
const brands = isPlatformAdmin
? (await supabase.from("brands").select("id, name").order("name")).data ?? []
? ((await supabase.from("brands").select("id, name").order("name")) as unknown as { data: { id: string; name: string }[] }).data ?? []
: [];
const effectiveBrandId = brandId || (brands[0]?.id ?? "");
@@ -65,7 +65,7 @@ export default function SquareSyncSettingsClient({ settings, logs, brandId }: Pr
setError(null);
const result = await savePaymentSettings({
brandId,
provider: (settings?.provider as any) || null,
provider: (settings?.provider ?? null) as "stripe" | "square" | "manual" | null,
squareSyncEnabled,
squareInventoryMode,
});
+2 -2
View File
@@ -1,6 +1,6 @@
import { redirect } from "next/navigation";
import { getAdminUser } from "@/lib/admin-permissions";
import { getPaymentSettings } from "@/actions/payments";
import { getPaymentSettings, type PaymentSettings } from "@/actions/payments";
import { getSyncLog, type SyncLogEntry } from "@/actions/square-sync-ui";
import SquareSyncSettingsClient from "./SquareSyncSettingsClient";
@@ -26,7 +26,7 @@ export default async function SquareSyncSettingsPage() {
return (
<SquareSyncSettingsClient
settings={settings as any}
settings={settings as PaymentSettings}
logs={logs}
brandId={brandId}
/>