c86e97e816
Deploy to route.crispygoat.com / deploy (push) Failing after 7s
- 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
106 lines
3.3 KiB
TypeScript
106 lines
3.3 KiB
TypeScript
/**
|
|
* Shipping + Payments. Source: `db/migrations/0001_init.sql`.
|
|
*/
|
|
import {
|
|
pgTable,
|
|
uuid,
|
|
text,
|
|
numeric,
|
|
boolean,
|
|
date,
|
|
timestamp,
|
|
index,
|
|
} from "drizzle-orm/pg-core";
|
|
import { brands } from "./brands";
|
|
import { orders } from "./orders";
|
|
|
|
export const shippingSettings = pgTable(
|
|
"shipping_settings",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
brandId: uuid("brand_id")
|
|
.notNull()
|
|
.unique()
|
|
.references(() => brands.id, { onDelete: "cascade" }),
|
|
carrier: text("carrier").notNull().default("fedex"),
|
|
fedexAccountNumber: text("fedex_account_number"),
|
|
fedexApiKey: text("fedex_api_key"),
|
|
fedexApiSecret: text("fedex_api_secret"),
|
|
fedexUseProduction: boolean("fedex_use_production")
|
|
.notNull()
|
|
.default(false),
|
|
defaultServiceType: text("default_service_type")
|
|
.notNull()
|
|
.default("FEDEX_GROUND"),
|
|
refrigeratedHandlingNotes: text("refrigerated_handling_notes"),
|
|
fragileHandlingNotes: text("fragile_handling_notes"),
|
|
createdAt: timestamp("created_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow(),
|
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow(),
|
|
},
|
|
);
|
|
|
|
export const shipments = pgTable(
|
|
"shipments",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
orderId: uuid("order_id")
|
|
.notNull()
|
|
.references(() => orders.id, { onDelete: "cascade" }),
|
|
carrier: text("carrier").notNull().default("fedex"),
|
|
serviceType: text("service_type").notNull(),
|
|
trackingNumber: text("tracking_number"),
|
|
labelUrl: text("label_url"),
|
|
rateCharged: numeric("rate_charged", { precision: 10, scale: 2 }),
|
|
estimatedDeliveryDate: date("estimated_delivery_date"),
|
|
isRefrigerated: boolean("is_refrigerated").notNull().default(false),
|
|
isFragile: boolean("is_fragile").notNull().default(false),
|
|
handlingNotes: text("handling_notes"),
|
|
status: text("status", {
|
|
enum: [
|
|
"created", "label_printed", "picked_up",
|
|
"in_transit", "delivered", "exception",
|
|
],
|
|
}).notNull().default("created"),
|
|
fedexShipmentId: text("fedex_shipment_id"),
|
|
createdAt: timestamp("created_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow(),
|
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow(),
|
|
createdBy: uuid("created_by"),
|
|
},
|
|
(t) => ({
|
|
orderIdx: index("shipments_order_idx").on(t.orderId),
|
|
}),
|
|
);
|
|
|
|
export const paymentSettings = pgTable(
|
|
"payment_settings",
|
|
{
|
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
brandId: uuid("brand_id")
|
|
.notNull()
|
|
.unique()
|
|
.references(() => brands.id, { onDelete: "cascade" }),
|
|
provider: text("provider"),
|
|
stripePublishableKey: text("stripe_publishable_key"),
|
|
stripeSecretKey: text("stripe_secret_key"),
|
|
squareAccessToken: text("square_access_token"),
|
|
squareLocationId: text("square_location_id"),
|
|
createdAt: timestamp("created_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow(),
|
|
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
.notNull()
|
|
.defaultNow(),
|
|
},
|
|
);
|
|
|
|
export type ShippingSettings = typeof shippingSettings.$inferSelect;
|
|
export type Shipment = typeof shipments.$inferSelect;
|
|
export type PaymentSettings = typeof paymentSettings.$inferSelect; |