Replace Firebase Storage with MinIO and add user account features

- Storage Integration:
  * Remove Firebase Storage dependency and useFirebaseStorage composable
  * Implement direct MinIO uploads via POST /storage/upload with multipart/form-data
  * Upload canvas JSON, preview PNG, and production PNG as separate objects
  * Store public URLs and metadata in design records

- Authentication & Registration:
  * Add email/password registration page with validation
  * Integrate backend user session via /auth/login endpoint
  * Store backendUser.id as ownerId in design records
  * Auto-sync backend session on Firebase auth state changes

- User Account Pages:
  * Create profile page showing user details and backend session info
  * Create orders page with transaction history filtered by customerEmail
  * Add server proxy /api/orders to forward GET /transactions queries

- Navigation Improvements:
  * Replace inline auth buttons with avatar dropdown menu
  * Add Profile, Orders, and Logout options to dropdown
  * Implement outside-click and route-change handlers for dropdown
  * Display user initials in avatar badge

- API Updates:
  * Update transactions endpoint to accept amount as string
  * Format amount with .toFixed(2) in checkout success flow
  * Query orders by customerEmail instead of ownerId for consistency
This commit is contained in:
Frank John Begornia
2025-11-16 01:19:35 +08:00
parent 0ff41822af
commit bf701f8342
19 changed files with 1807 additions and 223 deletions

View File

@@ -1,39 +1,51 @@
import Stripe from 'stripe'
import Stripe from "stripe";
export default defineEventHandler(async (event) => {
const config = useRuntimeConfig()
const stripeSecretKey = config.stripeSecretKey
const config = useRuntimeConfig();
const stripeSecretKey = config.stripeSecretKey;
if (!stripeSecretKey) {
throw createError({ statusCode: 500, statusMessage: 'Stripe secret key not configured' })
throw createError({
statusCode: 500,
statusMessage: "Stripe secret key not configured",
});
}
const stripe = new Stripe(stripeSecretKey, {
apiVersion: '2024-10-28.acacia',
})
apiVersion: "2025-10-29.clover",
});
const body = await readBody<{
designId: string
templateId?: string
designName?: string
amount: number
currency?: string
successUrl: string
cancelUrl: string
customerEmail?: string
}>(event)
designId: string;
templateId?: string;
designName?: string;
amount: number;
currency?: string;
successUrl: string;
cancelUrl: string;
customerEmail?: string;
}>(event);
if (!body?.designId || !body?.amount || !body?.successUrl || !body?.cancelUrl) {
throw createError({ statusCode: 400, statusMessage: 'Missing required fields' })
if (
!body?.designId ||
!body?.amount ||
!body?.successUrl ||
!body?.cancelUrl
) {
throw createError({
statusCode: 400,
statusMessage: "Missing required fields",
});
}
const { currency = 'usd' } = body
const { currency = "usd" } = body;
const session = await stripe.checkout.sessions.create({
mode: 'payment',
payment_method_types: ['card'],
billing_address_collection: 'auto',
mode: "payment",
payment_method_types: ["card"],
billing_address_collection: "auto",
customer_email: body.customerEmail,
shipping_address_collection: { allowed_countries: ["US", "CA"] },
line_items: [
{
quantity: 1,
@@ -56,7 +68,7 @@ export default defineEventHandler(async (event) => {
},
success_url: body.successUrl,
cancel_url: body.cancelUrl,
})
});
return { id: session.id, url: session.url }
})
return { id: session.id, url: session.url };
});