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
+8 -8
View File
@@ -64,7 +64,7 @@ export type ConversionFunnel = {
// when the relevant data isn't present.
//
// `recent_orders` / `conversion_funnel` use the new `orders` schema directly
// (total_cents, placed_at, fulfillment, status, customer_id, tenant_id).
// (total_cents, placed_at, fulfillment, status, customer_id, brand_id).
/**
* Aggregate KPIs over a date window. Replaces the
@@ -84,7 +84,7 @@ async function getReportsSummary(
const params: unknown[] = [startDate, endDate];
let brandFilter = "";
if (brandId) {
brandFilter = `AND tenant_id = $3::uuid`;
brandFilter = `AND brand_id = $3::uuid`;
params.push(brandId);
}
const { rows } = await pool.query<{
@@ -137,7 +137,7 @@ export async function getAnalyticsMetrics(periodDays: number = 30): Promise<Anal
const customerParams: unknown[] = [];
let customerFilter = "";
if (brandId) {
customerFilter = "WHERE tenant_id = $1::uuid";
customerFilter = "WHERE brand_id = $1::uuid";
customerParams.push(brandId);
}
const customerCountRes = await pool.query<{ count: string }>(
@@ -184,7 +184,7 @@ export async function getRevenueChart(periodDays: number = 30): Promise<RevenueD
const params: unknown[] = [startDate.toISOString().split("T")[0], endDate.toISOString().split("T")[0]];
let brandFilter = "";
if (brandId) {
brandFilter = "AND tenant_id = $3::uuid";
brandFilter = "AND brand_id = $3::uuid";
params.push(brandId);
}
const { rows } = await pool.query<{ date: string; revenue: number; orders: number }>(
@@ -219,7 +219,7 @@ export async function getTopProducts(limit: number = 5): Promise<ProductPerforma
const params: unknown[] = [startDate, endDate];
let brandFilter = "";
if (brandId) {
brandFilter = "AND o.tenant_id = $3::uuid";
brandFilter = "AND o.brand_id = $3::uuid";
params.push(brandId);
}
@@ -270,7 +270,7 @@ export async function getRecentOrders(limit: number = 10): Promise<RecentOrder[]
const params: unknown[] = [];
let brandFilter = "";
if (brandId) {
brandFilter = "WHERE tenant_id = $1::uuid";
brandFilter = "WHERE brand_id = $1::uuid";
params.push(brandId);
}
const cappedLimit = Math.max(1, Math.min(limit, 100));
@@ -322,7 +322,7 @@ export async function getCustomerGrowth(): Promise<CustomerGrowth> {
const params: unknown[] = [startDate, endDate];
let brandFilter = "";
if (brandId) {
brandFilter = "AND tenant_id = $3::uuid";
brandFilter = "AND brand_id = $3::uuid";
params.push(brandId);
}
@@ -365,7 +365,7 @@ export async function getConversionFunnel(): Promise<ConversionFunnel[]> {
const params: unknown[] = [];
let brandFilter = "";
if (brandId) {
brandFilter = "WHERE tenant_id = $1::uuid";
brandFilter = "WHERE brand_id = $1::uuid";
params.push(brandId);
}
const { rows } = await pool.query<{ status: string }>(