auto-commit for f3b04df9-f563-4cb2-9a0a-69756e09f838

This commit is contained in:
emergent-agent-e1
2026-05-06 04:03:04 +00:00
parent deaad7b5d3
commit a02bdba1b7
38 changed files with 3127 additions and 0 deletions
+38
View File
@@ -0,0 +1,38 @@
# Epic Travel & Expeditions - API .htaccess
# Place this file in the /api/ directory
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /api/
# Route all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L,QSA]
</IfModule>
# Security Headers
<IfModule mod_headers.c>
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "SAMEORIGIN"
Header set X-XSS-Protection "1; mode=block"
</IfModule>
# Protect sensitive files
<FilesMatch "^(config\.php|\.env)">
Order allow,deny
Deny from all
</FilesMatch>
# Enable compression
<IfModule mod_deflate.c>
AddOutputFilterByType DEFLATE application/json text/plain
</IfModule>
# PHP Settings
<IfModule mod_php7.c>
php_value upload_max_filesize 10M
php_value post_max_size 10M
php_value max_execution_time 300
php_value max_input_time 300
</IfModule>
+55
View File
@@ -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);
+37
View File
@@ -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);
+139
View File
@@ -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);
+37
View File
@@ -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);
+130
View File
@@ -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);
+72
View File
@@ -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);
+45
View File
@@ -0,0 +1,45 @@
<?php
/**
* Epic Travel & Expeditions - Configuration File
* Update these settings with your cPanel MySQL database credentials
*/
// Database Configuration
define('DB_HOST', 'localhost');
define('DB_NAME', 'your_database_name');
define('DB_USER', 'your_database_user');
define('DB_PASS', 'your_database_password');
define('DB_CHARSET', 'utf8mb4');
// Security Configuration
define('JWT_SECRET_KEY', 'CHANGE_THIS_TO_A_RANDOM_SECRET_KEY_32_CHARACTERS_OR_MORE');
define('JWT_EXPIRY', 86400); // 24 hours in seconds
define('ADMIN_PASSWORD_HASH', '$2y$10$PLACEHOLDER'); // Generate using setup_password.php
// CORS Configuration
define('ALLOWED_ORIGINS', 'https://yourdomain.com');
// Application Settings
define('DEBUG_MODE', false);
define('UPLOAD_DIR', __DIR__ . '/uploads/');
define('MAX_UPLOAD_SIZE', 5242880); // 5MB in bytes
// API Settings
define('API_PREFIX', '/api');
// Error Reporting
if (DEBUG_MODE) {
error_reporting(E_ALL);
ini_set('display_errors', 1);
} else {
error_reporting(0);
ini_set('display_errors', 0);
}
// Timezone
date_default_timezone_set('America/Chicago');
// Create uploads directory if it doesn't exist
if (!file_exists(UPLOAD_DIR)) {
mkdir(UPLOAD_DIR, 0755, true);
}
+52
View File
@@ -0,0 +1,52 @@
<?php
/**
* Database Connection Class
* Uses PDO for secure MySQL connections
*/
class Database {
private static $instance = null;
private $conn;
private function __construct() {
try {
$dsn = "mysql:host=" . DB_HOST . ";dbname=" . DB_NAME . ";charset=" . DB_CHARSET;
$options = [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
];
$this->conn = new PDO($dsn, DB_USER, DB_PASS, $options);
} catch (PDOException $e) {
$this->handleError($e->getMessage());
}
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function getConnection() {
return $this->conn;
}
private function handleError($message) {
if (DEBUG_MODE) {
die(json_encode(['error' => 'Database Error: ' . $message]));
} else {
die(json_encode(['error' => 'Database connection failed']));
}
}
// Prevent cloning
private function __clone() {}
// Prevent unserialization
public function __wakeup() {
throw new Exception("Cannot unserialize singleton");
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
/**
* Helper Functions
*/
/**
* Set CORS headers
*/
function setCorsHeaders() {
$origin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : '';
if ($origin && (ALLOWED_ORIGINS === '*' || strpos(ALLOWED_ORIGINS, $origin) !== false)) {
header("Access-Control-Allow-Origin: $origin");
} else {
header("Access-Control-Allow-Origin: " . ALLOWED_ORIGINS);
}
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type, Authorization");
header("Access-Control-Allow-Credentials: true");
// Handle preflight requests
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(200);
exit;
}
}
/**
* Send JSON response
*/
function jsonResponse($data, $statusCode = 200) {
http_response_code($statusCode);
header('Content-Type: application/json');
echo json_encode($data);
exit;
}
/**
* Get JSON input
*/
function getJsonInput() {
$input = file_get_contents('php://input');
return json_decode($input, true);
}
/**
* Validate email
*/
function isValidEmail($email) {
return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}
/**
* Generate UUID v4
*/
function generateUuid() {
return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000,
mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
}
/**
* Sanitize string
*/
function sanitizeString($string) {
return htmlspecialchars(strip_tags(trim($string)), ENT_QUOTES, 'UTF-8');
}
/**
* Validate required fields
*/
function validateRequired($data, $requiredFields) {
$errors = [];
foreach ($requiredFields as $field) {
if (!isset($data[$field]) || empty(trim($data[$field]))) {
$errors[] = "$field is required";
}
}
return $errors;
}
+117
View File
@@ -0,0 +1,117 @@
<?php
/**
* JWT Authentication Functions
* Simple JWT implementation for PHP
*/
class JWT {
/**
* Create a JWT token
*/
public static function encode($payload, $secret) {
$header = json_encode(['typ' => 'JWT', 'alg' => 'HS256']);
$payload = json_encode($payload);
$base64UrlHeader = self::base64UrlEncode($header);
$base64UrlPayload = self::base64UrlEncode($payload);
$signature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true);
$base64UrlSignature = self::base64UrlEncode($signature);
return $base64UrlHeader . "." . $base64UrlPayload . "." . $base64UrlSignature;
}
/**
* Decode and verify a JWT token
*/
public static function decode($jwt, $secret) {
$parts = explode('.', $jwt);
if (count($parts) !== 3) {
return false;
}
list($base64UrlHeader, $base64UrlPayload, $base64UrlSignature) = $parts;
$signature = self::base64UrlDecode($base64UrlSignature);
$expectedSignature = hash_hmac('sha256', $base64UrlHeader . "." . $base64UrlPayload, $secret, true);
if (!hash_equals($signature, $expectedSignature)) {
return false;
}
$payload = json_decode(self::base64UrlDecode($base64UrlPayload), true);
// Check expiration
if (isset($payload['exp']) && $payload['exp'] < time()) {
return false;
}
return $payload;
}
/**
* Create authentication token
*/
public static function createToken($email) {
$payload = [
'sub' => $email,
'iat' => time(),
'exp' => time() + JWT_EXPIRY
];
return self::encode($payload, JWT_SECRET_KEY);
}
/**
* Verify token from Authorization header
*/
public static function verifyToken() {
$headers = getallheaders();
if (!isset($headers['Authorization'])) {
return false;
}
$authHeader = $headers['Authorization'];
if (!preg_match('/Bearer\s+(.*)$/i', $authHeader, $matches)) {
return false;
}
$token = $matches[1];
$payload = self::decode($token, JWT_SECRET_KEY);
return $payload;
}
/**
* Base64 URL encode
*/
private static function base64UrlEncode($data) {
return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data));
}
/**
* Base64 URL decode
*/
private static function base64UrlDecode($data) {
return base64_decode(str_replace(['-', '_'], ['+', '/'], $data));
}
}
/**
* Require authentication middleware
*/
function requireAuth() {
$payload = JWT::verifyToken();
if (!$payload) {
http_response_code(401);
echo json_encode(['error' => 'Unauthorized']);
exit;
}
return $payload;
}
+74
View File
@@ -0,0 +1,74 @@
<?php
/**
* Epic Travel & Expeditions - Main API Entry Point
* This file routes all API requests to appropriate handlers
*/
// Load configuration and dependencies
require_once __DIR__ . '/config.php';
require_once __DIR__ . '/includes/database.php';
require_once __DIR__ . '/includes/jwt.php';
require_once __DIR__ . '/includes/functions.php';
// Set CORS headers
setCorsHeaders();
// Get request method and path
$method = $_SERVER['REQUEST_METHOD'];
$path = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '/';
$path = trim($path, '/');
// Parse path
$pathParts = explode('/', $path);
$resource = isset($pathParts[0]) ? $pathParts[0] : '';
$id = isset($pathParts[1]) ? $pathParts[1] : null;
// Health check endpoint
if ($path === '' || $path === 'api') {
jsonResponse([
'message' => 'Epic Travel API is running',
'status' => 'healthy'
]);
}
// Route to appropriate handler
try {
switch ($resource) {
case 'auth':
require __DIR__ . '/api/auth.php';
break;
case 'destinations':
require __DIR__ . '/api/destinations.php';
break;
case 'specials':
require __DIR__ . '/api/specials.php';
break;
case 'contact':
require __DIR__ . '/api/contact.php';
break;
case 'newsletter':
require __DIR__ . '/api/newsletter.php';
break;
case 'upload':
require __DIR__ . '/api/upload.php';
break;
case 'download':
require __DIR__ . '/api/download.php';
break;
default:
jsonResponse(['error' => 'Endpoint not found'], 404);
}
} catch (Exception $e) {
if (DEBUG_MODE) {
jsonResponse(['error' => $e->getMessage()], 500);
} else {
jsonResponse(['error' => 'Internal server error'], 500);
}
}
+193
View File
@@ -0,0 +1,193 @@
<?php
/**
* Setup Password Hash Generator
* Run this file once to generate a password hash for your admin account
*
* HOW TO USE:
* 1. Upload this file to your server
* 2. Visit it in your browser: https://yourdomain.com/api/setup_password.php
* 3. Enter your desired password
* 4. Copy the generated hash
* 5. Update config.php with the hash
* 6. DELETE this file for security
*/
$generated_hash = '';
$password = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['password'])) {
$password = $_POST['password'];
$generated_hash = password_hash($password, PASSWORD_BCRYPT);
// Update database
if (isset($_POST['update_db']) && $_POST['update_db'] === '1') {
try {
require_once 'config.php';
require_once 'includes/database.php';
$db = Database::getInstance()->getConnection();
$stmt = $db->prepare("UPDATE admin_users SET password_hash = ? WHERE email = 'admin@epictravel.com'");
$stmt->execute([$generated_hash]);
$message = "✅ Password updated in database successfully!";
} catch (Exception $e) {
$error = "❌ Database update failed: " . $e->getMessage();
}
}
}
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Password Setup - Epic Travel</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
max-width: 600px;
margin: 50px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #0891b2;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
input[type="password"],
input[type="text"] {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
button {
background: #0891b2;
color: white;
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
button:hover {
background: #0e7490;
}
.result {
background: #f0f9ff;
border: 1px solid #0891b2;
padding: 15px;
border-radius: 4px;
margin-top: 20px;
word-break: break-all;
}
.warning {
background: #fef3c7;
border: 1px solid #f59e0b;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
}
.success {
background: #d1fae5;
border: 1px solid #059669;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #065f46;
}
.error {
background: #fee2e2;
border: 1px solid #dc2626;
padding: 15px;
border-radius: 4px;
margin-bottom: 20px;
color: #991b1b;
}
.checkbox-group {
margin-top: 10px;
}
code {
background: #f3f4f6;
padding: 2px 6px;
border-radius: 3px;
font-family: monospace;
}
</style>
</head>
<body>
<div class="container">
<h1>🔐 Admin Password Setup</h1>
<div class="warning">
<strong>⚠️ Security Warning:</strong> Delete this file after use!
</div>
<?php if (isset($message)): ?>
<div class="success"><?php echo $message; ?></div>
<?php endif; ?>
<?php if (isset($error)): ?>
<div class="error"><?php echo $error; ?></div>
<?php endif; ?>
<form method="POST">
<div class="form-group">
<label for="password">Enter Admin Password:</label>
<input type="password" id="password" name="password" required
placeholder="Enter a strong password" value="<?php echo htmlspecialchars($password); ?>">
</div>
<div class="checkbox-group">
<label>
<input type="checkbox" name="update_db" value="1">
Update password in database (requires config.php to be configured)
</label>
</div>
<button type="submit">Generate Hash</button>
</form>
<?php if ($generated_hash): ?>
<div class="result">
<strong>Generated Password Hash:</strong><br>
<code><?php echo $generated_hash; ?></code>
<h3>Next Steps:</h3>
<ol>
<li>Copy the hash above</li>
<li>Open <code>config.php</code></li>
<li>Find the line with <code>ADMIN_PASSWORD_HASH</code></li>
<li>Replace the placeholder with your hash</li>
<li><strong>DELETE this file immediately!</strong></li>
</ol>
<h3>Or run this SQL:</h3>
<code style="display:block; padding:10px; margin-top:10px;">
UPDATE admin_users SET password_hash = '<?php echo $generated_hash; ?>' WHERE email = 'admin@epictravel.com';
</code>
</div>
<?php endif; ?>
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; color: #666; font-size: 12px;">
Epic Travel & Expeditions | admin@epictravel.com
</div>
</div>
</body>
</html>