Files
route-commerce/src/lib/data-service.ts
T
openclaw 916ad39176
Deploy to route.crispygoat.com / deploy (push) Failing after 3m1s
feat(storage): MinIO object storage, Neon Auth, Supabase removal
- 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
2026-06-09 12:23:37 -06:00

146 lines
4.0 KiB
TypeScript

// Data service for direct PostgreSQL queries via pg Pool
// This replaces the Supabase client for production use
import { pool } from "./db";
import type { QueryResultRow } from "pg";
// Re-export pool for convenience
export { pool };
// Query helpers for common operations
export async function query<T extends QueryResultRow = Record<string, unknown>>(
sql: string,
params: unknown[] = []
): Promise<T[]> {
const result = await pool.query<T>(sql, params);
return result.rows;
}
export async function queryOne<T extends QueryResultRow = Record<string, unknown>>(
sql: string,
params: unknown[] = []
): Promise<T | null> {
const result = await pool.query<T>(sql, params);
return result.rows[0] ?? null;
}
// Brand operations
export async function getBrands() {
return query<{ id: string; name: string; slug: string; accent_color: string | null; active: boolean }>(
"SELECT id, name, slug, accent_color, active FROM brands ORDER BY name"
);
}
export async function getBrandById(brandId: string) {
return queryOne<{ id: string; name: string; slug: string }>(
"SELECT id, name, slug FROM brands WHERE id = $1",
[brandId]
);
}
// Product operations
export async function getProducts(brandId?: string | null) {
if (brandId) {
return query<Record<string, unknown>>(
"SELECT * FROM products WHERE brand_id = $1 AND active = true AND deleted_at IS NULL ORDER BY name",
[brandId]
);
}
return query<Record<string, unknown>>(
"SELECT * FROM products WHERE active = true AND deleted_at IS NULL ORDER BY name"
);
}
export async function getProductById(productId: string) {
return queryOne<Record<string, unknown>>(
"SELECT * FROM products WHERE id = $1 AND deleted_at IS NULL",
[productId]
);
}
// Stop operations
export async function getStops(brandId?: string | null) {
if (brandId) {
return query<Record<string, unknown>>(
"SELECT * FROM stops WHERE brand_id = $1 AND active = true ORDER BY date DESC",
[brandId]
);
}
return query<Record<string, unknown>>(
"SELECT * FROM stops WHERE active = true ORDER BY date DESC"
);
}
export async function getStopById(stopId: string) {
return queryOne<Record<string, unknown>>(
"SELECT * FROM stops WHERE id = $1",
[stopId]
);
}
// Order operations
export async function getOrders(brandId?: string | null) {
if (brandId) {
return query<Record<string, unknown>>(
`SELECT o.*, s.city, s.state, s.date, s.location, s.slug
FROM orders o
LEFT JOIN stops s ON o.stop_id = s.id
WHERE o.brand_id = $1
ORDER BY o.created_at DESC`,
[brandId]
);
}
return query<Record<string, unknown>>(
`SELECT o.*, s.city, s.state, s.date, s.location, s.slug
FROM orders o
LEFT JOIN stops s ON o.stop_id = s.id
ORDER BY o.created_at DESC`
);
}
// Worker operations (for time tracking)
export async function getWorkers(brandId?: string | null) {
if (brandId) {
return query<Record<string, unknown>>(
"SELECT * FROM workers WHERE brand_id = $1 AND is_active = true ORDER BY name",
[brandId]
);
}
return query<Record<string, unknown>>(
"SELECT * FROM workers WHERE is_active = true ORDER BY name"
);
}
// Task operations (for time tracking)
export async function getTasks(brandId?: string | null) {
if (brandId) {
return query<Record<string, unknown>>(
"SELECT * FROM tasks WHERE brand_id = $1 ORDER BY sort_order",
[brandId]
);
}
return query<Record<string, unknown>>(
"SELECT * FROM tasks ORDER BY sort_order"
);
}
// Customer operations
export async function getCustomers(brandId?: string | null) {
if (brandId) {
return query<Record<string, unknown>>(
"SELECT * FROM customers WHERE brand_id = $1 ORDER BY name",
[brandId]
);
}
return query<Record<string, unknown>>(
"SELECT * FROM customers ORDER BY name"
);
}
// Brand settings
export async function getBrandSettings(brandId: string) {
return queryOne<Record<string, unknown>>(
"SELECT * FROM brand_settings WHERE brand_id = $1",
[brandId]
);
}