auto-commit for 01035626-fc86-4553-b85b-3396ef438dce

This commit is contained in:
emergent-agent-e1
2026-05-06 04:10:43 +00:00
parent 0f89cba316
commit cbc00fa39c
103 changed files with 9779 additions and 0 deletions
@@ -0,0 +1,55 @@
<?php
/**
* Authentication Endpoints
*/
$db = Database::getInstance()->getConnection();
// Login endpoint
if ($method === 'POST' && $id === 'login') {
$input = getJsonInput();
// Validate input
$errors = validateRequired($input, ['email', 'password']);
if (!empty($errors)) {
jsonResponse(['error' => implode(', ', $errors)], 400);
}
$email = sanitizeString($input['email']);
$password = $input['password'];
// Find admin user
$stmt = $db->prepare("SELECT * FROM admin_users WHERE email = ?");
$stmt->execute([$email]);
$admin = $stmt->fetch();
if (!$admin) {
jsonResponse(['error' => 'Invalid email or password'], 401);
}
// Verify password
if (!password_verify($password, $admin['password_hash'])) {
jsonResponse(['error' => 'Invalid email or password'], 401);
}
// Create token
$token = JWT::createToken($email);
jsonResponse([
'access_token' => $token,
'token_type' => 'bearer',
'email' => $email
]);
}
// Verify token endpoint
if ($method === 'POST' && $id === 'verify') {
$payload = requireAuth();
jsonResponse([
'valid' => true,
'email' => $payload['sub']
]);
}
jsonResponse(['error' => 'Invalid auth endpoint'], 404);
@@ -0,0 +1,37 @@
<?php
/**
* Contact Form Endpoint
*/
$db = Database::getInstance()->getConnection();
if ($method === 'POST') {
$input = getJsonInput();
$errors = validateRequired($input, ['name', 'email', 'message']);
if (!empty($errors)) {
jsonResponse(['error' => implode(', ', $errors)], 400);
}
if (!isValidEmail($input['email'])) {
jsonResponse(['error' => 'Invalid email address'], 400);
}
$id = generateUuid();
$stmt = $db->prepare("
INSERT INTO contacts (id, name, email, message, created_at)
VALUES (?, ?, ?, ?, NOW())
");
$stmt->execute([
$id,
sanitizeString($input['name']),
sanitizeString($input['email']),
$input['message']
]);
jsonResponse(['message' => 'Contact form submitted successfully']);
}
jsonResponse(['error' => 'Method not allowed'], 405);
@@ -0,0 +1,139 @@
<?php
/**
* Destinations CRUD Endpoints
*/
$db = Database::getInstance()->getConnection();
// GET all destinations or single destination
if ($method === 'GET') {
if ($id) {
// Get single destination
$stmt = $db->prepare("SELECT * FROM destinations WHERE id = ?");
$stmt->execute([$id]);
$destination = $stmt->fetch();
if (!$destination) {
jsonResponse(['error' => 'Destination not found'], 404);
}
jsonResponse($destination);
} else {
// Get all destinations with optional filtering
$category = isset($_GET['category']) ? sanitizeString($_GET['category']) : null;
$search = isset($_GET['search']) ? sanitizeString($_GET['search']) : null;
$sql = "SELECT * FROM destinations WHERE 1=1";
$params = [];
if ($category && $category !== 'All') {
$sql .= " AND category = ?";
$params[] = $category;
}
if ($search) {
$sql .= " AND (name LIKE ? OR location LIKE ?)";
$params[] = "%$search%";
$params[] = "%$search%";
}
$sql .= " LIMIT 100";
$stmt = $db->prepare($sql);
$stmt->execute($params);
$destinations = $stmt->fetchAll();
jsonResponse($destinations);
}
}
// POST create new destination (admin only)
if ($method === 'POST') {
requireAuth();
$input = getJsonInput();
$errors = validateRequired($input, ['name', 'location', 'description', 'image', 'category', 'rating', 'price']);
if (!empty($errors)) {
jsonResponse(['error' => implode(', ', $errors)], 400);
}
$id = generateUuid();
$stmt = $db->prepare("
INSERT INTO destinations (id, name, location, description, image, category, rating, price, currency, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
$id,
sanitizeString($input['name']),
sanitizeString($input['location']),
$input['description'],
$input['image'],
$input['category'],
$input['rating'],
$input['price'],
isset($input['currency']) ? $input['currency'] : 'USD'
]);
// Fetch created destination
$stmt = $db->prepare("SELECT * FROM destinations WHERE id = ?");
$stmt->execute([$id]);
$destination = $stmt->fetch();
jsonResponse($destination, 201);
}
// PUT update destination (admin only)
if ($method === 'PUT' && $id) {
requireAuth();
$input = getJsonInput();
// Build update query dynamically
$updates = [];
$params = [];
$allowedFields = ['name', 'location', 'description', 'image', 'category', 'rating', 'price', 'currency'];
foreach ($allowedFields as $field) {
if (isset($input[$field])) {
$updates[] = "$field = ?";
$params[] = $field === 'description' ? $input[$field] : sanitizeString($input[$field]);
}
}
if (empty($updates)) {
jsonResponse(['error' => 'No fields to update'], 400);
}
$params[] = $id;
$sql = "UPDATE destinations SET " . implode(', ', $updates) . " WHERE id = ?";
$stmt = $db->prepare($sql);
$stmt->execute($params);
// Fetch updated destination
$stmt = $db->prepare("SELECT * FROM destinations WHERE id = ?");
$stmt->execute([$id]);
$destination = $stmt->fetch();
jsonResponse($destination);
}
// DELETE destination (admin only)
if ($method === 'DELETE' && $id) {
requireAuth();
// Delete destination (cascades to specials)
$stmt = $db->prepare("DELETE FROM destinations WHERE id = ?");
$stmt->execute([$id]);
if ($stmt->rowCount() === 0) {
jsonResponse(['error' => 'Destination not found'], 404);
}
jsonResponse(['message' => 'Destination deleted successfully']);
}
jsonResponse(['error' => 'Invalid destinations endpoint'], 404);
@@ -0,0 +1,37 @@
<?php
/**
* Newsletter Subscription Endpoint
*/
$db = Database::getInstance()->getConnection();
if ($method === 'POST' && $id === 'subscribe') {
$input = getJsonInput();
if (!isset($input['email']) || !isValidEmail($input['email'])) {
jsonResponse(['error' => 'Valid email address is required'], 400);
}
$email = sanitizeString($input['email']);
// Check if already subscribed
$stmt = $db->prepare("SELECT id FROM newsletter_subscribers WHERE email = ?");
$stmt->execute([$email]);
if ($stmt->fetch()) {
jsonResponse(['message' => 'Email already subscribed']);
}
$id = generateUuid();
$stmt = $db->prepare("
INSERT INTO newsletter_subscribers (id, email, subscribed_at)
VALUES (?, ?, NOW())
");
$stmt->execute([$id, $email]);
jsonResponse(['message' => 'Successfully subscribed to newsletter']);
}
jsonResponse(['error' => 'Invalid newsletter endpoint'], 404);
@@ -0,0 +1,130 @@
<?php
/**
* Weekly Specials CRUD Endpoints
*/
$db = Database::getInstance()->getConnection();
// GET all specials
if ($method === 'GET' && !$id) {
$stmt = $db->query("SELECT * FROM specials LIMIT 100");
$specials = $stmt->fetchAll();
// Parse JSON highlights
foreach ($specials as &$special) {
$special['highlights'] = json_decode($special['highlights'], true);
}
jsonResponse($specials);
}
// POST create special (admin only)
if ($method === 'POST') {
requireAuth();
$input = getJsonInput();
$errors = validateRequired($input, ['destination_id', 'discount', 'end_date', 'highlights']);
if (!empty($errors)) {
jsonResponse(['error' => implode(', ', $errors)], 400);
}
// Check if destination exists
$stmt = $db->prepare("SELECT id FROM destinations WHERE id = ?");
$stmt->execute([$input['destination_id']]);
if (!$stmt->fetch()) {
jsonResponse(['error' => 'Destination not found'], 404);
}
// Check if special already exists for this destination
$stmt = $db->prepare("SELECT id FROM specials WHERE destination_id = ?");
$stmt->execute([$input['destination_id']]);
if ($stmt->fetch()) {
jsonResponse(['error' => 'Special already exists for this destination'], 400);
}
$id = generateUuid();
$highlights = json_encode($input['highlights']);
$stmt = $db->prepare("
INSERT INTO specials (id, destination_id, discount, end_date, highlights, created_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
$id,
$input['destination_id'],
$input['discount'],
$input['end_date'],
$highlights
]);
// Fetch created special
$stmt = $db->prepare("SELECT * FROM specials WHERE id = ?");
$stmt->execute([$id]);
$special = $stmt->fetch();
$special['highlights'] = json_decode($special['highlights'], true);
jsonResponse($special, 201);
}
// PUT update special (admin only)
if ($method === 'PUT' && $id) {
requireAuth();
$input = getJsonInput();
$updates = [];
$params = [];
if (isset($input['discount'])) {
$updates[] = "discount = ?";
$params[] = $input['discount'];
}
if (isset($input['end_date'])) {
$updates[] = "end_date = ?";
$params[] = $input['end_date'];
}
if (isset($input['highlights'])) {
$updates[] = "highlights = ?";
$params[] = json_encode($input['highlights']);
}
if (empty($updates)) {
jsonResponse(['error' => 'No fields to update'], 400);
}
$params[] = $id;
$sql = "UPDATE specials SET " . implode(', ', $updates) . " WHERE id = ?";
$stmt = $db->prepare($sql);
$stmt->execute($params);
// Fetch updated special
$stmt = $db->prepare("SELECT * FROM specials WHERE id = ?");
$stmt->execute([$id]);
$special = $stmt->fetch();
$special['highlights'] = json_decode($special['highlights'], true);
jsonResponse($special);
}
// DELETE special by destination_id (admin only)
if ($method === 'DELETE' && isset($pathParts[1]) && $pathParts[1] === 'destination' && isset($pathParts[2])) {
requireAuth();
$destinationId = $pathParts[2];
$stmt = $db->prepare("DELETE FROM specials WHERE destination_id = ?");
$stmt->execute([$destinationId]);
if ($stmt->rowCount() === 0) {
jsonResponse(['error' => 'Special not found for this destination'], 404);
}
jsonResponse(['message' => 'Special removed successfully']);
}
jsonResponse(['error' => 'Invalid specials endpoint'], 404);
@@ -0,0 +1,72 @@
<?php
/**
* Image Upload Endpoint
*/
requireAuth(); // Only authenticated users can upload
if ($method === 'POST' && $id === 'image') {
if (!isset($_FILES['file'])) {
jsonResponse(['error' => 'No file uploaded'], 400);
}
$file = $_FILES['file'];
// Validate file
if ($file['error'] !== UPLOAD_ERR_OK) {
jsonResponse(['error' => 'File upload failed'], 400);
}
// Check file size
if ($file['size'] > MAX_UPLOAD_SIZE) {
jsonResponse(['error' => 'File too large. Maximum size is 5MB'], 400);
}
// Check file type
$allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp'];
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!in_array($mimeType, $allowedTypes)) {
jsonResponse(['error' => 'Invalid file type. Only JPG, PNG, and WebP allowed'], 400);
}
// Generate unique filename
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$filename = generateUuid() . '.' . $extension;
$filepath = UPLOAD_DIR . $filename;
// Move uploaded file
if (!move_uploaded_file($file['tmp_name'], $filepath)) {
jsonResponse(['error' => 'Failed to save file'], 500);
}
$fileUrl = '/api/uploads/' . $filename;
jsonResponse([
'url' => $fileUrl,
'filename' => $filename
]);
}
// Serve uploaded images
if ($method === 'GET' && isset($pathParts[1]) && $pathParts[1] === 'uploads' && isset($pathParts[2])) {
$filename = basename($pathParts[2]);
$filepath = UPLOAD_DIR . $filename;
if (!file_exists($filepath)) {
jsonResponse(['error' => 'Image not found'], 404);
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filepath);
finfo_close($finfo);
header('Content-Type: ' . $mimeType);
header('Content-Length: ' . filesize($filepath));
readfile($filepath);
exit;
}
jsonResponse(['error' => 'Invalid upload endpoint'], 404);