pwf-website-new/functions/api/bookings.ts

147 lines
4.2 KiB
TypeScript

import { z } from 'zod';
const bookingSchema = z.object({
classId: z.number(),
date: z.string(),
paid: z.boolean().default(false),
status: z.string().default("pending")
});
// JWT verification helper
async function verifyJWT(token: string, secret: string): Promise<any> {
const [header, payload, signature] = token.split('.');
const data = `${header}.${payload}`;
const signatureBuffer = new Uint8Array(
atob(signature).split('').map(c => c.charCodeAt(0))
);
const key = await crypto.subtle.importKey(
'raw',
new TextEncoder().encode(secret),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['verify']
);
const isValid = await crypto.subtle.verify('HMAC', key, signatureBuffer, new TextEncoder().encode(data));
if (!isValid) {
throw new Error('Invalid token');
}
return JSON.parse(atob(payload));
}
// Booking interface
interface Booking {
id: string;
userId: string;
classId: number;
date: string;
paid: boolean;
status: string;
createdAt: string;
}
export const onRequest: PagesFunction = async (context) => {
const { request, env } = context;
const JWT_SECRET = env.JWT_SECRET || 'pilateswithfadia-secret-key';
// Helper function to get user from token
async function getCurrentUser(): Promise<any> {
const authHeader = request.headers.get('Authorization');
if (!authHeader || !authHeader.startsWith('Bearer ')) {
throw new Error('No valid token provided');
}
const token = authHeader.substring(7);
return await verifyJWT(token, JWT_SECRET);
}
// Helper function to get user bookings
async function getUserBookings(userId: string): Promise<Booking[]> {
const bookingsData = await env.STORAGE.get(`bookings:${userId}`);
return bookingsData ? JSON.parse(bookingsData) : [];
}
// Helper function to save user bookings
async function saveUserBookings(userId: string, bookings: Booking[]): Promise<void> {
await env.STORAGE.put(`bookings:${userId}`, JSON.stringify(bookings));
}
// GET /api/bookings - Get user's bookings
if (request.method === 'GET') {
try {
const user = await getCurrentUser();
const bookings = await getUserBookings(user.userId);
return new Response(JSON.stringify(bookings), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response(JSON.stringify({ message: "Unauthorized" }), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
}
// POST /api/bookings - Create new booking
if (request.method === 'POST') {
try {
const user = await getCurrentUser();
const body = await request.json();
const bookingData = bookingSchema.parse(body);
const bookings = await getUserBookings(user.userId);
const newBooking: Booking = {
id: `booking_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
userId: user.userId,
classId: bookingData.classId,
date: bookingData.date,
paid: bookingData.paid,
status: bookingData.status,
createdAt: new Date().toISOString()
};
bookings.push(newBooking);
await saveUserBookings(user.userId, bookings);
return new Response(JSON.stringify(newBooking), {
status: 201,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
if (error instanceof z.ZodError) {
return new Response(JSON.stringify({
message: "Invalid booking data",
errors: error.errors
}), {
status: 400,
headers: { 'Content-Type': 'application/json' }
});
}
if (error instanceof Error && error.message === 'No valid token provided') {
return new Response(JSON.stringify({ message: "Unauthorized" }), {
status: 401,
headers: { 'Content-Type': 'application/json' }
});
}
return new Response(JSON.stringify({ message: "Failed to create booking" }), {
status: 500,
headers: { 'Content-Type': 'application/json' }
});
}
}
return new Response(JSON.stringify({ message: "Method not allowed" }), {
status: 405,
headers: { 'Content-Type': 'application/json' }
});
};